Procházet zdrojové kódy

Merge branch 'master' into treimann/acc-cdr

* master: (76 commits)
  core: new core event for received datagrams
  Added documentation to the DMQ module
  htable: docs updated with db_expires parameter
  htable: control load/save of expires value from db via parameter
  srdb1: updated the definition of siptrace table to include time_us column
  modules_k/siptrace: Add time to x-headers
  modules_k/siptrace: Add column time_us
  modules_k/siptrace: Add "x-headers" feature
  modules_k/siptrace: Add trace_to_database configuration parameter
  modules_k/siptrace: separately store to db and/or send duplicate
  pua_xmpp: fixes to SIP-XMPP presence status translations
  Expose terminate_dlg through C API
  Expose terminate_dlg through C API
  Added terminate_dlg description
  Expose terminate_dlg through C API
  modules_k/dialog: Fix a bug that would cause a segfault when caller bind address information could not be retrieved from database and "dlg_list" was fifo-issued.
  modules_k/dialog: Improve dialog timer list handling.
  modules/tm, modules_k/pua: Fix for concurrency issue in PUA module
  tm: keep internal retr. intervals in ms
  lib/srdb1/schema: forgot to increase version number of pua table
  ...

Conflicts:
	modules_k/dialog/dialog.c
	modules_k/dialog/dlg_load.h
Timo Reimann před 14 roky
rodič
revize
83c9d7187a
100 změnil soubory, kde provedl 3328 přidání a 603 odebrání
  1. 1 1
      Makefile
  2. 15 0
      action.c
  3. 6 0
      cfg.lex
  4. 13 0
      cfg.y
  5. 11 0
      events.c
  6. 2 0
      events.h
  7. 8 2
      lib/srdb1/schema/htable.xml
  8. 2 2
      lib/srdb1/schema/pr_pua.xml
  9. 3 3
      lib/srdb1/schema/rls_presentity.xml
  10. 9 2
      lib/srdb1/schema/sip_trace.xml
  11. 1 1
      modules/async/async_mod.c
  12. 0 2
      modules/db_postgres/km_pg_con.c
  13. 10 13
      modules/db_postgres/pg_cmd.h
  14. 6 28
      modules/db_postgres/pg_fld.c
  15. 25 28
      modules/db_postgres/pg_fld.h
  16. 1 1
      modules/lcr/README
  17. 1 1
      modules/lcr/doc/lcr_admin.xml
  18. 7 3
      modules/tls/tls_rpc.c
  19. 68 44
      modules/tm/README
  20. 4 4
      modules/tm/config.c
  21. 2 2
      modules/tm/config.h
  22. 40 0
      modules/tm/doc/params.xml
  23. 5 2
      modules/tm/h_table.c
  24. 5 6
      modules/tm/h_table.h
  25. 6 4
      modules/tm/t_funcs.h
  26. 12 7
      modules/tm/t_fwd.c
  27. 29 26
      modules/tm/t_lookup.c
  28. 29 8
      modules/tm/t_reply.c
  29. 4 0
      modules/tm/t_reply.h
  30. 32 17
      modules/tm/t_serial.c
  31. 49 36
      modules/tm/timer.c
  32. 32 28
      modules/tm/timer.h
  33. 3 2
      modules/tm/tm.c
  34. 15 5
      modules/tm/uac.c
  35. 2 2
      modules/tm/uac.h
  36. 9 4
      modules_k/auth_radius/README
  37. 12 2
      modules_k/auth_radius/authorize.c
  38. 10 5
      modules_k/auth_radius/authorize.h
  39. 3 1
      modules_k/auth_radius/authrad_mod.c
  40. 12 1
      modules_k/auth_radius/doc/auth_radius_admin.xml
  41. 1 0
      modules_k/dialog/dialog.c
  42. 18 13
      modules_k/dialog/dlg_hash.c
  43. 4 1
      modules_k/dialog/dlg_load.h
  44. 7 8
      modules_k/dialog/dlg_timer.c
  45. 23 0
      modules_k/dialog/doc/dialog_devel.xml
  46. 23 0
      modules_k/dmq/Makefile
  47. 11 0
      modules_k/dmq/bind_dmq.c
  48. 21 0
      modules_k/dmq/bind_dmq.h
  49. 283 0
      modules_k/dmq/dmq.c
  50. 52 0
      modules_k/dmq/dmq.h
  51. 169 0
      modules_k/dmq/dmq_funcs.c
  52. 28 0
      modules_k/dmq/dmq_funcs.h
  53. 248 0
      modules_k/dmq/dmqnode.c
  54. 57 0
      modules_k/dmq/dmqnode.h
  55. 4 0
      modules_k/dmq/doc/Makefile
  56. 44 0
      modules_k/dmq/doc/dmq.xml
  57. 103 0
      modules_k/dmq/doc/dmq_admin.xml
  58. 45 0
      modules_k/dmq/doc/dmq_devel.xml
  59. 50 0
      modules_k/dmq/message.c
  60. 2 0
      modules_k/dmq/message.h
  61. 220 0
      modules_k/dmq/notification_peer.c
  62. 26 0
      modules_k/dmq/notification_peer.h
  63. 44 0
      modules_k/dmq/peer.c
  64. 44 0
      modules_k/dmq/peer.h
  65. 185 0
      modules_k/dmq/worker.c
  66. 43 0
      modules_k/dmq/worker.h
  67. 72 40
      modules_k/htable/README
  68. 41 1
      modules_k/htable/doc/htable_admin.xml
  69. 66 11
      modules_k/htable/ht_db.c
  70. 2 0
      modules_k/htable/ht_db.h
  71. 3 0
      modules_k/htable/htable.c
  72. 3 7
      modules_k/presence/event_list.c
  73. 2 5
      modules_k/presence/event_list.h
  74. 10 14
      modules_k/presence/presence.c
  75. 6 9
      modules_k/presence/presence.h
  76. 2 5
      modules_k/presence/presentity.c
  77. 2 6
      modules_k/presence/presentity.h
  78. 2 5
      modules_k/presence/publish.c
  79. 2 6
      modules_k/presence/publish.h
  80. 5 2
      modules_k/presence_mwi/presence_mwi.c
  81. 0 2
      modules_k/presence_mwi/presence_mwi.h
  82. 4 9
      modules_k/presence_xml/add_events.c
  83. 3 7
      modules_k/presence_xml/add_events.h
  84. 4 6
      modules_k/presence_xml/presence_xml.c
  85. 0 4
      modules_k/presence_xml/presence_xml.h
  86. 58 17
      modules_k/pua/hash.c
  87. 1 0
      modules_k/pua/hash.h
  88. 48 16
      modules_k/pua/pua.c
  89. 73 2
      modules_k/pua/send_subscribe.c
  90. 10 5
      modules_k/pua_xmpp/simple2xmpp.c
  91. 13 29
      modules_k/pua_xmpp/xmpp2simple.c
  92. 3 3
      modules_k/pv/pv_trans.c
  93. 114 7
      modules_k/rls/notify.c
  94. 1 1
      modules_k/rls/rls.c
  95. 85 0
      modules_k/siptrace/doc/siptrace_admin.xml
  96. 218 10
      modules_k/siptrace/siptrace.c
  97. 80 53
      modules_k/textops/README
  98. 56 0
      modules_k/textops/doc/textops_admin.xml
  99. 49 0
      modules_k/textops/textops.c
  100. 31 6
      parser/msg_parser.c

+ 1 - 1
Makefile

@@ -208,7 +208,7 @@ module_group_kstandard=acc alias_db auth auth_db benchmark call_control \
 				avpops cfg_db cfg_rpc ctl db_flatstore dialplan enum \
 				avpops cfg_db cfg_rpc ctl db_flatstore dialplan enum \
 				iptrtpproxy lcr mediaproxy mi_rpc pdb sanity tm topoh \
 				iptrtpproxy lcr mediaproxy mi_rpc pdb sanity tm topoh \
 				blst prefix_route counters debugger matrix mqueue mtree \
 				blst prefix_route counters debugger matrix mqueue mtree \
-				pipelimit rtpproxy textopsx xhttp ipops p_usrloc sdpops
+				pipelimit rtpproxy textopsx xhttp ipops p_usrloc sdpops async
 
 
 # K mysql module
 # K mysql module
 module_group_kmysql=db_mysql
 module_group_kmysql=db_mysql

+ 15 - 0
action.c

@@ -559,6 +559,21 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 				ruri_mark_consumed();
 				ruri_mark_consumed();
 			break;
 			break;
 
 
+		/* remove last branch */
+		case REMOVE_BRANCH_T:
+			if (a->val[0].type!=NUMBER_ST) {
+				ret=drop_sip_branch(0) ? -1 : 1;
+			} else {
+				ret=drop_sip_branch(a->val[0].u.number) ? -1 : 1;
+			}
+			break;
+
+		/* remove all branches */
+		case CLEAR_BRANCHES_T:
+			clear_branches();
+			ret=1;
+			break;
+
 		/* jku begin: is_length_greater_than */
 		/* jku begin: is_length_greater_than */
 		case LEN_GT_T:
 		case LEN_GT_T:
 			if (a->val[0].type!=NUMBER_ST) {
 			if (a->val[0].type!=NUMBER_ST) {

+ 6 - 0
cfg.lex

@@ -237,6 +237,8 @@ STRIP			"strip"
 STRIP_TAIL		"strip_tail"
 STRIP_TAIL		"strip_tail"
 SET_USERPHONE		"userphone"
 SET_USERPHONE		"userphone"
 APPEND_BRANCH	"append_branch"
 APPEND_BRANCH	"append_branch"
+REMOVE_BRANCH	"remove_branch"
+CLEAR_BRANCHES	"clear_branches"
 IF				"if"
 IF				"if"
 ELSE			"else"
 ELSE			"else"
 SET_ADV_ADDRESS	"set_advertised_address"
 SET_ADV_ADDRESS	"set_advertised_address"
@@ -623,6 +625,10 @@ IMPORTFILE      "import_file"
 <INITIAL>{STRIP_TAIL}	{ count(); yylval.strval=yytext; return STRIP_TAIL; }
 <INITIAL>{STRIP_TAIL}	{ count(); yylval.strval=yytext; return STRIP_TAIL; }
 <INITIAL>{APPEND_BRANCH}	{ count(); yylval.strval=yytext;
 <INITIAL>{APPEND_BRANCH}	{ count(); yylval.strval=yytext;
 								return APPEND_BRANCH; }
 								return APPEND_BRANCH; }
+<INITIAL>{REMOVE_BRANCH}	{ count(); yylval.strval=yytext;
+								return REMOVE_BRANCH; }
+<INITIAL>{CLEAR_BRANCHES}	{ count(); yylval.strval=yytext;
+								return CLEAR_BRANCHES; }
 <INITIAL>{SET_USERPHONE}	{ count(); yylval.strval=yytext;
 <INITIAL>{SET_USERPHONE}	{ count(); yylval.strval=yytext;
 								return SET_USERPHONE; }
 								return SET_USERPHONE; }
 <INITIAL>{FORCE_RPORT}	{ count(); yylval.strval=yytext; return FORCE_RPORT; }
 <INITIAL>{FORCE_RPORT}	{ count(); yylval.strval=yytext; return FORCE_RPORT; }

+ 13 - 0
cfg.y

@@ -324,6 +324,8 @@ extern char *finame;
 %token STRIP_TAIL
 %token STRIP_TAIL
 %token SET_USERPHONE
 %token SET_USERPHONE
 %token APPEND_BRANCH
 %token APPEND_BRANCH
+%token REMOVE_BRANCH
+%token CLEAR_BRANCHES
 %token SET_USER
 %token SET_USER
 %token SET_USERPASS
 %token SET_USERPASS
 %token SET_PORT
 %token SET_PORT
@@ -3198,6 +3200,17 @@ cmd:
 							NUMBER_ST, (void *)Q_UNSPECIFIED);
 							NUMBER_ST, (void *)Q_UNSPECIFIED);
 		set_cfg_pos($$);
 		set_cfg_pos($$);
 	}
 	}
+	| REMOVE_BRANCH LPAREN intno RPAREN {
+			$$=mk_action(REMOVE_BRANCH_T, 1, NUMBER_ST, (void*)$3);
+			set_cfg_pos($$);
+	}
+	| REMOVE_BRANCH LPAREN RPAREN {
+			$$=mk_action(REMOVE_BRANCH_T, 0);
+			set_cfg_pos($$);
+	}
+	| REMOVE_BRANCH error { $$=0; yyerror("missing '(' or ')' ?"); }
+	| REMOVE_BRANCH LPAREN error RPAREN { $$=0; yyerror("bad argument, number expected"); }
+	| CLEAR_BRANCHES LPAREN RPAREN { $$=mk_action(CLEAR_BRANCHES_T, 0); set_cfg_pos($$); }
 	| SET_HOSTPORT LPAREN STRING RPAREN { $$=mk_action(SET_HOSTPORT_T, 1, STRING_ST, $3); set_cfg_pos($$); }
 	| SET_HOSTPORT LPAREN STRING RPAREN { $$=mk_action(SET_HOSTPORT_T, 1, STRING_ST, $3); set_cfg_pos($$); }
 	| SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); }
 	| SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); }
 	| SET_HOSTPORT LPAREN error RPAREN { $$=0; yyerror("bad argument, string expected"); }
 	| SET_HOSTPORT LPAREN error RPAREN { $$=0; yyerror("bad argument, string expected"); }

+ 11 - 0
events.c

@@ -81,6 +81,11 @@ int sr_event_register_cb(int type, sr_event_cb_f f)
 					_sr_events_list.pkg_set_real_used = f;
 					_sr_events_list.pkg_set_real_used = f;
 				else return -1;
 				else return -1;
 			break;
 			break;
+		case SREV_NET_DGRAM_IN:
+				if(_sr_events_list.net_dgram_in==0)
+					_sr_events_list.net_dgram_in = f;
+				else return -1;
+			break;
 		default:
 		default:
 			return -1;
 			return -1;
 	}
 	}
@@ -154,6 +159,12 @@ int sr_event_exec(int type, void *data)
 					ret = _sr_events_list.pkg_set_real_used(data);
 					ret = _sr_events_list.pkg_set_real_used(data);
 					return ret;
 					return ret;
 				} else return 1;
 				} else return 1;
+		case SREV_NET_DGRAM_IN:
+				if(unlikely(_sr_events_list.net_dgram_in!=0))
+				{
+					ret = _sr_events_list.net_dgram_in(data);
+					return ret;
+				} else return 1;
 		default:
 		default:
 			return -1;
 			return -1;
 	}
 	}

+ 2 - 0
events.h

@@ -29,6 +29,7 @@
 #define SREV_CFG_RUN_ACTION		4
 #define SREV_CFG_RUN_ACTION		4
 #define SREV_PKG_SET_USED		5
 #define SREV_PKG_SET_USED		5
 #define SREV_PKG_SET_REAL_USED	6
 #define SREV_PKG_SET_REAL_USED	6
+#define SREV_NET_DGRAM_IN		7
 
 
 typedef int (*sr_event_cb_f)(void *data);
 typedef int (*sr_event_cb_f)(void *data);
 
 
@@ -39,6 +40,7 @@ typedef struct sr_event_cb {
 	sr_event_cb_f run_action;
 	sr_event_cb_f run_action;
 	sr_event_cb_f pkg_set_used;
 	sr_event_cb_f pkg_set_used;
 	sr_event_cb_f pkg_set_real_used;
 	sr_event_cb_f pkg_set_real_used;
+	sr_event_cb_f net_dgram_in;
 } sr_event_cb_t;
 } sr_event_cb_t;
 
 
 void sr_event_cb_init(void);
 void sr_event_cb_init(void);

+ 8 - 2
lib/srdb1/schema/htable.xml

@@ -9,7 +9,7 @@
 
 
 <table id="htable" xmlns:db="http://docbook.org/ns/docbook">
 <table id="htable" xmlns:db="http://docbook.org/ns/docbook">
     <name>htable</name>
     <name>htable</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 us used by the htable module to load values in the hash table at start up. More information about the htable module can be found at: &KAMAILIO_MOD_DOC;htable.html
         <db:para>This table us used by the htable module to load values in the hash table at start up. More information about the htable module can be found at: &KAMAILIO_MOD_DOC;htable.html
@@ -45,7 +45,7 @@
         <name>value_type</name>
         <name>value_type</name>
         <type>int</type>
         <type>int</type>
         <default>0</default>
         <default>0</default>
-        <description>Type of the value (0 - string valuel 1 - integer value)</description>
+        <description>Type of the value (0 - string value; 1 - integer value)</description>
     </column>
     </column>
 
 
     <column id="attribute">
     <column id="attribute">
@@ -56,4 +56,10 @@
         <description>The value of the key</description>
         <description>The value of the key</description>
     </column>
     </column>
 
 
+    <column id="expires">
+        <name>expires</name>
+        <type>string</type>
+		<default>0</default>
+        <description>The epoch at which the key expires</description>
+    </column>
 </table>
 </table>

+ 2 - 2
lib/srdb1/schema/pr_pua.xml

@@ -9,7 +9,7 @@
 
 
 <table id="pua" xmlns:db="http://docbook.org/ns/docbook">
 <table id="pua" xmlns:db="http://docbook.org/ns/docbook">
     <name>pua</name>
     <name>pua</name>
-    <version>6</version>
+    <version>7</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
     <description>
         <db:para>Table for the presence related pua module. More information can be found at: &KAMAILIO_MOD_DOC;pua.html
         <db:para>Table for the presence related pua module. More information can be found at: &KAMAILIO_MOD_DOC;pua.html
@@ -95,7 +95,7 @@
     <column>
     <column>
         <name>call_id</name>
         <name>call_id</name>
         <type>string</type>
         <type>string</type>
-        <size>&uri_len;</size>
+        <size>&callid_len;</size>
         <description>Call ID</description>
         <description>Call ID</description>
     </column>
     </column>
 
 

+ 3 - 3
lib/srdb1/schema/rls_presentity.xml

@@ -9,7 +9,7 @@
 
 
 <table id="rls_presentity" xmlns:db="http://docbook.org/ns/docbook">
 <table id="rls_presentity" xmlns:db="http://docbook.org/ns/docbook">
     <name>rls_presentity</name>
     <name>rls_presentity</name>
-    <version>0</version>
+    <version>1</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
     <description>
         <db:para>Table for the RLS module.
         <db:para>Table for the RLS module.
@@ -44,8 +44,8 @@
     <column>
     <column>
         <name>content_type</name>
         <name>content_type</name>
         <type>string</type>
         <type>string</type>
-        <size>&domain_len;</size>
-        <description>Event</description>
+        <size>255</size>
+        <description>Content type</description>
     </column>
     </column>
 
 
     <column>
     <column>

+ 9 - 2
lib/srdb1/schema/sip_trace.xml

@@ -9,7 +9,7 @@
 
 
 <table id="sip_trace" xmlns:db="http://docbook.org/ns/docbook">
 <table id="sip_trace" xmlns:db="http://docbook.org/ns/docbook">
     <name>sip_trace</name>
     <name>sip_trace</name>
-    <version>2</version>
+    <version>3</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
     <description>
         <db:para>This table is used to store incoming/outgoing SIP messages in database. More informations can be found in the siptrace module documentation at: &KAMAILIO_MOD_DOC;siptrace.html.
         <db:para>This table is used to store incoming/outgoing SIP messages in database. More informations can be found in the siptrace module documentation at: &KAMAILIO_MOD_DOC;siptrace.html.
@@ -31,7 +31,14 @@
         <type>datetime</type>
         <type>datetime</type>
         <default>&DEFAULT_DATETIME;</default>
         <default>&DEFAULT_DATETIME;</default>
         <default db="oracle">to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss')</default>
         <default db="oracle">to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss')</default>
-        <description>Recording date</description>
+        <description>Time stamp of processing the SIP message</description>
+    </column>
+
+    <column id="time_us">
+        <name>time_us</name>
+        <type>unsigned int</type>
+        <default>0</default>
+        <description>Store the milliseconds part of the time</description>
     </column>
     </column>
 
 
     <column id="callid">
     <column id="callid">

+ 1 - 1
modules/async/async_mod.c

@@ -145,7 +145,7 @@ static int w_async_sleep(struct sip_msg* msg, char* sec, char* str2)
 	}
 	}
 	if(ap->type==0)
 	if(ap->type==0)
 	{
 	{
-		if(ap->u.paction==NULL || ap->u.paction->next!=NULL)
+		if(ap->u.paction==NULL || ap->u.paction->next==NULL)
 		{
 		{
 			LM_ERR("cannot be executed as last action in a route block\n");
 			LM_ERR("cannot be executed as last action in a route block\n");
 			return -1;
 			return -1;

+ 0 - 2
modules/db_postgres/km_pg_con.c

@@ -1,6 +1,4 @@
 /* 
 /* 
- * $Id$
- *
  * Copyright (C) 2001-2004 iptel.org
  * Copyright (C) 2001-2004 iptel.org
  * Copyright (C) 2008 1&1 Internet AG
  * Copyright (C) 2008 1&1 Internet AG
  *
  *

+ 10 - 13
modules/db_postgres/pg_cmd.h

@@ -1,8 +1,4 @@
 /* 
 /* 
- * $Id$ 
- *
- * PostgreSQL Database Driver for SER
- *
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -31,13 +27,15 @@
 #ifndef _PG_CMD_H
 #ifndef _PG_CMD_H
 #define _PG_CMD_H
 #define _PG_CMD_H
 
 
-/** \addtogroup postgres
- * @{ 
- */
 
 
-/** \file 
- * Declaration of pg_cmd data structure that contains PostgreSQL specific data
+/*!
+ * \file
+ * \brief DB_POSTGRES :: * Declaration of pg_cmd data structure
+ * 
+ * Declaration of pg_cmd data structure  that contains PostgreSQL specific data
  * stored in db_cmd structures and related functions.
  * stored in db_cmd structures and related functions.
+ * \ingroup db_postgres
+ * Module: \ref db_postgres
  */
  */
 
 
 #include "pg_oid.h"
 #include "pg_oid.h"
@@ -90,6 +88,7 @@ int pg_cmd(db_cmd_t* cmd);
  * necessary.
  * necessary.
  * @param res A pointer to (optional) result structure if the command returns
  * @param res A pointer to (optional) result structure if the command returns
  *            a result.
  *            a result.
+ * @param cmd executed command
  * @retval 0 if executed successfully
  * @retval 0 if executed successfully
  * @retval A negative number if the database server failed to execute command
  * @retval A negative number if the database server failed to execute command
  * @retval A positive number if there was an error on client side (SER)
  * @retval A positive number if there was an error on client side (SER)
@@ -106,7 +105,7 @@ int pg_cmd_exec(db_res_t* res, db_cmd_t* cmd);
  * @param res A result set retrieved from PostgreSQL server.
  * @param res A result set retrieved from PostgreSQL server.
  * @retval 0 If executed successfully.
  * @retval 0 If executed successfully.
  * @retval 1 If the result is empty.
  * @retval 1 If the result is empty.
- * @retival A negative number on error.
+ * @retval A negative number on error.
  */
  */
 int pg_cmd_first(db_res_t* res);
 int pg_cmd_first(db_res_t* res);
 
 
@@ -120,7 +119,7 @@ int pg_cmd_first(db_res_t* res);
  * @param res A result set retrieved from PostgreSQL server.
  * @param res A result set retrieved from PostgreSQL server.
  * @retval 0 If executed successfully.
  * @retval 0 If executed successfully.
  * @retval 1 If there are no more records in the result.
  * @retval 1 If there are no more records in the result.
- * @retival A negative number on error.
+ * @retval A negative number on error.
  */
  */
 int pg_cmd_next(db_res_t* res);
 int pg_cmd_next(db_res_t* res);
 
 
@@ -150,6 +149,4 @@ int pg_getopt(db_cmd_t* cmd, char* optname, va_list ap);
  */
  */
 int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap);
 int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap);
 
 
-/** @} */
-
 #endif /* _PG_CMD_H */
 #endif /* _PG_CMD_H */

+ 6 - 28
modules/db_postgres/pg_fld.c

@@ -1,8 +1,4 @@
 /* 
 /* 
- * $Id$ 
- *
- * PostgreSQL Database Driver for SER
- *
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -28,12 +24,12 @@
  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
  */
 
 
-/** \addtogroup postgres
- * @{ 
- */
 
 
-/** \file 
- * Data field conversion and type checking functions.
+/*!
+ * \file
+ * \brief DB_POSTGRES :: Data field conversion and type checking functions.
+ * \ingroup db_postgres
+ * Module: \ref db_postgres
  */
  */
 
 
 #include "pg_fld.h"
 #include "pg_fld.h"
@@ -250,22 +246,6 @@ static inline void db_int2pg_bit(struct pg_params* dst, int i, db_fld_t* src)
    	dst->len[i] = 8;
    	dst->len[i] = 8;
 }
 }
 
 
-/*
-static inline void db_int2pg_varbit(struct pg_params* dst, int i,
-						  db_fld_t* src)
-{
-	unsigned int len = 32;
-	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
-
-	pfld->v.int4[0] = htonl(len);
-	pfld->v.int4[1] = htonl(src->v.int4);
-
-	dst->fmt[i] = 1;
-	dst->val[i] = pfld->v.byte;
-   	dst->len[i] = 4 + len / 8 + (len % 8 ? 1 : 0);
-}
-*/
-
 
 
 static inline void db_str2pg_string(struct pg_params* dst, int i,
 static inline void db_str2pg_string(struct pg_params* dst, int i,
 									db_fld_t* src)
 									db_fld_t* src)
@@ -275,6 +255,7 @@ static inline void db_str2pg_string(struct pg_params* dst, int i,
 	dst->len[i] = src->v.lstr.len;
 	dst->len[i] = src->v.lstr.len;
 }
 }
 
 
+
 static inline void db_cstr2pg_string(struct pg_params* dst, int i,
 static inline void db_cstr2pg_string(struct pg_params* dst, int i,
 									 db_fld_t* src)
 									 db_fld_t* src)
 {
 {
@@ -934,6 +915,3 @@ int pg_pg2fld(db_fld_t* dst, PGresult* src, int row,
 		type, dst[i].type);
 		type, dst[i].type);
 	return -1;
 	return -1;
 }
 }
-
-
-/** @} */

+ 25 - 28
modules/db_postgres/pg_fld.h

@@ -1,8 +1,4 @@
 /* 
 /* 
- * $Id$ 
- *
- * PostgreSQL Database Driver for SER
- *
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -31,13 +27,14 @@
 #ifndef _PG_FLD_H
 #ifndef _PG_FLD_H
 #define _PG_FLD_H
 #define _PG_FLD_H
 
 
-/** \addtogroup postgres
- * @{ 
- */
 
 
-/** \file 
- * Implementation of pg_fld data structure representing PostgreSQL fields and
- * related functions.
+/*!
+ * \file
+ * \brief DB_POSTGRES :: Implementation of pg_fld data structure
+ * 
+ * Implementation of pg_fld data structure representing PostgreSQL fields and related functions.
+ * \ingroup db_postgres
+ * Module: \ref db_postgres
  */
  */
 
 
 #include "pg_oid.h"
 #include "pg_oid.h"
@@ -75,8 +72,7 @@ struct pg_fld {
  * attaches the structure to the generic db_fld structure.
  * attaches the structure to the generic db_fld structure.
  * @param fld A generic db_fld structure to be exended.
  * @param fld A generic db_fld structure to be exended.
  * @param table Name of the table on the server.
  * @param table Name of the table on the server.
- * @retval 0 on success.
- * @retval A negative number on error.
+ * @return 0 on success, negative number on error.
  */
  */
 int pg_fld(db_fld_t* fld, char* table);
 int pg_fld(db_fld_t* fld, char* table);
 
 
@@ -89,15 +85,11 @@ int pg_resolve_result_oids(db_fld_t* fld, int n, PGresult* res);
 /** Converts arrays of db_fld fields to PostgreSQL parameters.
 /** Converts arrays of db_fld fields to PostgreSQL parameters.
  * The function converts fields in SER db_fld format to parameters suitable
  * The function converts fields in SER db_fld format to parameters suitable
  * for PostgreSQL API functions.
  * for PostgreSQL API functions.
- * @param values An array of pointers to values in PostgreSQL format. The
+ * @param dst An array of pointers to values in PostgreSQL format. The
  *               function will store pointers to converted values there.
  *               function will store pointers to converted values there.
- * @param lenghts An array of integers that will be filled with lenghts
- *                of values.
- * @param formats An array of value formats, see PostgreSQL API client
- *                library documentation for more detail.
- * @param oids Types of corresponding columns on the server.
+ * @param off offset 
  * @param types A type conversion table.
  * @param types A type conversion table.
- * @param fld An array of db_fld fields to be converted.
+ * @param src An array of db_fld fields to be converted.
  * @param flags Connection flags controlling how values are converted.
  * @param flags Connection flags controlling how values are converted.
  * @todo Implement support for bit fields with size bigger than 32 
  * @todo Implement support for bit fields with size bigger than 32 
  * @todo Implement support for varbit properly to remove leading zeroes
  * @todo Implement support for varbit properly to remove leading zeroes
@@ -113,10 +105,11 @@ int pg_fld2pg(struct pg_params* dst, int off, pg_type_t* types,
  * The function converts fields from PostgreSQL result (PGresult structure)
  * The function converts fields from PostgreSQL result (PGresult structure)
  * into the internal format used in SER. The function converts one row at a
  * into the internal format used in SER. The function converts one row at a
  * time.
  * time.
- * @param fld The destination array of db_fld fields to be filled with converted
+ * @param dst The destination array of db_fld fields to be filled with converted
  *            values.
  *            values.
- * @param pres A PostgreSQL result structure to be converted into SER format.
+ * @param src A PostgreSQL result structure to be converted into SER format.
  * @param row Number of the row to be converted.
  * @param row Number of the row to be converted.
+ * @param types A type conversion table.
  * @param flags Connection flags controlling how values are converted.
  * @param flags Connection flags controlling how values are converted.
  * @retval 0 on success
  * @retval 0 on success
  * @retval A negative number on error.
  * @retval A negative number on error.
@@ -129,20 +122,24 @@ int pg_pg2fld(db_fld_t* dst, PGresult* src, int row, pg_type_t* types,
 /** Checks if all db_fld fields have types compatible with corresponding field 
 /** Checks if all db_fld fields have types compatible with corresponding field 
  * types on the server.
  * types on the server.
  * The functions checks whether all db_fld fields in the last parameter are
  * The functions checks whether all db_fld fields in the last parameter are
- * compatible with column types on the server.
- * @param oids An array of Oids of corresponding columns on the server.
- * @param lenghts An array of sizes of corresponding columns on the server.
- * @param types An array used to map internal field types to Oids.
+ * compatible with column types on the server, for conversion to postgres format.
  * @param fld An array of db_fld fields to be checked.
  * @param fld An array of db_fld fields to be checked.
+ * @param types An array used to map internal field types to Oids.
  * @retval 0 on success
  * @retval 0 on success
  * @retval A negative number on error.
  * @retval A negative number on error.
  */
  */
 int pg_check_fld2pg(db_fld_t* fld, pg_type_t* types);
 int pg_check_fld2pg(db_fld_t* fld, pg_type_t* types);
 
 
-
+/** Checks if all db_fld fields have types compatible with corresponding field 
+ * types on the server.
+ * The functions checks whether all db_fld fields in the last parameter are
+ * compatible with column types on the server, for conversion to interal DB format.
+ * @param fld An array of db_fld fields to be checked.
+ * @param types An array used to map internal field types to Oids.
+ * @retval 0 on success
+ * @retval A negative number on error.
+ */
 int pg_check_pg2fld(db_fld_t* fld, pg_type_t* types);
 int pg_check_pg2fld(db_fld_t* fld, pg_type_t* types);
 
 
 
 
-/** @} */
-
 #endif /* _PG_FLD_H */
 #endif /* _PG_FLD_H */

+ 1 - 1
modules/lcr/README

@@ -865,7 +865,7 @@ defunct_gw(60);
 
 
    lcr_id can be an integer constant or a pseudo variable holding an
    lcr_id can be an integer constant or a pseudo variable holding an
    integer value. ip_addr can be a string or a pseudo variable holding a
    integer value. ip_addr can be a string or a pseudo variable holding a
-   string value. proto can be an integer constant (0 = 1 = UDP, 2 = TCP, 3
+   string value. proto can be an integer constant (0 = ANY, 1 = UDP, 2 = TCP, 3
    = TLS, 4 = SCTP) or a pseudo variable holding such an integer value.
    = TLS, 4 = SCTP) or a pseudo variable holding such an integer value.
 
 
    If request comes from a gateway, gateway's tag and flags are stored as
    If request comes from a gateway, gateway's tag and flags are stored as

+ 1 - 1
modules/lcr/doc/lcr_admin.xml

@@ -1116,7 +1116,7 @@ defunct_gw(60);
 		lcr_id can be an integer constant or a pseudo variable
 		lcr_id can be an integer constant or a pseudo variable
 		holding an integer value.  ip_addr can be a string or a pseudo
 		holding an integer value.  ip_addr can be a string or a pseudo
 		variable holding a string value.  proto can be an integer
 		variable holding a string value.  proto can be an integer
-		constant (0 = 1 = UDP, 2 = TCP,
+		constant (0 = ANY, 1 = UDP, 2 = TCP,
 		3 = TLS, 4 = SCTP) or a pseudo variable holding such an
 		3 = TLS, 4 = SCTP) or a pseudo variable holding such an
 		integer value.
 		integer value.
 		</para>
 		</para>

+ 7 - 3
modules/tls/tls_rpc.c

@@ -133,11 +133,15 @@ static void tls_list(rpc_t* rpc, void* c)
 					"dst_ip", dst_ip,
 					"dst_ip", dst_ip,
 					"dst_port", con->rcv.dst_port);
 					"dst_port", con->rcv.dst_port);
 			if (tls_d) {
 			if (tls_d) {
-				tls_info = SSL_CIPHER_description(
+				if(SSL_get_current_cipher(tls_d->ssl)) {
+					tls_info = SSL_CIPHER_description(
 									SSL_get_current_cipher(tls_d->ssl),
 									SSL_get_current_cipher(tls_d->ssl),
 									buf, sizeof(buf));
 									buf, sizeof(buf));
-				len = strlen(buf);
-				if (len && buf[len - 1] == '\n') buf[len - 1] = '\0';
+					len = strlen(buf);
+					if (len && buf[len - 1] == '\n') buf[len - 1] = '\0';
+				} else {
+					tls_info = "unknown";
+				}
 				/* tls data */
 				/* tls data */
 				state = "unknown/error";
 				state = "unknown/error";
 				lock_get(&con->write_lock);
 				lock_get(&con->write_lock);

+ 68 - 44
modules/tm/README

@@ -55,8 +55,9 @@ Juha Heinanen
         1.4.35. disable_6xx_block (integer)
         1.4.35. disable_6xx_block (integer)
         1.4.36. local_ack_mode (integer)
         1.4.36. local_ack_mode (integer)
         1.4.37. failure_reply_mode (integer)
         1.4.37. failure_reply_mode (integer)
-        1.4.38. local_cancel_reason (boolean)
-        1.4.39. e2e_cancel_reason (boolean)
+        1.4.38. faked_reply_prio (integer)
+        1.4.39. local_cancel_reason (boolean)
+        1.4.40. e2e_cancel_reason (boolean)
 
 
    1.5. Functions
    1.5. Functions
 
 
@@ -1129,7 +1130,30 @@ modparam("tm", "local_ack_mode", 1)
 modparam("tm", "failure_reply_mode", 3)
 modparam("tm", "failure_reply_mode", 3)
 ...
 ...
 
 
-1.4.38. local_cancel_reason (boolean)
+1.4.38. faked_reply_prio (integer)
+
+   It controls how branch selection is done. It allows to give a penalty
+   to faked replies such as the infamous 408 on branch timeout.
+
+   Internally, every reply is assigned a priority between 0 (high prio)
+   and 32000 (low prio). With this parameter the priority of fake replies
+   can be adjusted.
+     * 0 - disabled (default)
+     * < 0 - priority is increased by given amount.
+     * > 0 - priority is decreased by given amount. Do not make it higer
+       than 10000 or faked replies will even loose from 1xx clsss replies.
+
+   The default value is 0.
+
+   To let received replies win from a locally generated 408, set this
+   value to 2000.
+
+   Example 38. Set faked_reply_prio parameter
+...
+modparam("tm", "faked_reply_prio", 2000)
+...
+
+1.4.39. local_cancel_reason (boolean)
 
 
    Enables/disables adding reason headers (RFC 3326) for CANCELs generated
    Enables/disables adding reason headers (RFC 3326) for CANCELs generated
    due to receiving a final reply. The reason header added will look like:
    due to receiving a final reply. The reason header added will look like:
@@ -1142,12 +1166,12 @@ modparam("tm", "failure_reply_mode", 3)
 
 
    See also: e2e_cancel_reason.
    See also: e2e_cancel_reason.
 
 
-   Example 38. Set local_cancel_reason parameter
+   Example 39. Set local_cancel_reason parameter
 ...
 ...
 modparam("tm", "local_cancel_reason", 0)
 modparam("tm", "local_cancel_reason", 0)
 ...
 ...
 
 
-1.4.39. e2e_cancel_reason (boolean)
+1.4.40. e2e_cancel_reason (boolean)
 
 
    Enables/disables adding reason headers (RFC 3326) for CANCELs generated
    Enables/disables adding reason headers (RFC 3326) for CANCELs generated
    due to a received CANCEL. If enabled the reason headers from received
    due to a received CANCEL. If enabled the reason headers from received
@@ -1160,7 +1184,7 @@ modparam("tm", "local_cancel_reason", 0)
 
 
    See also: t_set_no_e2e_cancel_reason() and local_cancel_reason.
    See also: t_set_no_e2e_cancel_reason() and local_cancel_reason.
 
 
-   Example 39. Set e2e_cancel_reason parameter
+   Example 40. Set e2e_cancel_reason parameter
 ...
 ...
 modparam("tm", "e2e_cancel_reason", 0)
 modparam("tm", "e2e_cancel_reason", 0)
 ...
 ...
@@ -1187,7 +1211,7 @@ t_relay_to_sctp(ip, port) t_relay_to_sctp()
    derived from the message uri (using sip sepcific DNS lookups), but with
    derived from the message uri (using sip sepcific DNS lookups), but with
    the protocol corresponding to the function name.
    the protocol corresponding to the function name.
 
 
-   Example 40. t_relay_to_udp usage
+   Example 41. t_relay_to_udp usage
 ...
 ...
 if (src_ip==10.0.0.0/8)
 if (src_ip==10.0.0.0/8)
         t_relay_to_udp("1.2.3.4", "5060"); # sent to 1.2.3.4:5060 over udp
         t_relay_to_udp("1.2.3.4", "5060"); # sent to 1.2.3.4:5060 over udp
@@ -1214,7 +1238,7 @@ else
    Returns a negative value on failure--you may still want to send a
    Returns a negative value on failure--you may still want to send a
    negative reply upstream statelessly not to leave upstream UAC in lurch.
    negative reply upstream statelessly not to leave upstream UAC in lurch.
 
 
-   Example 41. t_relay usage
+   Example 42. t_relay usage
 ...
 ...
 if (!t_relay())
 if (!t_relay())
 {
 {
@@ -1243,7 +1267,7 @@ if (!t_relay())
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * failure_route - Failure route block to be called.
      * failure_route - Failure route block to be called.
 
 
-   Example 42. t_on_failure usage
+   Example 43. t_on_failure usage
 ...
 ...
 route {
 route {
     t_on_failure("1");
     t_on_failure("1");
@@ -1269,7 +1293,7 @@ failure_route[1] {
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * onreply_route - Onreply route block to be called.
      * onreply_route - Onreply route block to be called.
 
 
-   Example 43. t_on_reply usage
+   Example 44. t_on_reply usage
 ...
 ...
 loadmodule "/usr/local/lib/ser/modules/nathelper.so"
 loadmodule "/usr/local/lib/ser/modules/nathelper.so"
 ...
 ...
@@ -1301,7 +1325,7 @@ es');
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * branch_route - branch route block to be called.
      * branch_route - branch route block to be called.
 
 
-   Example 44. t_on_branch usage
+   Example 45. t_on_branch usage
 ...
 ...
 route {
 route {
         t_on_branch("1");
         t_on_branch("1");
@@ -1319,7 +1343,7 @@ branch_route[1] {
    Similarly to t_fork_to, it extends destination set by a new entry. The
    Similarly to t_fork_to, it extends destination set by a new entry. The
    difference is that current URI is taken as new entry.
    difference is that current URI is taken as new entry.
 
 
-   Example 45. append_branch usage
+   Example 46. append_branch usage
 ...
 ...
 set_user("john");
 set_user("john");
 t_fork();
 t_fork();
@@ -1334,7 +1358,7 @@ t_relay();
    the only way a script can add a new transaction in an atomic way.
    the only way a script can add a new transaction in an atomic way.
    Typically, it is used to deploy a UAS.
    Typically, it is used to deploy a UAS.
 
 
-   Example 46. t_newtran usage
+   Example 47. t_newtran usage
 ...
 ...
 if (t_newtran()) {
 if (t_newtran()) {
     log("UAS logic");
     log("UAS logic");
@@ -1353,7 +1377,7 @@ if (t_newtran()) {
      * code - Reply code number.
      * code - Reply code number.
      * reason_phrase - Reason string.
      * reason_phrase - Reason string.
 
 
-   Example 47. t_reply usage
+   Example 48. t_reply usage
 ...
 ...
 t_reply("404", "Not found");
 t_reply("404", "Not found");
 ...
 ...
@@ -1366,7 +1390,7 @@ t_reply("404", "Not found");
    none was found. However this is safely (atomically) done using
    none was found. However this is safely (atomically) done using
    t_newtran.
    t_newtran.
 
 
-   Example 48. t_lookup_request usage
+   Example 49. t_lookup_request usage
 ...
 ...
 if (t_lookup_request()) {
 if (t_lookup_request()) {
     ...
     ...
@@ -1377,7 +1401,7 @@ if (t_lookup_request()) {
 
 
    Retransmits a reply sent previously by UAS transaction.
    Retransmits a reply sent previously by UAS transaction.
 
 
-   Example 49. t_retransmit_reply usage
+   Example 50. t_retransmit_reply usage
 ...
 ...
 t_retransmit_reply();
 t_retransmit_reply();
 ...
 ...
@@ -1387,7 +1411,7 @@ t_retransmit_reply();
    Remove transaction from memory (it will be first put on a wait timer to
    Remove transaction from memory (it will be first put on a wait timer to
    absorb delayed messages).
    absorb delayed messages).
 
 
-   Example 50. t_release usage
+   Example 51. t_release usage
 ...
 ...
 t_release();
 t_release();
 ...
 ...
@@ -1402,7 +1426,7 @@ t_forward_nonack_tls(ip, port) t_forward_nonack_sctp(ip, port)
      * ip - IP address where the message should be sent.
      * ip - IP address where the message should be sent.
      * port - Port number.
      * port - Port number.
 
 
-   Example 51. t_forward_nonack usage
+   Example 52. t_forward_nonack usage
 ...
 ...
 t_forward_nonack("1.2.3.4", "5060");
 t_forward_nonack("1.2.3.4", "5060");
 ...
 ...
@@ -1425,7 +1449,7 @@ t_forward_nonack("1.2.3.4", "5060");
 
 
    See also: fr_timer, fr_inv_timer, t_reset_fr().
    See also: fr_timer, fr_inv_timer, t_reset_fr().
 
 
-   Example 52. t_set_fr usage
+   Example 53. t_set_fr usage
 ...
 ...
 route {
 route {
         t_set_fr(10000); # set only fr invite timeout to 10s
         t_set_fr(10000); # set only fr invite timeout to 10s
@@ -1452,7 +1476,7 @@ branch_route[1] {
 
 
    See also: fr_timer, fr_inv_timer, t_set_fr.
    See also: fr_timer, fr_inv_timer, t_set_fr.
 
 
-   Example 53. t_reset_fr usage
+   Example 54. t_reset_fr usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -1478,7 +1502,7 @@ route {
 
 
    See also: max_inv_lifetime, max_noninv_lifetime, t_reset_max_lifetime.
    See also: max_inv_lifetime, max_noninv_lifetime, t_reset_max_lifetime.
 
 
-   Example 54. t_set_max_lifetime usage
+   Example 55. t_set_max_lifetime usage
 ...
 ...
 route {
 route {
     if (src_ip=1.2.3.4)
     if (src_ip=1.2.3.4)
@@ -1500,7 +1524,7 @@ route {
 
 
    See also: max_inv_lifetime, max_noninv_lifetime, t_set_max_lifetime.
    See also: max_inv_lifetime, max_noninv_lifetime, t_set_max_lifetime.
 
 
-   Example 55. t_reset_max_lifetime usage
+   Example 56. t_reset_max_lifetime usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -1538,7 +1562,7 @@ route {
 
 
    See also: retr_timer1, retr_timer2, t_reset_retr().
    See also: retr_timer1, retr_timer2, t_reset_retr().
 
 
-   Example 56. t_set_retr usage
+   Example 57. t_set_retr usage
 ...
 ...
 route {
 route {
         t_set_retr(250, 0); # set only T1 to 250 ms
         t_set_retr(250, 0); # set only T1 to 250 ms
@@ -1565,7 +1589,7 @@ branch_route[1] {
 
 
    See also: retr_timer1, retr_timer2, t_set_retr.
    See also: retr_timer1, retr_timer2, t_set_retr.
 
 
-   Example 57. t_reset_retr usage
+   Example 58. t_reset_retr usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -1581,7 +1605,7 @@ route {
 
 
    See also: auto_inv_100.
    See also: auto_inv_100.
 
 
-   Example 58. t_set_auto_inv_100 usage
+   Example 59. t_set_auto_inv_100 usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -1595,7 +1619,7 @@ route {
    Returns true if the failure route is executed for a branch that did
    Returns true if the failure route is executed for a branch that did
    timeout. It can be used only from the failure_route.
    timeout. It can be used only from the failure_route.
 
 
-   Example 59. t_branch_timeout usage
+   Example 60. t_branch_timeout usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_branch_timeout()){
         if (t_branch_timeout()){
@@ -1610,7 +1634,7 @@ failure_route[0]{
    receive at least one reply in the past (the "current" reply is not
    receive at least one reply in the past (the "current" reply is not
    taken into account). It can be used only from the failure_route.
    taken into account). It can be used only from the failure_route.
 
 
-   Example 60. t_branch_replied usage
+   Example 61. t_branch_replied usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_branch_timeout()){
         if (t_branch_timeout()){
@@ -1627,7 +1651,7 @@ failure_route[0]{
    Returns true if at least one of the current transactions branches did
    Returns true if at least one of the current transactions branches did
    timeout.
    timeout.
 
 
-   Example 61. t_any_timeout usage
+   Example 62. t_any_timeout usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (!t_branch_timeout()){
         if (!t_branch_timeout()){
@@ -1644,7 +1668,7 @@ failure_route[0]{
    receive some reply in the past. If called from a failure or onreply
    receive some reply in the past. If called from a failure or onreply
    route, the "current" reply is not taken into account.
    route, the "current" reply is not taken into account.
 
 
-   Example 62. t_any_replied usage
+   Example 63. t_any_replied usage
 ...
 ...
 onreply_route[0]{
 onreply_route[0]{
         if (!t_any_replied()){
         if (!t_any_replied()){
@@ -1658,7 +1682,7 @@ onreply_route[0]{
    Returns true if "code" is the final reply received (or locally
    Returns true if "code" is the final reply received (or locally
    generated) in at least one of the current transactions branches.
    generated) in at least one of the current transactions branches.
 
 
-   Example 63. t_grep_status usage
+   Example 64. t_grep_status usage
 ...
 ...
 onreply_route[0]{
 onreply_route[0]{
         if (t_grep_status("486")){
         if (t_grep_status("486")){
@@ -1671,7 +1695,7 @@ onreply_route[0]{
 
 
    Returns true if the current transaction was canceled.
    Returns true if the current transaction was canceled.
 
 
-   Example 64. t_is_canceled usage
+   Example 65. t_is_canceled usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_is_canceled()){
         if (t_is_canceled()){
@@ -1685,7 +1709,7 @@ failure_route[0]{
    Returns true if the current transaction has already been expired, i.e.
    Returns true if the current transaction has already been expired, i.e.
    the max_inv_lifetime/max_noninv_lifetime interval has already elapsed.
    the max_inv_lifetime/max_noninv_lifetime interval has already elapsed.
 
 
-   Example 65. t_is_expired usage
+   Example 66. t_is_expired usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_is_expired()){
         if (t_is_expired()){
@@ -1706,7 +1730,7 @@ failure_route[0]{
    CANCELs were successfully sent to the pending branches, true if the
    CANCELs were successfully sent to the pending branches, true if the
    INVITE was not found, and false in case of any error.
    INVITE was not found, and false in case of any error.
 
 
-   Example 66. t_relay_cancel usage
+   Example 67. t_relay_cancel usage
 if (method == CANCEL) {
 if (method == CANCEL) {
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
                                   # nothing to do
                                   # nothing to do
@@ -1733,7 +1757,7 @@ if (method == CANCEL) {
    overwritten with the flags of the INVITE. isflagset() can be used to
    overwritten with the flags of the INVITE. isflagset() can be used to
    check the flags of the previously forwarded INVITE in this case.
    check the flags of the previously forwarded INVITE in this case.
 
 
-   Example 67. t_lookup_cancel usage
+   Example 68. t_lookup_cancel usage
 if (method == CANCEL) {
 if (method == CANCEL) {
         if (t_lookup_cancel()) {
         if (t_lookup_cancel()) {
                 log("INVITE transaction exists");
                 log("INVITE transaction exists");
@@ -1763,7 +1787,7 @@ if (method == CANCEL) {
    Dropping replies works only if a new branch is added to the
    Dropping replies works only if a new branch is added to the
    transaction, or it is explicitly replied in the script!
    transaction, or it is explicitly replied in the script!
 
 
-   Example 68. t_drop_replies() usage
+   Example 69. t_drop_replies() usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_check_status("5[0-9][0-9]")){
         if (t_check_status("5[0-9][0-9]")){
@@ -1794,7 +1818,7 @@ failure_route[0]{
    The transaction must be created by t_newtran() before calling
    The transaction must be created by t_newtran() before calling
    t_save_lumps().
    t_save_lumps().
 
 
-   Example 69. t_save_lumps() usage
+   Example 70. t_save_lumps() usage
 route {
 route {
         ...
         ...
         t_newtran();
         t_newtran();
@@ -1864,7 +1888,7 @@ failure_route[1] {
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 70. t_load_contacts usage
+   Example 71. t_load_contacts usage
 ...
 ...
 if (!t_load_contacts()) {
 if (!t_load_contacts()) {
         sl_send_reply("500", "Server Internal Error - Cannot load contacts");
         sl_send_reply("500", "Server Internal Error - Cannot load contacts");
@@ -1905,7 +1929,7 @@ if (!t_load_contacts()) {
    anymore set. Based on that test, you can then use t_set_fr() function
    anymore set. Based on that test, you can then use t_set_fr() function
    to set timers according to your needs.
    to set timers according to your needs.
 
 
-   Example 71. t_next_contacts usage
+   Example 72. t_next_contacts usage
 ...
 ...
 # First call after t_load_contacts() when transaction does not exist yet
 # First call after t_load_contacts() when transaction does not exist yet
 # and contacts should be available
 # and contacts should be available
@@ -1969,7 +1993,7 @@ Note
 
 
    See also: t_lookup_request(), t_lookup_cancel().
    See also: t_lookup_request(), t_lookup_cancel().
 
 
-   Example 72. t_check_trans usage
+   Example 73. t_check_trans usage
 if ( method == "CANCEL" && !t_check_trans())
 if ( method == "CANCEL" && !t_check_trans())
         sl_reply("403", "cancel out of the blue forbidden");
         sl_reply("403", "cancel out of the blue forbidden");
 # note: in this example t_check_trans() can be replaced by t_lookup_cancel()
 # note: in this example t_check_trans() can be replaced by t_lookup_cancel()
@@ -1984,7 +2008,7 @@ if ( method == "CANCEL" && !t_check_trans())
 
 
    See also: disable_6xx_block.
    See also: disable_6xx_block.
 
 
-   Example 73. t_set_disable_6xx usage
+   Example 74. t_set_disable_6xx usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -1999,7 +2023,7 @@ route {
 
 
    See also: use_dns_failover.
    See also: use_dns_failover.
 
 
-   Example 74. t_set_disable_failover usage
+   Example 75. t_set_disable_failover usage
 ...
 ...
 route {
 route {
 ...
 ...
@@ -2030,7 +2054,7 @@ route {
      * hostport - address in "host:port" format. It can be given via an
      * hostport - address in "host:port" format. It can be given via an
        AVP.
        AVP.
 
 
-   Example 75. t_replicate usage
+   Example 76. t_replicate usage
 ...
 ...
 # sent to 1.2.3.4:5060 over tcp
 # sent to 1.2.3.4:5060 over tcp
 t_replicate("sip:1.2.3.4:5060;transport=tcp");
 t_replicate("sip:1.2.3.4:5060;transport=tcp");
@@ -2065,7 +2089,7 @@ t_replicate_to_udp("1.2.3.4", "5060");
             effect anymore).
             effect anymore).
           + 0x04 - disable dns failover.
           + 0x04 - disable dns failover.
 
 
-   Example 76. t_replicate usage
+   Example 77. t_replicate usage
 ...
 ...
 # sent to 1.2.3.4:5060 over tcp
 # sent to 1.2.3.4:5060 over tcp
 t_relay_to("tcp:1.2.3.4:5060");
 t_relay_to("tcp:1.2.3.4:5060");
@@ -2088,7 +2112,7 @@ t_relay_to("0x01");
 
 
    See also: e2e_cancel_reason.
    See also: e2e_cancel_reason.
 
 
-   Example 77. t_set_no_e2e_cancel_reason usage
+   Example 78. t_set_no_e2e_cancel_reason usage
 ...
 ...
 route {
 route {
 ...
 ...

+ 4 - 4
modules/tm/config.c

@@ -52,8 +52,8 @@ struct cfg_group_tm	default_tm_cfg = {
 	INV_FR_TIME_OUT_NEXT, /* fr_inv_timeout_next */
 	INV_FR_TIME_OUT_NEXT, /* fr_inv_timeout_next */
 	WT_TIME_OUT,	/* wait_timeout */
 	WT_TIME_OUT,	/* wait_timeout */
 	DEL_TIME_OUT,	/* delete_timeout */
 	DEL_TIME_OUT,	/* delete_timeout */
-	RETR_T1,	/* rt_t1_timeout */
-	RETR_T2,	/* rt_t2_timeout */
+	RETR_T1,	/* rt_t1_timeout_ms */
+	RETR_T2,	/* rt_t2_timeout_ms */
 
 
 	/* maximum time an invite or noninv transaction will live, from
 	/* maximum time an invite or noninv transaction will live, from
 	 * the moment of creation (overrides larger fr/fr_inv timeouts,
 	 * the moment of creation (overrides larger fr/fr_inv timeouts,
@@ -122,9 +122,9 @@ cfg_def_t	tm_cfg_def[] = {
 	{"delete_timer",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
 	{"delete_timer",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
 		"time after which a to-be-deleted transaction currently "
 		"time after which a to-be-deleted transaction currently "
 		"ref-ed by a process will be tried to be deleted again."},
 		"ref-ed by a process will be tried to be deleted again."},
-	{"retr_timer1",		CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
+	{"retr_timer1",		CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup_ms, 0,
 		"initial retransmission period (in milliseconds)"},
 		"initial retransmission period (in milliseconds)"},
-	{"retr_timer2",		CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
+	{"retr_timer2",		CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup_ms, 0,
 		"maximum retransmission period (in milliseconds)"},
 		"maximum retransmission period (in milliseconds)"},
 	{"max_inv_lifetime",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
 	{"max_inv_lifetime",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
 		"maximum time an invite transaction can live "
 		"maximum time an invite transaction can live "

+ 2 - 2
modules/tm/config.h

@@ -109,8 +109,8 @@ struct cfg_group_tm {
 	unsigned int    fr_inv_timeout_next;
 	unsigned int    fr_inv_timeout_next;
 	unsigned int	wait_timeout;
 	unsigned int	wait_timeout;
 	unsigned int	delete_timeout;
 	unsigned int	delete_timeout;
-	unsigned int	rt_t1_timeout;
-	unsigned int	rt_t2_timeout;
+	unsigned int	rt_t1_timeout_ms;
+	unsigned int	rt_t2_timeout_ms;
 	unsigned int	tm_max_inv_lifetime;
 	unsigned int	tm_max_inv_lifetime;
 	unsigned int	tm_max_noninv_lifetime;
 	unsigned int	tm_max_noninv_lifetime;
 	int	noisy_ctimer;
 	int	noisy_ctimer;

+ 40 - 0
modules/tm/doc/params.xml

@@ -1168,6 +1168,46 @@ modparam("tm", "failure_reply_mode", 3)
 	</example>
 	</example>
 	</section>
 	</section>
 
 
+	<section id="faked_reply_prio">
+	<title><varname>faked_reply_prio</varname> (integer)</title>
+	<para>
+		It controls how branch selection is done. It allows to give a penalty
+		to faked replies such as the infamous 408 on branch timeout.
+	</para>
+	<para>
+		Internally, every reply is assigned a priority between 0 (high prio) 
+		and 32000 (low prio). With this parameter the priority of fake replies
+		can be adjusted.
+	</para>
+	<itemizedlist>
+		<listitem><para>
+		<emphasis>0</emphasis> - disabled (default)
+		</para></listitem>
+		<listitem><para>
+		<emphasis>&lt; 0</emphasis> - priority is increased by given amount.
+		</para></listitem>
+		<listitem><para>
+		<emphasis>&gt; 0</emphasis> - priority is decreased by given amount.
+		Do not make it higer than 10000 or faked replies will even loose
+		from 1xx clsss replies.
+		</para></listitem>
+	</itemizedlist>
+	<para>
+		The default value is 0.
+	</para>
+	<para>
+		To let received replies win from a locally generated 408, set this
+		value to 2000.
+	</para>
+	<example>
+		<title>Set <varname>faked_reply_prio</varname> parameter</title>
+		<programlisting>
+...
+modparam("tm", "faked_reply_prio", 2000)
+...
+	    </programlisting>
+	</example>
+	</section>
 
 
 	<section id="local_cancel_reason">
 	<section id="local_cancel_reason">
 		<title><varname>local_cancel_reason</varname> (boolean)</title>
 		<title><varname>local_cancel_reason</varname> (boolean)</title>

+ 5 - 2
modules/tm/h_table.c

@@ -492,12 +492,15 @@ error0:
  * - mode = 0 - from msg context to _txdata and use T lists
  * - mode = 0 - from msg context to _txdata and use T lists
  * - mode = 1 - restore to msg context from _txdata
  * - mode = 1 - restore to msg context from _txdata
  */
  */
-void tm_xdata_swap(tm_cell_t *t, int mode)
+void tm_xdata_swap(tm_cell_t *t, tm_xdata_t *xd, int mode)
 {
 {
 	static tm_xdata_t _txdata;
 	static tm_xdata_t _txdata;
 	tm_xdata_t *x;
 	tm_xdata_t *x;
 
 
-	x = &_txdata;
+	if(xd==NULL)
+		x = &_txdata;
+	else
+		x = xd;
 
 
 	if(mode==0) {
 	if(mode==0) {
 		if(t==NULL)
 		if(t==NULL)

+ 5 - 6
modules/tm/h_table.h

@@ -295,9 +295,8 @@ struct totag_elem {
 #	define pass_provisional(_t_)	((_t_)->flags&T_PASS_PROVISIONAL_FLAG)
 #	define pass_provisional(_t_)	((_t_)->flags&T_PASS_PROVISIONAL_FLAG)
 #endif
 #endif
 
 
-/* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
- * max  retr. = 1023 s for tick = 15 ms, which should be more then enough and
- * saves us 2*2 bytes */
+/* unsigned short should be enough for a retr. timer: max. 65535 ms =>
+ * max retr. = 65 s which should be enough and saves us 2*2 bytes */
 typedef unsigned short retr_timeout_t;
 typedef unsigned short retr_timeout_t;
 
 
 
 
@@ -406,8 +405,8 @@ typedef struct cell
 	ticks_t fr_timeout;     /* final response interval for retr_bufs */
 	ticks_t fr_timeout;     /* final response interval for retr_bufs */
 	ticks_t fr_inv_timeout; /* final inv. response interval for retr_bufs */
 	ticks_t fr_inv_timeout; /* final inv. response interval for retr_bufs */
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-	retr_timeout_t rt_t1_timeout; /* start retr. interval for retr_bufs */
-	retr_timeout_t rt_t2_timeout; /* maximum retr. interval for retr_bufs */
+	retr_timeout_t rt_t1_timeout_ms; /* start retr. interval for retr_bufs */
+	retr_timeout_t rt_t2_timeout_ms; /* maximum retr. interval for retr_bufs */
 #endif
 #endif
 	ticks_t end_of_life; /* maximum lifetime */
 	ticks_t end_of_life; /* maximum lifetime */
 
 
@@ -565,7 +564,7 @@ inline static void remove_from_hash_table_unsafe( struct cell * p_cell)
 /**
 /**
  * backup xdata from/to msg context to local var and use T lists
  * backup xdata from/to msg context to local var and use T lists
  */
  */
-void tm_xdata_swap(tm_cell_t *t, int mode);
+void tm_xdata_swap(tm_cell_t *t, tm_xdata_t *xd, int mode);
 
 
 #endif
 #endif
 
 

+ 6 - 4
modules/tm/t_funcs.h

@@ -169,19 +169,21 @@ int fr_inv_avp2timer(unsigned int* timer);
 #ifdef TIMER_DEBUG
 #ifdef TIMER_DEBUG
 #define start_retr(rb) \
 #define start_retr(rb) \
 	_set_fr_retr((rb), \
 	_set_fr_retr((rb), \
-				((rb)->dst.proto==PROTO_UDP)?RT_T1_TIMEOUT(rb):(ticks_t)(-1), \
+				((rb)->dst.proto==PROTO_UDP) ? RT_T1_TIMEOUT_MS(rb) : \
+												(unsigned)(-1), \
 				__FILE__, __FUNCTION__, __LINE__)
 				__FILE__, __FUNCTION__, __LINE__)
 
 
 #define force_retr(rb) \
 #define force_retr(rb) \
-	_set_fr_retr((rb), RT_T1_TIMEOUT(rb), __FILE__, __FUNCTION__, __LINE__)
+	_set_fr_retr((rb), RT_T1_TIMEOUT_MS(rb), __FILE__, __FUNCTION__, __LINE__)
 
 
 #else
 #else
 #define start_retr(rb) \
 #define start_retr(rb) \
 	_set_fr_retr((rb), \
 	_set_fr_retr((rb), \
-				((rb)->dst.proto==PROTO_UDP)?RT_T1_TIMEOUT(rb):(ticks_t)(-1))
+				((rb)->dst.proto==PROTO_UDP) ? RT_T1_TIMEOUT_MS(rb) : \
+												(unsigned)(-1))
 
 
 #define force_retr(rb) \
 #define force_retr(rb) \
-	_set_fr_retr((rb), RT_T1_TIMEOUT(rb))
+	_set_fr_retr((rb), RT_T1_TIMEOUT_MS(rb))
 
 
 #endif
 #endif
 
 

+ 12 - 7
modules/tm/t_fwd.c

@@ -151,6 +151,8 @@ unsigned int get_on_branch(void)
 
 
 /* prepare_new_uac flags */
 /* prepare_new_uac flags */
 #define UAC_DNS_FAILOVER_F 1 /**< new branch due to dns failover */
 #define UAC_DNS_FAILOVER_F 1 /**< new branch due to dns failover */
+#define UAC_SKIP_BR_DST_F  2 /**< don't set next hop as dst_uri for
+							   branch_route */
 
 
 
 
 /** prepares a new branch "buffer".
 /** prepares a new branch "buffer".
@@ -176,7 +178,7 @@ unsigned int get_on_branch(void)
  * @param fsocket - forced send socket for forwarding.
  * @param fsocket - forced send socket for forwarding.
  * @param send_flags - special flags for sending (see SND_F_* / snd_flags_t).
  * @param send_flags - special flags for sending (see SND_F_* / snd_flags_t).
  * @param fproto - forced proto for forwarding. Used only if next_hop!=0.
  * @param fproto - forced proto for forwarding. Used only if next_hop!=0.
- * @param flags - 0 or UAC_DNS_FAILOVER_F for now.
+ * @param flags - 0, UAC_DNS_FAILOVER_F or UAC_SKIP_BR_DST_F for now.
  *
  *
  * @return  0 on success, < 0 (ser_errror E***) on failure.
  * @return  0 on success, < 0 (ser_errror E***) on failure.
  */
  */
@@ -302,10 +304,12 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 		i_req->dst_uri.s=0;
 		i_req->dst_uri.s=0;
 		i_req->dst_uri.len=0;
 		i_req->dst_uri.len=0;
 		if (likely(next_hop)){
 		if (likely(next_hop)){
-			/* set dst uri to next_hop for the on_branch route */
-			if (unlikely(set_dst_uri(i_req, next_hop)<0)){
-				ret=E_OUT_OF_MEM;
-				goto error03;
+			if(unlikely((flags & UAC_SKIP_BR_DST_F)==0)){
+				/* set dst uri to next_hop for the on_branch route */
+				if (unlikely(set_dst_uri(i_req, next_hop)<0)){
+					ret=E_OUT_OF_MEM;
+					goto error03;
+				}
 			}
 			}
 		}
 		}
 
 
@@ -1475,7 +1479,8 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 		try_new=1;
 		try_new=1;
 		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
 		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
 							&p_msg->path_vec, proxy, p_msg->force_send_socket,
 							&p_msg->path_vec, proxy, p_msg->force_send_socket,
-							p_msg->fwd_send_flags, proto, 0);
+							p_msg->fwd_send_flags, proto,
+							(p_msg->dst_uri.len)?0:UAC_SKIP_BR_DST_F);
 		if (branch_ret>=0) 
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 			added_branches |= 1<<branch_ret;
 		else
 		else
@@ -1491,7 +1496,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 		branch_ret=add_uac( t, p_msg, &current_uri,
 		branch_ret=add_uac( t, p_msg, &current_uri,
 							(dst_uri.len) ? (&dst_uri) : &current_uri,
 							(dst_uri.len) ? (&dst_uri) : &current_uri,
 							&path, proxy, si, p_msg->fwd_send_flags,
 							&path, proxy, si, p_msg->fwd_send_flags,
-							proto, 0);
+							proto, (dst_uri.len)?0:UAC_SKIP_BR_DST_F);
 		/* pick some of the errors in case things go wrong;
 		/* pick some of the errors in case things go wrong;
 		   note that picking lowest error is just as good as
 		   note that picking lowest error is just as good as
 		   any other algorithm which picks any other negative
 		   any other algorithm which picks any other negative

+ 29 - 26
modules/tm/t_lookup.c

@@ -1294,14 +1294,16 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 		}
 		}
 	}
 	}
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-	new_cell->rt_t1_timeout=(ticks_t)get_msgid_val(user_rt_t1_timeout,
-												p_msg->id, int);
-	if (likely(new_cell->rt_t1_timeout==0))
-		new_cell->rt_t1_timeout=cfg_get(tm, tm_cfg, rt_t1_timeout);
-	new_cell->rt_t2_timeout=(ticks_t)get_msgid_val(user_rt_t2_timeout,
-												p_msg->id, int);
-	if (likely(new_cell->rt_t2_timeout==0))
-		new_cell->rt_t2_timeout=cfg_get(tm, tm_cfg, rt_t2_timeout);
+	new_cell->rt_t1_timeout_ms = (retr_timeout_t) get_msgid_val(
+														user_rt_t1_timeout_ms,
+														p_msg->id, int);
+	if (likely(new_cell->rt_t1_timeout_ms == 0))
+		new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms);
+	new_cell->rt_t2_timeout_ms = (retr_timeout_t) get_msgid_val(
+														user_rt_t2_timeout_ms,
+														p_msg->id, int);
+	if (likely(new_cell->rt_t2_timeout_ms == 0))
+		new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms);
 #endif
 #endif
 	new_cell->on_branch=get_on_branch();
 	new_cell->on_branch=get_on_branch();
 }
 }
@@ -1813,30 +1815,30 @@ int t_reset_fr()
 
 
 /* params: retr. t1 & retr. t2 value in ms, 0 means "do not touch"
 /* params: retr. t1 & retr. t2 value in ms, 0 means "do not touch"
  * ret: 1 on success, -1 on error (script safe)*/
  * ret: 1 on success, -1 on error (script safe)*/
-int t_set_retr(struct sip_msg* msg, unsigned int t1_to, unsigned int t2_to)
+int t_set_retr(struct sip_msg* msg, unsigned int t1_ms, unsigned int t2_ms)
 {
 {
 	struct cell *t;
 	struct cell *t;
 	ticks_t retr_t1, retr_t2;
 	ticks_t retr_t1, retr_t2;
 	
 	
 	
 	
-	retr_t1=MS_TO_TICKS((ticks_t)t1_to);
-	if (unlikely((retr_t1==0) && (t1_to!=0))){
-		ERR("t_set_retr: retr. t1 interval too small (%u)\n", t1_to);
+	retr_t1=MS_TO_TICKS((ticks_t)t1_ms);
+	if (unlikely((retr_t1==0) && (t1_ms!=0))){
+		ERR("t_set_retr: retr. t1 interval too small (%u)\n", t1_ms);
 		return -1;
 		return -1;
 	}
 	}
-	if (unlikely(MAX_UVAR_VALUE(t->rt_t1_timeout) < retr_t1)){
+	if (unlikely(MAX_UVAR_VALUE(t->rt_t1_timeout_ms) < t1_ms)){
 		ERR("t_set_retr: retr. t1 interval too big: %d (max %lu)\n",
 		ERR("t_set_retr: retr. t1 interval too big: %d (max %lu)\n",
-				t1_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t1_timeout))); 
+				t1_ms, MAX_UVAR_VALUE(t->rt_t1_timeout_ms)); 
 		return -1;
 		return -1;
 	} 
 	} 
-	retr_t2=MS_TO_TICKS((ticks_t)t2_to);
-	if (unlikely((retr_t2==0) && (t2_to!=0))){
-		ERR("t_set_retr: retr. t2 interval too small (%d)\n", t2_to);
+	retr_t2=MS_TO_TICKS((ticks_t)t2_ms);
+	if (unlikely((retr_t2==0) && (t2_ms!=0))){
+		ERR("t_set_retr: retr. t2 interval too small (%d)\n", t2_ms);
 		return -1;
 		return -1;
 	}
 	}
-	if (unlikely(MAX_UVAR_VALUE(t->rt_t2_timeout) < retr_t2)){
+	if (unlikely(MAX_UVAR_VALUE(t->rt_t2_timeout_ms) < t2_ms)){
 		ERR("t_set_retr: retr. t2 interval too big: %u (max %lu)\n",
 		ERR("t_set_retr: retr. t2 interval too big: %u (max %lu)\n",
-				t2_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t2_timeout))); 
+				t2_ms, MAX_UVAR_VALUE(t->rt_t2_timeout_ms)); 
 		return -1;
 		return -1;
 	} 
 	} 
 	
 	
@@ -1845,10 +1847,10 @@ int t_set_retr(struct sip_msg* msg, unsigned int t1_to, unsigned int t2_to)
 	 * in REQUEST_ROUTE T will be set only if the transaction was already
 	 * in REQUEST_ROUTE T will be set only if the transaction was already
 	 * created; if not -> use the static variables */
 	 * created; if not -> use the static variables */
 	if (!t || t==T_UNDEFINED ){
 	if (!t || t==T_UNDEFINED ){
-		set_msgid_val(user_rt_t1_timeout, msg->id, int, (int)retr_t1);
-		set_msgid_val(user_rt_t2_timeout, msg->id, int, (int)retr_t2);
+		set_msgid_val(user_rt_t1_timeout_ms, msg->id, int, (int)t1_ms);
+		set_msgid_val(user_rt_t2_timeout_ms, msg->id, int, (int)t2_ms);
 	}else{
 	}else{
-		change_retr(t, 1, retr_t1, retr_t2); /* change running uac timers */
+		change_retr(t, 1, t1_ms, t2_ms); /* change running uac timers */
 	}
 	}
 	return 1;
 	return 1;
 }
 }
@@ -1863,13 +1865,14 @@ int t_reset_retr()
 	 * in REQUEST_ROUTE T will be set only if the transaction was already
 	 * in REQUEST_ROUTE T will be set only if the transaction was already
 	 * created; if not -> use the static variables */
 	 * created; if not -> use the static variables */
 	if (!t || t==T_UNDEFINED ){
 	if (!t || t==T_UNDEFINED ){
-		memset(&user_rt_t1_timeout, 0, sizeof(user_rt_t1_timeout));
-		memset(&user_rt_t2_timeout, 0, sizeof(user_rt_t2_timeout));
+		memset(&user_rt_t1_timeout_ms, 0, sizeof(user_rt_t1_timeout_ms));
+		memset(&user_rt_t2_timeout_ms, 0, sizeof(user_rt_t2_timeout_ms));
 	}else{
 	}else{
+		 /* change running uac timers */
 		change_retr(t,
 		change_retr(t,
 			1,
 			1,
-			cfg_get(tm, tm_cfg, rt_t1_timeout),
-			cfg_get(tm, tm_cfg, rt_t2_timeout)); /* change running uac timers */
+			cfg_get(tm, tm_cfg, rt_t1_timeout_ms),
+			cfg_get(tm, tm_cfg, rt_t2_timeout_ms));
 	}
 	}
 	return 1;
 	return 1;
 }
 }

+ 29 - 8
modules/tm/t_reply.c

@@ -201,6 +201,14 @@ static unsigned short resp_class_prio[]={
 			1000   /* 6xx, highest priority */
 			1000   /* 6xx, highest priority */
 };
 };
 
 
+/* How to prioritize faked replies 
+ * The value will be added to the default prio
+ * - 0 disabled
+ * - < 0 increase prio
+ * - > 0 decrease prio
+ */
+int faked_reply_prio = 0;
+
 
 
 int t_get_reply_totag(struct sip_msg *msg, str *totag)
 int t_get_reply_totag(struct sip_msg *msg, str *totag)
 {
 {
@@ -1007,18 +1015,26 @@ inline static short int get_4xx_prio(unsigned char xx)
  *  6xx                          1000+xx              (high)
  *  6xx                          1000+xx              (high)
  *  2xx                          0000+xx              (highest) 
  *  2xx                          0000+xx              (highest) 
  */
  */
-inline static short int get_prio(unsigned int resp)
+inline static short int get_prio(unsigned int resp, struct sip_msg *rpl)
 {
 {
 	int class;
 	int class;
 	int xx;
 	int xx;
+	int prio;
 	
 	
 	class=resp/100;
 	class=resp/100;
 
 
 	if (class<7){
 	if (class<7){
 		xx=resp%100;
 		xx=resp%100;
-		return resp_class_prio[class]+((class==4)?get_4xx_prio(xx):xx);
+		prio = resp_class_prio[class]+((class==4)?get_4xx_prio(xx):xx);
+	} else {
+		prio = 10000+resp; /* unknown response class => return very low prio */
+	}
+	if (rpl == FAKED_REPLY) {
+		/* Add faked_reply penalty */
+		return prio + faked_reply_prio;
+	} else {
+		return prio;
 	}
 	}
-	return 10000+resp; /* unknown response class => return very low prio */
 }
 }
 
 
 
 
@@ -1031,12 +1047,15 @@ inline static short int get_prio(unsigned int resp)
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 {
 {
 	int best_b, best_s, b;
 	int best_b, best_s, b;
+	sip_msg_t *rpl;
 
 
 	best_b=-1; best_s=0;
 	best_b=-1; best_s=0;
 	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
 	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
+		rpl = t->uac[b].reply;
+
 		/* "fake" for the currently processed branch */
 		/* "fake" for the currently processed branch */
 		if (b==inc_branch) {
 		if (b==inc_branch) {
-			if (get_prio(inc_code)<get_prio(best_s)) {
+			if (get_prio(inc_code, rpl)<get_prio(best_s, rpl)) {
 				best_b=b;
 				best_b=b;
 				best_s=inc_code;
 				best_s=inc_code;
 			}
 			}
@@ -1051,8 +1070,8 @@ int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 		if ( t->uac[b].last_received<200 )
 		if ( t->uac[b].last_received<200 )
 			return -2;
 			return -2;
 		/* if reply is null => t_send_branch "faked" reply, skip over it */
 		/* if reply is null => t_send_branch "faked" reply, skip over it */
-		if ( t->uac[b].reply && 
-				get_prio(t->uac[b].last_received)<get_prio(best_s) ) {
+		if ( rpl && 
+				get_prio(t->uac[b].last_received, rpl)<get_prio(best_s, rpl) ) {
 			best_b =b;
 			best_b =b;
 			best_s = t->uac[b].last_received;
 			best_s = t->uac[b].last_received;
 		}
 		}
@@ -1075,6 +1094,7 @@ int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 int t_pick_branch_blind(struct cell *t, int *res_code)
 int t_pick_branch_blind(struct cell *t, int *res_code)
 {
 {
 	int best_b, best_s, b;
 	int best_b, best_s, b;
+	sip_msg_t *rpl;
 
 
 	best_b=-1; best_s=0;
 	best_b=-1; best_s=0;
 	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
 	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
@@ -1082,8 +1102,9 @@ int t_pick_branch_blind(struct cell *t, int *res_code)
 		if ( t->uac[b].last_received<200 )
 		if ( t->uac[b].last_received<200 )
 			return -2;
 			return -2;
 		/* if reply is null => t_send_branch "faked" reply, skip over it */
 		/* if reply is null => t_send_branch "faked" reply, skip over it */
-		if ( t->uac[b].reply && 
-				get_prio(t->uac[b].last_received)<get_prio(best_s) ) {
+		rpl = t->uac[b].reply;
+		if ( rpl && 
+				get_prio(t->uac[b].last_received, rpl)<get_prio(best_s, rpl) ) {
 			best_b = b;
 			best_b = b;
 			best_s = t->uac[b].last_received;
 			best_s = t->uac[b].last_received;
 		}
 		}

+ 4 - 0
modules/tm/t_reply.h

@@ -65,6 +65,10 @@ extern int goto_on_sl_reply;
 
 
 extern int failure_reply_mode;
 extern int failure_reply_mode;
 
 
+extern int faked_reply_prio;
+
+extern int tm_rich_redirect;
+ 
 /* has this to-tag been never seen in previous 200/INVs? */
 /* has this to-tag been never seen in previous 200/INVs? */
 int unmatched_totag(struct cell *t, struct sip_msg *ack);
 int unmatched_totag(struct cell *t, struct sip_msg *ack);
 
 

+ 32 - 17
modules/tm/t_serial.c

@@ -242,15 +242,21 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
     }
     }
 
 
     ruri = (str *)0;
     ruri = (str *)0;
-
-    /* Take first q from Request-URI */
-    ruri = GET_RURI(msg);
-    if (!ruri) {
-	LM_ERR("no Request-URI found\n");
-	return -1;
+	if (ruri_is_new) {
+		/* Take first q from Request-URI */
+		ruri = GET_RURI(msg);
+		if (!ruri) {
+			LM_ERR("no Request-URI found\n");
+			return -1;
+		}
+		first_q = get_ruri_q();
+		first_idx = 0;
+	} else {
+		/* Take first q from first branch */
+		uri.s = get_branch(0, &uri.len, &first_q, &dst_uri, &path, &flags,
+			               &sock);
+		first_idx = 1;
     }
     }
-    first_q = get_ruri_q();
-    first_idx = 0;
 
 
     /* Check if all q values are equal */
     /* Check if all q values are equal */
     for(idx = first_idx; (tmp.s = get_branch(idx, &tmp.len, &q, 0, 0, 0, 0))
     for(idx = first_idx; (tmp.s = get_branch(idx, &tmp.len, &q, 0, 0, 0, 0))
@@ -272,15 +278,24 @@ rest:
 		return -1;
 		return -1;
     }
     }
 
 
-    /* Insert Request-URI branch to first contact */
-    contacts->uri.s = ruri->s;
-    contacts->uri.len = ruri->len;
-    contacts->dst_uri = msg->dst_uri;
-    contacts->sock = msg->force_send_socket;
-    getbflagsval(0, &contacts->flags);
-    contacts->path = msg->path_vec;
-    contacts->q = first_q;
-    contacts->next = (struct contact *)0;
+	if (ruri_is_new) {
+		/* Insert Request-URI branch to first contact */
+		contacts->uri.s = ruri->s;
+		contacts->uri.len = ruri->len;
+		contacts->dst_uri = msg->dst_uri;
+		contacts->sock = msg->force_send_socket;
+		getbflagsval(0, &contacts->flags);
+		contacts->path = msg->path_vec;
+	} else {
+		/* Insert first branch to first contact */
+		contacts->uri = uri;
+		contacts->dst_uri = dst_uri;
+		contacts->sock = sock;
+		contacts->flags = flags;
+		contacts->path = path;
+    }
+	contacts->q = first_q;
+	contacts->next = (struct contact *)0;
 
 
     /* Insert (remaining) branches to contact list in increasing q order */
     /* Insert (remaining) branches to contact list in increasing q order */
 
 

+ 49 - 36
modules/tm/timer.c

@@ -150,8 +150,8 @@
 struct msgid_var user_fr_timeout;
 struct msgid_var user_fr_timeout;
 struct msgid_var user_fr_inv_timeout;
 struct msgid_var user_fr_inv_timeout;
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-struct msgid_var user_rt_t1_timeout;
-struct msgid_var user_rt_t2_timeout;
+struct msgid_var user_rt_t1_timeout_ms;
+struct msgid_var user_rt_t2_timeout_ms;
 #endif
 #endif
 struct msgid_var user_inv_max_lifetime;
 struct msgid_var user_inv_max_lifetime;
 struct msgid_var user_noninv_max_lifetime;
 struct msgid_var user_noninv_max_lifetime;
@@ -185,8 +185,6 @@ int tm_init_timers(void)
 	default_tm_cfg.fr_inv_timeout=MS_TO_TICKS(default_tm_cfg.fr_inv_timeout);
 	default_tm_cfg.fr_inv_timeout=MS_TO_TICKS(default_tm_cfg.fr_inv_timeout);
 	default_tm_cfg.wait_timeout=MS_TO_TICKS(default_tm_cfg.wait_timeout);
 	default_tm_cfg.wait_timeout=MS_TO_TICKS(default_tm_cfg.wait_timeout);
 	default_tm_cfg.delete_timeout=MS_TO_TICKS(default_tm_cfg.delete_timeout);
 	default_tm_cfg.delete_timeout=MS_TO_TICKS(default_tm_cfg.delete_timeout);
-	default_tm_cfg.rt_t1_timeout=MS_TO_TICKS(default_tm_cfg.rt_t1_timeout);
-	default_tm_cfg.rt_t2_timeout=MS_TO_TICKS(default_tm_cfg.rt_t2_timeout);
 	default_tm_cfg.tm_max_inv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_inv_lifetime);
 	default_tm_cfg.tm_max_inv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_inv_lifetime);
 	default_tm_cfg.tm_max_noninv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_noninv_lifetime);
 	default_tm_cfg.tm_max_noninv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_noninv_lifetime);
 	/* fix 0 values to 1 tick (minimum possible wait time ) */
 	/* fix 0 values to 1 tick (minimum possible wait time ) */
@@ -194,8 +192,8 @@ int tm_init_timers(void)
 	if (default_tm_cfg.fr_inv_timeout==0) default_tm_cfg.fr_inv_timeout=1;
 	if (default_tm_cfg.fr_inv_timeout==0) default_tm_cfg.fr_inv_timeout=1;
 	if (default_tm_cfg.wait_timeout==0) default_tm_cfg.wait_timeout=1;
 	if (default_tm_cfg.wait_timeout==0) default_tm_cfg.wait_timeout=1;
 	if (default_tm_cfg.delete_timeout==0) default_tm_cfg.delete_timeout=1;
 	if (default_tm_cfg.delete_timeout==0) default_tm_cfg.delete_timeout=1;
-	if (default_tm_cfg.rt_t2_timeout==0) default_tm_cfg.rt_t2_timeout=1;
-	if (default_tm_cfg.rt_t1_timeout==0) default_tm_cfg.rt_t1_timeout=1;
+	if (default_tm_cfg.rt_t2_timeout_ms==0) default_tm_cfg.rt_t2_timeout_ms=1;
+	if (default_tm_cfg.rt_t1_timeout_ms==0) default_tm_cfg.rt_t1_timeout_ms=1;
 	if (default_tm_cfg.tm_max_inv_lifetime==0) default_tm_cfg.tm_max_inv_lifetime=1;
 	if (default_tm_cfg.tm_max_inv_lifetime==0) default_tm_cfg.tm_max_inv_lifetime=1;
 	if (default_tm_cfg.tm_max_noninv_lifetime==0) default_tm_cfg.tm_max_noninv_lifetime=1;
 	if (default_tm_cfg.tm_max_noninv_lifetime==0) default_tm_cfg.tm_max_noninv_lifetime=1;
 	
 	
@@ -203,8 +201,10 @@ int tm_init_timers(void)
 	SIZE_FIT_CHECK(fr_timeout, default_tm_cfg.fr_timeout, "fr_timer");
 	SIZE_FIT_CHECK(fr_timeout, default_tm_cfg.fr_timeout, "fr_timer");
 	SIZE_FIT_CHECK(fr_inv_timeout, default_tm_cfg.fr_inv_timeout, "fr_inv_timer");
 	SIZE_FIT_CHECK(fr_inv_timeout, default_tm_cfg.fr_inv_timeout, "fr_inv_timer");
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-	SIZE_FIT_CHECK(rt_t1_timeout, default_tm_cfg.rt_t1_timeout, "retr_timer1");
-	SIZE_FIT_CHECK(rt_t2_timeout, default_tm_cfg.rt_t2_timeout, "retr_timer2");
+	SIZE_FIT_CHECK(rt_t1_timeout_ms, default_tm_cfg.rt_t1_timeout_ms,
+					"retr_timer1");
+	SIZE_FIT_CHECK(rt_t2_timeout_ms, default_tm_cfg.rt_t2_timeout_ms,
+					"retr_timer2");
 #endif
 #endif
 	SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_inv_lifetime, "max_inv_lifetime");
 	SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_inv_lifetime, "max_inv_lifetime");
 	SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_noninv_lifetime, "max_noninv_lifetime");
 	SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_noninv_lifetime, "max_noninv_lifetime");
@@ -212,8 +212,8 @@ int tm_init_timers(void)
 	memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
 	memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
 	memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout));
 	memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout));
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-	memset(&user_rt_t1_timeout, 0, sizeof(user_rt_t1_timeout));
-	memset(&user_rt_t2_timeout, 0, sizeof(user_rt_t2_timeout));
+	memset(&user_rt_t1_timeout_ms, 0, sizeof(user_rt_t1_timeout_ms));
+	memset(&user_rt_t2_timeout_ms, 0, sizeof(user_rt_t2_timeout_ms));
 #endif
 #endif
 	memset(&user_inv_max_lifetime, 0, sizeof(user_inv_max_lifetime));
 	memset(&user_inv_max_lifetime, 0, sizeof(user_inv_max_lifetime));
 	memset(&user_noninv_max_lifetime, 0, sizeof(user_noninv_max_lifetime));
 	memset(&user_noninv_max_lifetime, 0, sizeof(user_noninv_max_lifetime));
@@ -222,7 +222,7 @@ int tm_init_timers(void)
 			" max_inv_lifetime=%d max_noninv_lifetime=%d\n",
 			" max_inv_lifetime=%d max_noninv_lifetime=%d\n",
 			default_tm_cfg.fr_timeout, default_tm_cfg.fr_inv_timeout,
 			default_tm_cfg.fr_timeout, default_tm_cfg.fr_inv_timeout,
 			default_tm_cfg.wait_timeout, default_tm_cfg.delete_timeout,
 			default_tm_cfg.wait_timeout, default_tm_cfg.delete_timeout,
-			default_tm_cfg.rt_t1_timeout, default_tm_cfg.rt_t2_timeout,
+			default_tm_cfg.rt_t1_timeout_ms, default_tm_cfg.rt_t2_timeout_ms,
 			default_tm_cfg.tm_max_inv_lifetime, default_tm_cfg.tm_max_noninv_lifetime);
 			default_tm_cfg.tm_max_inv_lifetime, default_tm_cfg.tm_max_noninv_lifetime);
 	return 0;
 	return 0;
 error:
 error:
@@ -263,10 +263,6 @@ int timer_fixup(void *handle, str *gname, str *name, void **val)
 	/* size fix checks */
 	/* size fix checks */
 	IF_IS_TIMER_NAME(fr_timeout, "fr_timer")
 	IF_IS_TIMER_NAME(fr_timeout, "fr_timer")
 	else IF_IS_TIMER_NAME(fr_inv_timeout, "fr_inv_timer")
 	else IF_IS_TIMER_NAME(fr_inv_timeout, "fr_inv_timer")
-#ifdef TM_DIFF_RT_TIMEOUT
-	else IF_IS_TIMER_NAME(rt_t1_timeout, "retr_timer1")
-	else IF_IS_TIMER_NAME(rt_t2_timeout, "retr_timer2")
-#endif
 	else IF_IS_TIMER_NAME(end_of_life, "max_inv_lifetime")
 	else IF_IS_TIMER_NAME(end_of_life, "max_inv_lifetime")
 	else IF_IS_TIMER_NAME(end_of_life, "max_noninv_lifetime")
 	else IF_IS_TIMER_NAME(end_of_life, "max_noninv_lifetime")
 
 
@@ -277,6 +273,30 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
+
+
+/** fixup function for timer values that are kept in ms.
+ * (called by the configuration framework)
+ * It checks if the value fits in the tm structures 
+ */
+int timer_fixup_ms(void *handle, str *gname, str *name, void **val)
+{
+	long	t;
+
+	t = (long)(*val);
+
+	/* size fix checks */
+#ifdef TM_DIFF_RT_TIMEOUT
+	IF_IS_TIMER_NAME(rt_t1_timeout_ms, "retr_timer1")
+	else IF_IS_TIMER_NAME(rt_t2_timeout_ms, "retr_timer2")
+#endif
+
+	return 0;
+
+error:
+	return -1;
+}
+
 /******************** handlers ***************************/
 /******************** handlers ***************************/
 
 
 
 
@@ -528,7 +548,8 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 	ticks_t fr_remainder;
 	ticks_t fr_remainder;
 	ticks_t retr_remainder;
 	ticks_t retr_remainder;
 	ticks_t retr_interval;
 	ticks_t retr_interval;
-	ticks_t new_retr_interval;
+	unsigned long new_retr_interval_ms;
+	unsigned long crt_retr_interval_ms;
 	struct cell *t;
 	struct cell *t;
 
 
 	rbuf=(struct  retr_buf*)
 	rbuf=(struct  retr_buf*)
@@ -569,28 +590,20 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 			if ((s_ticks_t)(rbuf->retr_expire-ticks)<=0){
 			if ((s_ticks_t)(rbuf->retr_expire-ticks)<=0){
 				if (rbuf->flags & F_RB_RETR_DISABLED)
 				if (rbuf->flags & F_RB_RETR_DISABLED)
 					goto disabled;
 					goto disabled;
-				/* retr_interval= min (2*ri, rt_t2) , *p==2*ri*/
-				/* no branch version: 
-					#idef CC_SIGNED_RIGHT_SHIFT
-						ri=  rt_t2+((2*ri-rt_t2) & 
-						((signed)(2*ri-rt_t2)>>(sizeof(ticks_t)*8-1));
-					#else
-						ri=rt_t2+((2*ri-rt_t2)& -(2*ri<rt_t2));
-					#endif
-				*/
-				
+				crt_retr_interval_ms = (unsigned long)p;
 				/* get the  current interval from timer param. */
 				/* get the  current interval from timer param. */
-				if ((rbuf->flags & F_RB_T2) || 
-						(((ticks_t)(unsigned long)p)>RT_T2_TIMEOUT(rbuf))){
-					retr_interval=RT_T2_TIMEOUT(rbuf);
-					new_retr_interval=RT_T2_TIMEOUT(rbuf);
+				if (unlikely((rbuf->flags & F_RB_T2) ||
+						(crt_retr_interval_ms > RT_T2_TIMEOUT_MS(rbuf)))){
+					retr_interval = MS_TO_TICKS(RT_T2_TIMEOUT_MS(rbuf));
+					new_retr_interval_ms = RT_T2_TIMEOUT_MS(rbuf);
 				}else{
 				}else{
-					retr_interval=(ticks_t)(unsigned long)p;
-					new_retr_interval=retr_interval<<1;
+					retr_interval = MS_TO_TICKS(crt_retr_interval_ms);
+					new_retr_interval_ms=crt_retr_interval_ms<<1;
 				}
 				}
 #ifdef TIMER_DEBUG
 #ifdef TIMER_DEBUG
-				DBG("tm: timer: retr: new interval %d (max %d)\n", 
-						retr_interval, RT_T2_TIMEOUT(rbuf));
+				DBG("tm: timer: retr: new interval %ld ms / %d ticks"
+						" (max %d ms)\n", new_retr_interval_ms, retr_interval,
+						RT_T2_TIMEOUT_MS(rbuf));
 #endif
 #endif
 				/* we could race with the reply_received code, but the 
 				/* we could race with the reply_received code, but the 
 				 * worst thing that can happen is to delay a reset_to_t2
 				 * worst thing that can happen is to delay a reset_to_t2
@@ -598,9 +611,9 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 				rbuf->retr_expire=ticks+retr_interval;
 				rbuf->retr_expire=ticks+retr_interval;
 				/* set new interval to -1 on error, or retr_int. on success */
 				/* set new interval to -1 on error, or retr_int. on success */
 				retr_remainder=retransmission_handler(rbuf) | retr_interval;
 				retr_remainder=retransmission_handler(rbuf) | retr_interval;
-				/* store the next retr. interval inside the timer struct,
+				/* store the next retr. interval in ms inside the timer struct,
 				 * in the data member */
 				 * in the data member */
-				tl->data=(void*)(unsigned long)(new_retr_interval);
+				tl->data=(void*)(new_retr_interval_ms);
 			}else{
 			}else{
 				retr_remainder= rbuf->retr_expire-ticks;
 				retr_remainder= rbuf->retr_expire-ticks;
 				DBG("tm: timer: retr: nothing to do, expire in %d\n", 
 				DBG("tm: timer: retr: nothing to do, expire in %d\n", 

+ 32 - 28
modules/tm/timer.h

@@ -126,11 +126,11 @@
 
 
 
 
 #ifdef  TM_DIFF_RT_TIMEOUT
 #ifdef  TM_DIFF_RT_TIMEOUT
-#define RT_T1_TIMEOUT(rb)	((rb)->my_T->rt_t1_timeout)
-#define RT_T2_TIMEOUT(rb)	((rb)->my_T->rt_t2_timeout)
+#define RT_T1_TIMEOUT_MS(rb)	((rb)->my_T->rt_t1_timeout_ms)
+#define RT_T2_TIMEOUT_MS(rb)	((rb)->my_T->rt_t2_timeout_ms)
 #else
 #else
-#define RT_T1_TIMEOUT(rb)	(cfg_get(tm, tm_cfg, rt_t1_timeout))
-#define RT_T2_TIMEOUT(rb)	(cfg_get(tm, tm_cfg, rt_t2_timeout))
+#define RT_T1_TIMEOUT_MS(rb)	(cfg_get(tm, tm_cfg, rt_t1_timeout_ms))
+#define RT_T2_TIMEOUT_MS(rb)	(cfg_get(tm, tm_cfg, rt_t2_timeout_ms))
 #endif
 #endif
 
 
 #define TM_REQ_TIMEOUT(t) \
 #define TM_REQ_TIMEOUT(t) \
@@ -142,8 +142,8 @@
 extern struct msgid_var user_fr_timeout;
 extern struct msgid_var user_fr_timeout;
 extern struct msgid_var user_fr_inv_timeout;
 extern struct msgid_var user_fr_inv_timeout;
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
-extern struct msgid_var user_rt_t1_timeout;
-extern struct msgid_var user_rt_t2_timeout;
+extern struct msgid_var user_rt_t1_timeout_ms;
+extern struct msgid_var user_rt_t2_timeout_ms;
 #endif
 #endif
 extern struct msgid_var user_inv_max_lifetime;
 extern struct msgid_var user_inv_max_lifetime;
 extern struct msgid_var user_noninv_max_lifetime;
 extern struct msgid_var user_noninv_max_lifetime;
@@ -166,6 +166,7 @@ extern int tm_init_timers(void);
  * \return 0 on success, -1 on error
  * \return 0 on success, -1 on error
  */
  */
 int timer_fixup(void *handle, str *gname, str *name, void **val);
 int timer_fixup(void *handle, str *gname, str *name, void **val);
+int timer_fixup_ms(void *handle, str *gname, str *name, void **val);
 
 
 ticks_t wait_handler(ticks_t t, struct timer_ln *tl, void* data);
 ticks_t wait_handler(ticks_t t, struct timer_ln *tl, void* data);
 ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
 ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
@@ -176,7 +177,7 @@ ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
 
 
 #define init_rb_timers(rb) \
 #define init_rb_timers(rb) \
 	timer_init(&(rb)->timer, retr_buf_handler, \
 	timer_init(&(rb)->timer, retr_buf_handler, \
-				(void*)(unsigned long)RT_T1_TIMEOUT(rb), 0)
+				(void*)(unsigned long)(RT_T1_TIMEOUT_MS(rb)), 0)
 
 
 /* set fr & retr timer
 /* set fr & retr timer
  * rb  -  pointer to struct retr_buf
  * rb  -  pointer to struct retr_buf
@@ -184,23 +185,26 @@ ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
  * returns: -1 on error, 0 on success
  * returns: -1 on error, 0 on success
  */
  */
 #ifdef TIMER_DEBUG
 #ifdef TIMER_DEBUG
-inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr,
+inline static int _set_fr_retr(struct retr_buf* rb, unsigned retr_ms,
 								const char* file, const char* func,
 								const char* file, const char* func,
 								unsigned line)
 								unsigned line)
 #else
 #else
-inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
+inline static int _set_fr_retr(struct retr_buf* rb, unsigned retr_ms)
 #endif
 #endif
 {
 {
 	ticks_t timeout;
 	ticks_t timeout;
 	ticks_t ticks;
 	ticks_t ticks;
 	ticks_t eol;
 	ticks_t eol;
+	ticks_t retr_ticks;
 	int ret;
 	int ret;
 	
 	
 	ticks=get_ticks_raw();
 	ticks=get_ticks_raw();
 	timeout=rb->my_T->fr_timeout;
 	timeout=rb->my_T->fr_timeout;
 	eol=rb->my_T->end_of_life;
 	eol=rb->my_T->end_of_life;
-	rb->timer.data=(void*)(unsigned long)(2*retr); /* hack , next retr. int. */
-	rb->retr_expire=ticks+retr;
+	/* hack , next retr. int. */
+	retr_ticks = MS_TO_TICKS(retr_ms);
+	rb->timer.data=(void*)(unsigned long)(2*retr_ms);
+	rb->retr_expire=ticks + retr_ticks;
 	if (unlikely(rb->t_active)){
 	if (unlikely(rb->t_active)){
 		/* we could have set_fr_retr called in the same time (acceptable 
 		/* we could have set_fr_retr called in the same time (acceptable 
 		 * race), we rely on timer_add adding it only once */
 		 * race), we rely on timer_add adding it only once */
@@ -211,11 +215,11 @@ inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
 		LOG(L_CRIT, "WARNING: -_set_fr_timer- already added: %p , tl=%p!!!\n",
 		LOG(L_CRIT, "WARNING: -_set_fr_timer- already added: %p , tl=%p!!!\n",
 					rb, &rb->timer);
 					rb, &rb->timer);
 	}
 	}
-	/* set active & if retr==-1 set disabled */
-	rb->flags|= (F_RB_RETR_DISABLED & -(retr==-1)); 
+	/* set active & if retr_ms==-1 set disabled */
+	rb->flags|= (F_RB_RETR_DISABLED & -(retr_ms==(unsigned)-1));
 #ifdef TM_FAST_RETR_TIMER
 #ifdef TM_FAST_RETR_TIMER
-	/* set timer to fast if retr enabled (retr!=-1) */
-	rb->timer.flags|=(F_TIMER_FAST & -(retr!=-1));
+	/* set timer to fast if retr enabled (retr_ms!=-1) */
+	rb->timer.flags|=(F_TIMER_FAST & -(retr_ms!=(unsigned)-1));
 #endif
 #endif
 	/* adjust timeout to MIN(fr, maximum lifetime) if rb is a request
 	/* adjust timeout to MIN(fr, maximum lifetime) if rb is a request
 	 *  (for neg. replies we are force to wait for the ACK so use fr) */
 	 *  (for neg. replies we are force to wait for the ACK so use fr) */
@@ -232,10 +236,10 @@ inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
 		return 0;
 		return 0;
 	}
 	}
 #ifdef TIMER_DEBUG
 #ifdef TIMER_DEBUG
-	ret=timer_add_safe(&(rb)->timer, (timeout<retr)?timeout:retr,
+	ret=timer_add_safe(&(rb)->timer, (timeout<retr_ticks)?timeout:retr_ticks,
 							file, func, line);
 							file, func, line);
 #else
 #else
-	ret=timer_add(&(rb)->timer, (timeout<retr)?timeout:retr);
+	ret=timer_add(&(rb)->timer, (timeout<retr_ticks)?timeout:retr_ticks);
 #endif
 #endif
 	if (ret==0) rb->t_active=1;
 	if (ret==0) rb->t_active=1;
 	membar_write_atomic_op(); /* make sure t_active will be commited to mem.
 	membar_write_atomic_op(); /* make sure t_active will be commited to mem.
@@ -265,7 +269,7 @@ do{ \
 #define switch_rb_retr_to_t2(rb) \
 #define switch_rb_retr_to_t2(rb) \
 	do{ \
 	do{ \
 		(rb)->flags|=F_RB_T2; \
 		(rb)->flags|=F_RB_T2; \
-		(rb)->retr_expire=get_ticks_raw()+RT_T2_TIMEOUT(rb); \
+		(rb)->retr_expire=get_ticks_raw()+MS_TO_TICKS(RT_T2_TIMEOUT_MS(rb)); \
 	}while(0)
 	}while(0)
 
 
 
 
@@ -324,23 +328,23 @@ inline static void change_fr(struct cell* t, ticks_t fr_inv, ticks_t fr)
  *  if timer value==0 => leave it unchanged
  *  if timer value==0 => leave it unchanged
  */
  */
 inline static void change_retr(struct cell* t, int now,
 inline static void change_retr(struct cell* t, int now,
-								ticks_t rt_t1, ticks_t rt_t2)
+								unsigned rt_t1_ms, unsigned rt_t2_ms)
 {
 {
 	int i;
 	int i;
 
 
-	if (rt_t1) t->rt_t1_timeout=rt_t1;
-	if (rt_t2) t->rt_t2_timeout=rt_t2;
+	if (rt_t1_ms) t->rt_t1_timeout_ms=rt_t1_ms;
+	if (rt_t2_ms) t->rt_t2_timeout_ms=rt_t2_ms;
 	if (now){
 	if (now){
 		for (i=0; i<t->nr_of_outgoings; i++){
 		for (i=0; i<t->nr_of_outgoings; i++){
-			if (t->uac[i].request.t_active){ 
-					if ((t->uac[i].request.flags & F_RB_T2) && rt_t2)
+			if (t->uac[i].request.t_active){
+					if ((t->uac[i].request.flags & F_RB_T2) && rt_t2_ms)
 						/* not really needed (?) - if F_RB_T2 is set
 						/* not really needed (?) - if F_RB_T2 is set
 						 * t->rt_t2_timeout will be used anyway */
 						 * t->rt_t2_timeout will be used anyway */
-						t->uac[i].request.timer.data=
-									(void*)(unsigned long)rt_t2;
-					else if (rt_t1)
-						t->uac[i].request.timer.data=
-									(void*)(unsigned long)rt_t1;
+						t->uac[i].request.timer.data =
+							(void*)(unsigned long)rt_t2_ms;
+					else if (rt_t1_ms)
+						t->uac[i].request.timer.data =
+							(void*)(unsigned long)rt_t1_ms;
 			}
 			}
 		}
 		}
 	}
 	}

+ 3 - 2
modules/tm/tm.c

@@ -482,8 +482,8 @@ static param_export_t params[]={
 	{"fr_inv_timer",        PARAM_INT, &default_tm_cfg.fr_inv_timeout        },
 	{"fr_inv_timer",        PARAM_INT, &default_tm_cfg.fr_inv_timeout        },
 	{"wt_timer",            PARAM_INT, &default_tm_cfg.wait_timeout          },
 	{"wt_timer",            PARAM_INT, &default_tm_cfg.wait_timeout          },
 	{"delete_timer",        PARAM_INT, &default_tm_cfg.delete_timeout        },
 	{"delete_timer",        PARAM_INT, &default_tm_cfg.delete_timeout        },
-	{"retr_timer1",         PARAM_INT, &default_tm_cfg.rt_t1_timeout         },
-	{"retr_timer2"  ,       PARAM_INT, &default_tm_cfg.rt_t2_timeout         },
+	{"retr_timer1",         PARAM_INT, &default_tm_cfg.rt_t1_timeout_ms      },
+	{"retr_timer2"  ,       PARAM_INT, &default_tm_cfg.rt_t2_timeout_ms      },
 	{"max_inv_lifetime",    PARAM_INT, &default_tm_cfg.tm_max_inv_lifetime   },
 	{"max_inv_lifetime",    PARAM_INT, &default_tm_cfg.tm_max_inv_lifetime   },
 	{"max_noninv_lifetime", PARAM_INT, &default_tm_cfg.tm_max_noninv_lifetime},
 	{"max_noninv_lifetime", PARAM_INT, &default_tm_cfg.tm_max_noninv_lifetime},
 	{"noisy_ctimer",        PARAM_INT, &default_tm_cfg.noisy_ctimer          },
 	{"noisy_ctimer",        PARAM_INT, &default_tm_cfg.noisy_ctimer          },
@@ -517,6 +517,7 @@ static param_export_t params[]={
 	{"disable_6xx_block",   PARAM_INT, &default_tm_cfg.disable_6xx           },
 	{"disable_6xx_block",   PARAM_INT, &default_tm_cfg.disable_6xx           },
 	{"local_ack_mode",      PARAM_INT, &default_tm_cfg.local_ack_mode        },
 	{"local_ack_mode",      PARAM_INT, &default_tm_cfg.local_ack_mode        },
 	{"failure_reply_mode",  PARAM_INT, &failure_reply_mode                   },
 	{"failure_reply_mode",  PARAM_INT, &failure_reply_mode                   },
+	{"faked_reply_prio",    PARAM_INT, &faked_reply_prio                     },
 #ifdef CANCEL_REASON_SUPPORT
 #ifdef CANCEL_REASON_SUPPORT
 	{"local_cancel_reason", PARAM_INT, &default_tm_cfg.local_cancel_reason   },
 	{"local_cancel_reason", PARAM_INT, &default_tm_cfg.local_cancel_reason   },
 	{"e2e_cancel_reason",   PARAM_INT, &default_tm_cfg.e2e_cancel_reason     },
 	{"e2e_cancel_reason",   PARAM_INT, &default_tm_cfg.e2e_cancel_reason     },

+ 15 - 5
modules/tm/uac.c

@@ -216,6 +216,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	int backup_route_type;
 	int backup_route_type;
 #endif
 #endif
 	snd_flags_t snd_flags;
 	snd_flags_t snd_flags;
+	tm_xdata_t backup_xd;
 
 
 	ret=-1;
 	ret=-1;
 	hi=0; /* make gcc happy */
 	hi=0; /* make gcc happy */
@@ -303,8 +304,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	new_cell->end_of_life=get_ticks_raw()+lifetime;
 	new_cell->end_of_life=get_ticks_raw()+lifetime;
 #ifdef TM_DIFF_RT_TIMEOUT
 #ifdef TM_DIFF_RT_TIMEOUT
 	/* same as above for retransmission intervals */
 	/* same as above for retransmission intervals */
-	new_cell->rt_t1_timeout=cfg_get(tm, tm_cfg, rt_t1_timeout);
-	new_cell->rt_t2_timeout=cfg_get(tm, tm_cfg, rt_t2_timeout);
+	new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms);
+	new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms);
 #endif
 #endif
 
 
 	set_kr(REQ_FWDED);
 	set_kr(REQ_FWDED);
@@ -353,7 +354,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 				lreq.rcv.comp=dst.comp;
 				lreq.rcv.comp=dst.comp;
 			#endif /* USE_COMP */
 			#endif /* USE_COMP */
 				sflag_bk = getsflags();
 				sflag_bk = getsflags();
-				tm_xdata_swap(new_cell, 0);
+				tm_xdata_swap(new_cell, &backup_xd, 0);
 
 
 				/* run the route */
 				/* run the route */
 				backup_route_type = get_route_type();
 				backup_route_type = get_route_type();
@@ -372,7 +373,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 				set_route_type( backup_route_type );
 				set_route_type( backup_route_type );
 
 
 				/* restore original environment */
 				/* restore original environment */
-				tm_xdata_swap(new_cell, 1);
+				tm_xdata_swap(new_cell, &backup_xd, 1);
 				setsflagsval(sflag_bk);
 				setsflagsval(sflag_bk);
 
 
 				if (unlikely(lreq.new_uri.s))
 				if (unlikely(lreq.new_uri.s))
@@ -706,7 +707,7 @@ int req_within(uac_req_t *uac_r)
  * Send an initial request that will start a dialog
  * Send an initial request that will start a dialog
  * WARNING: writes uac_r->dialog
  * WARNING: writes uac_r->dialog
  */
  */
-int req_outside(uac_req_t *uac_r, str* to, str* from)
+int req_outside(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
 {
 {
 	str callid, fromtag;
 	str callid, fromtag;
 
 
@@ -720,6 +721,15 @@ int req_outside(uac_req_t *uac_r, str* to, str* from)
 		goto err;
 		goto err;
 	}
 	}
 
 
+	if (ruri) {
+		uac_r->dialog->rem_target.s = ruri->s;
+		uac_r->dialog->rem_target.len = ruri->len;
+		/* hooks will be set from w_calculate_hooks */
+	}
+
+	if (next_hop) uac_r->dialog->dst_uri = *next_hop;
+	w_calculate_hooks(uac_r->dialog);
+
 	return t_uac(uac_r);
 	return t_uac(uac_r);
 
 
  err:
  err:

+ 2 - 2
modules/tm/uac.h

@@ -83,7 +83,7 @@ extern int goto_on_local_req;
  * Function prototypes
  * Function prototypes
  */
  */
 typedef int (*reqwith_t)(uac_req_t *uac_r);
 typedef int (*reqwith_t)(uac_req_t *uac_r);
-typedef int (*reqout_t)(uac_req_t *uac_r, str* to, str* from);
+typedef int (*reqout_t)(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop);
 typedef int (*req_t)(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop);
 typedef int (*req_t)(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop);
 typedef int (*t_uac_t)(uac_req_t *uac_r);
 typedef int (*t_uac_t)(uac_req_t *uac_r);
 typedef int (*t_uac_with_ids_t)(uac_req_t *uac_r,
 typedef int (*t_uac_with_ids_t)(uac_req_t *uac_r,
@@ -128,7 +128,7 @@ int req_within(uac_req_t *uac_r);
 /*
 /*
  * Send an initial request that will start a dialog
  * Send an initial request that will start a dialog
  */
  */
-int req_outside(uac_req_t *uac_r, str* to, str* from);
+int req_outside(uac_req_t *uac_r, str* ruri, str* to, str* from, str* next_hop);
 
 
 
 
 #ifdef WITH_AS_SUPPORT
 #ifdef WITH_AS_SUPPORT

+ 9 - 4
modules_k/auth_radius/README

@@ -49,7 +49,7 @@ Jan Janak
 
 
         5. Exported Functions
         5. Exported Functions
 
 
-              5.1. radius_www_authorize(realm)
+              5.1. radius_www_authorize(realm [, uri_user])
               5.2. radius_proxy_authorize(realm [, uri_user])
               5.2. radius_proxy_authorize(realm [, uri_user])
 
 
    List of Examples
    List of Examples
@@ -82,7 +82,7 @@ Chapter 1. Admin Guide
 
 
    5. Exported Functions
    5. Exported Functions
 
 
-        5.1. radius_www_authorize(realm)
+        5.1. radius_www_authorize(realm [, uri_user])
         5.2. radius_proxy_authorize(realm [, uri_user])
         5.2. radius_proxy_authorize(realm [, uri_user])
 
 
 1. Overview
 1. Overview
@@ -206,10 +206,10 @@ modparam("auth_radius", "use_ruri_flag", 22)
 
 
 5. Exported Functions
 5. Exported Functions
 
 
-   5.1. radius_www_authorize(realm)
+   5.1. radius_www_authorize(realm [, uri_user])
    5.2. radius_proxy_authorize(realm [, uri_user])
    5.2. radius_proxy_authorize(realm [, uri_user])
 
 
-5.1. radius_www_authorize(realm)
+5.1. radius_www_authorize(realm [, uri_user])
 
 
    The function verifies credentials according to RFC2617. If the
    The function verifies credentials according to RFC2617. If the
    credentials are verified successfully then the function will succeed
    credentials are verified successfully then the function will succeed
@@ -240,6 +240,11 @@ modparam("auth_radius", "use_ruri_flag", 22)
        to the user so he can decide what username and password to use. In
        to the user so he can decide what username and password to use. In
        case of REGISTER requests it is usually hostpart of To URI.
        case of REGISTER requests it is usually hostpart of To URI.
        The string may contain pseudo variables.
        The string may contain pseudo variables.
+     * uri_user - Uri_user is an optional pseudo variable parameter whose
+       value, if present, will be given to Radius server as value of
+       SIP-URI-User check item. If uri_user pseudo variable parameter is
+       not present, the server will generate SIP-URI-User check item value
+       from user part of To/From URI.
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 

+ 12 - 2
modules_k/auth_radius/authorize.c

@@ -236,10 +236,20 @@ int radius_proxy_authorize_2(struct sip_msg* _msg, char* _realm,
 
 
 
 
 /*
 /*
- * Authorize using WWW-Authorize header field
+ * Authorize using WWW-Authorize header field (no URI user parameter given)
  */
  */
-int radius_www_authorize(struct sip_msg* _msg, char* _realm, char* _s2)
+int radius_www_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2)
 {
 {
 	return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)0,
 	return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)0,
 			 HDR_AUTHORIZATION_T);
 			 HDR_AUTHORIZATION_T);
 }
 }
+
+
+/*
+ * Authorize using WWW-Authorize header field (URI user parameter given)
+ */
+int radius_www_authorize_2(struct sip_msg* _msg, char* _realm, char* _uri_user)
+{
+	return authorize(_msg, (pv_elem_t*)_realm, (pv_spec_t *)_uri_user,
+			 HDR_AUTHORIZATION_T);
+}

+ 10 - 5
modules_k/auth_radius/authorize.h

@@ -33,21 +33,26 @@
 
 
 
 
 /*
 /*
- * Authorize using Proxy-Authorize header field (no from parameter given)
+ * Authorize using Proxy-Authorize header field (no URI user parameter given)
  */
  */
 int radius_proxy_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2);
 int radius_proxy_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2);
 
 
 
 
 /*
 /*
- * Authorize using Proxy-Authorize header field (from parameter given)
+ * Authorize using Proxy-Authorize header field (URI user parameter given)
  */
  */
-int radius_proxy_authorize_2(struct sip_msg* _msg, char* _realm, char* _from);
+int radius_proxy_authorize_2(struct sip_msg* _msg, char* _realm, char* _uri_user);
 
 
 
 
 /*
 /*
- * Authorize using WWW-Authorization header field
+ * Authorize using WWW-Authorization header field (no URI user parameter given)
  */
  */
-int radius_www_authorize(struct sip_msg* _msg, char* _realm, char* _s2);
+int radius_www_authorize_1(struct sip_msg* _msg, char* _realm, char* _s2);
+
+/*
+ * Authorize using WWW-Authorization header field (URI user parameter given)
+ */
+int radius_www_authorize_2(struct sip_msg* _msg, char* _realm, char* _uri_user);
 
 
 
 
 #endif /* AUTHORIZE_H */
 #endif /* AUTHORIZE_H */

+ 3 - 1
modules_k/auth_radius/authrad_mod.c

@@ -73,7 +73,9 @@ struct extra_attr *auth_extra = 0;
  * Exported functions
  * Exported functions
  */
  */
 static cmd_export_t cmds[] = {
 static cmd_export_t cmds[] = {
-	{"radius_www_authorize", (cmd_function)radius_www_authorize,   1, auth_fixup,
+	{"radius_www_authorize", (cmd_function)radius_www_authorize_1,   1, auth_fixup,
+			0, REQUEST_ROUTE},
+	{"radius_www_authorize", (cmd_function)radius_www_authorize_2,   2, auth_fixup,
 			0, REQUEST_ROUTE},
 			0, REQUEST_ROUTE},
 	{"radius_proxy_authorize", (cmd_function)radius_proxy_authorize_1, 1, auth_fixup,
 	{"radius_proxy_authorize", (cmd_function)radius_proxy_authorize_1, 1, auth_fixup,
 			0, REQUEST_ROUTE},
 			0, REQUEST_ROUTE},

+ 12 - 1
modules_k/auth_radius/doc/auth_radius_admin.xml

@@ -193,7 +193,7 @@ modparam("auth_radius", "use_ruri_flag", 22)
 	<section>
 	<section>
 	<title>Exported Functions</title>
 	<title>Exported Functions</title>
 	<section>
 	<section>
-		<title><function moreinfo="none">radius_www_authorize(realm)</function></title>
+		<title><function moreinfo="none">radius_www_authorize(realm [, uri_user])</function></title>
 		<para>
 		<para>
 		The function verifies credentials according to 
 		The function verifies credentials according to 
 		<ulink url="http://www.ietf.org/rfc/rfc2617.txt">RFC2617</ulink>. If 
 		<ulink url="http://www.ietf.org/rfc/rfc2617.txt">RFC2617</ulink>. If 
@@ -253,6 +253,17 @@ modparam("auth_radius", "use_ruri_flag", 22)
 			The string may contain pseudo variables.
 			The string may contain pseudo variables.
 			</para>
 			</para>
 		</listitem>
 		</listitem>
+		<listitem>
+			<para><emphasis>uri_user</emphasis> - Uri_user is an
+			optional pseudo variable parameter whose value, if
+			present, will be given to Radius server as value of
+			SIP-URI-User check item.
+			If uri_user pseudo variable parameter is not
+			present, the server will generate 
+                        SIP-URI-User check item value from user part of
+			To/From URI.
+			</para>
+		</listitem>
 		</itemizedlist>
 		</itemizedlist>
 		<para>
 		<para>
 		This function can be used from REQUEST_ROUTE.
 		This function can be used from REQUEST_ROUTE.

+ 1 - 0
modules_k/dialog/dialog.c

@@ -373,6 +373,7 @@ static int fixup_get_profile3(void** param, int param_no)
 int load_dlg( struct dlg_binds *dlgb )
 int load_dlg( struct dlg_binds *dlgb )
 {
 {
 	dlgb->register_dlgcb = register_dlgcb;
 	dlgb->register_dlgcb = register_dlgcb;
+	dlgb->terminate_dlg = dlg_bye_all;
 	dlgb->set_dlg_var = set_dlg_variable;
 	dlgb->set_dlg_var = set_dlg_variable;
 	dlgb->get_dlg_var = get_dlg_variable;
 	dlgb->get_dlg_var = get_dlg_variable;
 	return 1;
 	return 1;

+ 18 - 13
modules_k/dialog/dlg_hash.c

@@ -895,11 +895,25 @@ static inline int internal_mi_print_dlg(struct mi_node *rpl,
 	if(node1 == 0)
 	if(node1 == 0)
 		goto error;
 		goto error;
 
 
-	node1 = add_mi_node_child(node, 0,"caller_bind_addr",16,
-			dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s, 
+	if (dlg->bind_addr[DLG_CALLER_LEG]) {
+		node1 = add_mi_node_child(node, 0,
+			"caller_bind_addr",16,
+			dlg->bind_addr[DLG_CALLER_LEG]->sock_str.s,
 			dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len);
 			dlg->bind_addr[DLG_CALLER_LEG]->sock_str.len);
-	if(node1 == 0)
-		goto error;
+	} else {
+		node1 = add_mi_node_child(node, 0,
+			"caller_bind_addr",16,0,0);
+	}
+
+	if (dlg->bind_addr[DLG_CALLEE_LEG]) {
+		node1 = add_mi_node_child(node, 0,
+			"callee_bind_addr",16,
+			dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s,
+			dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len);
+	} else {
+		node1 = add_mi_node_child(node, 0,
+			"callee_bind_addr",16,0,0);
+	}
 
 
 	node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_uri", 6,
 	node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_uri", 6,
 			dlg->to_uri.s, dlg->to_uri.len);
 			dlg->to_uri.s, dlg->to_uri.len);
@@ -929,15 +943,6 @@ static inline int internal_mi_print_dlg(struct mi_node *rpl,
 	if(node1 == 0)
 	if(node1 == 0)
 		goto error;
 		goto error;
 
 
-	if (dlg->bind_addr[DLG_CALLEE_LEG]) {
-		node1 = add_mi_node_child(node, 0,
-			"callee_bind_addr",16,
-			dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.s, 
-			dlg->bind_addr[DLG_CALLEE_LEG]->sock_str.len);
-	} else {
-		node1 = add_mi_node_child(node, 0,
-			"callee_bind_addr",16,0,0);
-	}
 	if(node1 == 0)
 	if(node1 == 0)
 		goto error;
 		goto error;
 
 

+ 4 - 1
modules_k/dialog/dlg_load.h

@@ -32,8 +32,12 @@
 #include "dlg_cb.h"
 #include "dlg_cb.h"
 #include "../../sr_module.h"
 #include "../../sr_module.h"
 
 
+/* terminate_dlg function prototype */
+typedef int (*terminate_dlg_f)(struct dlg_cell* dlg, str *hdrs);
+
 struct dlg_binds {
 struct dlg_binds {
 	register_dlgcb_f  register_dlgcb;
 	register_dlgcb_f  register_dlgcb;
+	terminate_dlg_f terminate_dlg;
     set_dlg_variable_f set_dlg_var;
     set_dlg_variable_f set_dlg_var;
 	get_dlg_variable_f get_dlg_var;
 	get_dlg_variable_f get_dlg_var;
 };
 };
@@ -42,7 +46,6 @@ struct dlg_binds {
 typedef int(*load_dlg_f)( struct dlg_binds *dlgb );
 typedef int(*load_dlg_f)( struct dlg_binds *dlgb );
 int load_dlg( struct dlg_binds *dlgb);
 int load_dlg( struct dlg_binds *dlgb);
 
 
-
 static inline int load_dlg_api( struct dlg_binds *dlgb )
 static inline int load_dlg_api( struct dlg_binds *dlgb )
 {
 {
 	load_dlg_f load_dlg;
 	load_dlg_f load_dlg;

+ 7 - 8
modules_k/dialog/dlg_timer.c

@@ -133,9 +133,9 @@ int insert_dlg_timer(struct dlg_tl *tl, int interval)
 	lock_get( d_timer->lock);
 	lock_get( d_timer->lock);
 
 
 	if (tl->next!=0 || tl->prev!=0) {
 	if (tl->next!=0 || tl->prev!=0) {
-		lock_release( d_timer->lock);
 		LM_CRIT("Trying to insert a bogus dlg tl=%p tl->next=%p tl->prev=%p\n",
 		LM_CRIT("Trying to insert a bogus dlg tl=%p tl->next=%p tl->prev=%p\n",
 			tl, tl->next, tl->prev);
 			tl, tl->next, tl->prev);
+		lock_release( d_timer->lock);
 		return -1;
 		return -1;
 	}
 	}
 	tl->timeout = get_ticks()+interval;
 	tl->timeout = get_ticks()+interval;
@@ -202,14 +202,13 @@ int update_dlg_timer(struct dlg_tl *tl, int timeout)
 {
 {
 	lock_get( d_timer->lock);
 	lock_get( d_timer->lock);
 
 
-	if ( tl->next ) {
-		if (tl->prev==0) {
-			lock_release( d_timer->lock);
-			return -1;
-		}
-		remove_dialog_timer_unsafe(tl);
+	if (tl->next==0 || tl->prev==0) {
+		LM_CRIT("Trying to update a bogus dlg tl=%p tl->next=%p tl->prev=%p\n",
+			tl, tl->next, tl->prev);
+		lock_release( d_timer->lock);
+		return -1;
 	}
 	}
-
+	remove_dialog_timer_unsafe( tl );
 	tl->timeout = get_ticks()+timeout;
 	tl->timeout = get_ticks()+timeout;
 	insert_dialog_timer_unsafe( tl );
 	insert_dialog_timer_unsafe( tl );
 
 

+ 23 - 0
modules_k/dialog/doc/dialog_devel.xml

@@ -135,6 +135,29 @@
 		</itemizedlist>
 		</itemizedlist>
 	</section>
 	</section>
 
 
+
+	<section>
+		<title>
+		<function moreinfo="none">terminate_dlg (dlg, hdrs)</function>
+		</title>
+		<para>
+		Terminate a Dialog
+		</para>
+		<para>Meaning of parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>struct dlg_cell* dlg</emphasis> - dialog to 
+			terminate.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>str* hdrs</emphasis> - string containg extra headers (full format) 
+			to be added to the BYE requests of the dialog.
+			</para>
+		</listitem>
+		</itemizedlist>
+	</section>
+
 	</section>
 	</section>
 
 
 </chapter>
 </chapter>

+ 23 - 0
modules_k/dmq/Makefile

@@ -0,0 +1,23 @@
+# $Id$
+#
+# distributed message queue system for inter-intstance communication
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=dmq.so
+LIBS=
+
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+      -I$(LOCALBASE)/include
+LIBS+=-L/usr/include/lib  -L$(LOCALBASE)/lib -lxml2
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+include ../../Makefile.modules

+ 11 - 0
modules_k/dmq/bind_dmq.c

@@ -0,0 +1,11 @@
+#include "dmq.h"
+#include "bind_dmq.h"
+#include "peer.h"
+#include "dmq_funcs.h"
+
+int bind_dmq(dmq_api_t* api) {
+	api->register_dmq_peer = register_dmq_peer;
+	api->send_message = send_dmq_message;
+	api->bcast_message = bcast_dmq_message;
+	return 0;
+}

+ 21 - 0
modules_k/dmq/bind_dmq.h

@@ -0,0 +1,21 @@
+#ifndef BIND_DMQ_H
+#define BIND_DMQ_H
+
+#include "peer.h"
+#include "dmqnode.h"
+#include "dmq_funcs.h"
+
+typedef int (*bcast_message_t)(dmq_peer_t* peer, str* body, dmq_node_t* except, dmq_resp_cback_t* resp_cback, int max_forwards);
+typedef int (*send_message_t)(dmq_peer_t* peer, str* body, dmq_node_t* node, dmq_resp_cback_t* resp_cback, int max_forwards);
+
+typedef struct dmq_api {
+	register_dmq_peer_t register_dmq_peer;
+	bcast_message_t bcast_message;
+	send_message_t send_message;
+} dmq_api_t;
+
+typedef int (*bind_dmq_f)(dmq_api_t* api);
+
+int bind_dmq(dmq_api_t* api);
+
+#endif

+ 283 - 0
modules_k/dmq/dmq.c

@@ -0,0 +1,283 @@
+/*
+ * $Id$
+ *
+ * dmq module - distributed message queue
+ *
+ * Copyright (C) 2011 Bucur Marius - Ovidiu
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2010-03-29  initial version (mariusbucur)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../usr_avp.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../parser/parse_uri.h"
+#include "../../modules/sl/sl.h"
+#include "../../pt.h"
+#include "../../lib/kmi/mi.h"
+#include "../../lib/kcore/hash_func.h"
+#include "dmq.h"
+#include "dmq_funcs.h"
+#include "peer.h"
+#include "bind_dmq.h"
+#include "worker.h"
+#include "notification_peer.h"
+#include "dmqnode.h"
+#include "../../mod_fix.h"
+
+static int mod_init(void);
+static int child_init(int);
+static void destroy(void);
+
+MODULE_VERSION
+
+int startup_time = 0;
+int pid = 0;
+
+/* module parameters */
+int num_workers = DEFAULT_NUM_WORKERS;
+str dmq_server_address = {0, 0};
+struct sip_uri dmq_server_uri;
+
+str dmq_notification_address = {0, 0};
+struct sip_uri dmq_notification_uri;
+int ping_interval = 4;
+
+/* TM bind */
+struct tm_binds tmb;
+/* SL API structure */
+sl_api_t slb;
+
+/** module variables */
+str dmq_request_method = {"KDMQ", 4};
+dmq_worker_t* workers;
+dmq_peer_list_t* peer_list;
+/* the list of dmq servers */
+dmq_node_list_t* node_list;
+// the dmq module is a peer itself for receiving notifications regarding nodes
+dmq_peer_t* dmq_notification_peer;
+
+/** module functions */
+static int mod_init(void);
+static int child_init(int);
+static void destroy(void);
+static int handle_dmq_fixup(void** param, int param_no);
+static int parse_server_address(str* uri, struct sip_uri* parsed_uri);
+
+static cmd_export_t cmds[] = {
+	{"handle_dmq_message",  (cmd_function)handle_dmq_message, 0, handle_dmq_fixup, 0, 
+		REQUEST_ROUTE},
+	{"bind_dmq",        (cmd_function)bind_dmq, 0, 0, 0,
+		REQUEST_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+	{"num_workers", INT_PARAM, &num_workers},
+	{"ping_interval", INT_PARAM, &ping_interval},
+	{"server_address", STR_PARAM, &dmq_server_address.s},
+	{"notification_address", STR_PARAM, &dmq_notification_address.s},
+	{0, 0, 0}
+};
+
+static mi_export_t mi_cmds[] = {
+	{0, 0, 0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports = {
+	"dmq",				/* module name */
+	DEFAULT_DLFLAGS,		/* dlopen flags */
+	cmds,				/* exported functions */
+	params,				/* exported parameters */
+	0,				/* exported statistics */
+	mi_cmds,   			/* exported MI functions */
+	0,				/* exported pseudo-variables */
+	0,				/* extra processes */
+	mod_init,			/* module initialization function */
+	0,   				/* response handling function */
+	(destroy_function) destroy, 	/* destroy function */
+	child_init                  	/* per-child init function */
+};
+
+/**
+ * init module function
+ */
+static int mod_init(void) {
+	
+	if(register_mi_mod(exports.name, mi_cmds)!=0) {
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
+	/* bind the SL API */
+	if (sl_load_api(&slb)!=0) {
+		LM_ERR("cannot bind to SL API\n");
+		return -1;
+	}
+	
+	/* load all TM stuff */
+	if(load_tm_api(&tmb)==-1) {
+		LM_ERR("can't load tm functions. TM module probably not loaded\n");
+		return -1;
+	}
+	/* load peer list - the list containing the module callbacks for dmq */
+	
+	peer_list = init_peer_list();
+	
+	/* load the dmq node list - the list containing the dmq servers */
+	node_list = init_dmq_node_list();
+	
+	/* register worker processes - add one because of the ping process */
+	register_procs(num_workers);
+	
+	/* check server_address and notification_address are not empty and correct */
+	if(parse_server_address(&dmq_server_address, &dmq_server_uri) < 0) {
+		LM_ERR("server address invalid\n");
+		return -1;
+	}
+	
+	if(parse_server_address(&dmq_notification_address, &dmq_notification_uri) < 0) {
+		LM_ERR("notification address invalid\n");
+		return -1;
+	}
+	
+	/* allocate workers array */
+	workers = shm_malloc(num_workers * sizeof(*workers));
+	if(workers == NULL) {
+		LM_ERR("error in shm_malloc\n");
+		return -1;
+	}
+	
+	/**
+         * add the dmq notification peer.
+	 * the dmq is a peer itself so that it can receive node notifications
+	 */
+	add_notification_peer();
+	
+	startup_time = (int) time(NULL);
+	
+	/**
+	 * add the ping timer
+	 * it pings the servers once in a while so that we know which failed
+	 */
+	if(ping_interval < MIN_PING_INTERVAL) {
+		ping_interval = MIN_PING_INTERVAL;
+	}
+	register_timer(ping_servers, 0, ping_interval);
+	
+	return 0;
+}
+
+/**
+ * initialize children
+ */
+static int child_init(int rank) {
+  	int i, newpid;
+	if (rank == PROC_MAIN) {
+		/* fork worker processes */
+		for(i = 0; i < num_workers; i++) {
+			init_worker(&workers[i]);
+			LM_DBG("starting worker process %d\n", i);
+			newpid = fork_process(PROC_NOCHLDINIT, "DMQ WORKER", 0);
+			if(newpid < 0) {
+				LM_ERR("failed to form process\n");
+				return -1;
+			} else if(newpid == 0) {
+				// child - this will loop forever
+				worker_loop(i);
+			} else {
+				workers[i].pid = newpid;
+			}
+		}
+		/* notification_node - the node from which the Kamailio instance
+		 * gets the server list on startup.
+		 * the address is given as a module parameter in dmq_notification_address
+		 * the module MUST have this parameter if the Kamailio instance is not
+		 * a master in this architecture
+		 */
+		if(dmq_notification_address.s) {
+			notification_node = add_server_and_notify(&dmq_notification_address);
+			if(!notification_node) {
+				LM_ERR("cannot retrieve initial nodelist from %.*s\n",
+				       STR_FMT(&dmq_notification_address));
+				return -1;
+			}
+		}
+		return 0;
+	}
+	if(rank == PROC_INIT || rank == PROC_TCP_MAIN) {
+		/* do nothing for the main process */
+		return 0;
+	}
+
+	pid = my_pid();
+	return 0;
+}
+
+/*
+ * destroy function
+ */
+static void destroy(void) {
+	/* TODO unregister dmq node, free resources */
+	if(dmq_notification_address.s) {
+		LM_DBG("unregistering node %.*s\n", STR_FMT(&self_node->orig_uri));
+		self_node->status = DMQ_NODE_DISABLED;
+		request_nodelist(notification_node, 1);
+	}
+}
+
+static int handle_dmq_fixup(void** param, int param_no) {
+ 	return 0;
+}
+
+static int parse_server_address(str* uri, struct sip_uri* parsed_uri) {
+	if(!uri->s) {
+		LM_ERR("server address missing\n");
+		goto empty;
+	}
+	uri->len = strlen(uri->s);
+	if(!uri->len) {
+		LM_ERR("empty server address\n");
+		goto empty;
+	}
+	if(parse_uri(uri->s, uri->len, parsed_uri) < 0) {
+		LM_ERR("error parsing server address\n");
+		return -1;
+	}
+	return 0;
+empty:
+	uri->s = NULL;
+	return 0;
+}

+ 52 - 0
modules_k/dmq/dmq.h

@@ -0,0 +1,52 @@
+#ifndef DMQ_H
+#define DMQ_H
+
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../sr_module.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../parser/parse_uri.h"
+#include "../../modules/sl/sl.h"
+#include "bind_dmq.h"
+#include "peer.h"
+#include "worker.h"
+
+#define DEFAULT_NUM_WORKERS	2
+#define MIN_PING_INTERVAL	60
+
+extern int num_workers;
+extern dmq_worker_t* workers;
+extern dmq_peer_t* dmq_notification_peer;
+extern str dmq_server_address;
+extern dmq_peer_list_t* peer_list;
+extern str dmq_request_method;
+extern struct sip_uri dmq_server_uri;
+extern str dmq_notification_address;
+extern struct sip_uri dmq_notification_uri;
+/* sl and tm */
+extern struct tm_binds tmb;
+extern sl_api_t slb;
+
+extern str dmq_200_rpl;
+extern str dmq_400_rpl;
+extern str dmq_500_rpl;
+extern str dmq_404_rpl;
+
+static inline int dmq_load_api(dmq_api_t* api) {
+	bind_dmq_f binddmq;
+	binddmq = (bind_dmq_f)find_export("bind_dmq", 0, 0);
+	if ( binddmq == 0) {
+		LM_ERR("cannot find bind_dmq\n");
+		return -1;
+	}
+	if (binddmq(api) < 0)
+	{
+		LM_ERR("cannot bind dmq api\n");
+		return -1;
+	}
+	return 0;
+}
+
+int handle_dmq_message(struct sip_msg* msg, char* str1 ,char* str2);
+
+#endif

+ 169 - 0
modules_k/dmq/dmq_funcs.c

@@ -0,0 +1,169 @@
+#include "dmq_funcs.h"
+#include "notification_peer.h"
+
+dmq_peer_t* register_dmq_peer(dmq_peer_t* peer) {
+	dmq_peer_t* new_peer;
+	lock_get(&peer_list->lock);
+	if(search_peer_list(peer_list, peer)) {
+		LM_ERR("peer already exists: %.*s %.*s\n", peer->peer_id.len, peer->peer_id.s,
+		       peer->description.len, peer->description.s);
+		lock_release(&peer_list->lock);
+		return NULL;
+	}
+	new_peer = add_peer(peer_list, peer);
+	lock_release(&peer_list->lock);
+	return new_peer;
+}
+
+void dmq_tm_callback(struct cell *t, int type, struct tmcb_params *ps) {
+	dmq_cback_param_t* cb_param = (dmq_cback_param_t*)(*ps->param);
+	LM_DBG("dmq_tm_callback start\n");
+	if(cb_param->resp_cback.f) {
+		if(cb_param->resp_cback.f(ps->rpl, ps->code, cb_param->node, cb_param->resp_cback.param) < 0) {
+			LM_ERR("error in response callback\n");
+		}
+	}
+	LM_DBG("dmq_tm_callback done\n");
+	shm_free_node(cb_param->node);
+	shm_free(cb_param);
+}
+
+int build_uri_str(str* username, struct sip_uri* uri, str* from) {
+	/* sip:user@host:port */
+	int from_len = username->len + uri->host.len + uri->port.len + 10;
+	if(!uri->host.s || !uri->host.len) {
+		LM_ERR("no host in uri\n");
+		return -1;
+	}
+	if(!username->s || !username->len) {
+		LM_ERR("no username given\n");
+		return -1;
+	}
+	from->s = pkg_malloc(from_len);
+	from->len = 0;
+	
+	memcpy(from->s, "sip:", 4);
+	from->len += 4;
+	
+	memcpy(from->s + from->len, username->s, username->len);
+	from->len += username->len;
+	
+	memcpy(from->s + from->len, "@", 1);
+	from->len += 1;
+	
+	memcpy(from->s + from->len, uri->host.s, uri->host.len);
+	from->len += uri->host.len;
+	
+	if(uri->port.s && uri->port.len) {
+		memcpy(from->s + from->len, ":", 1);
+		from->len += 1;
+		memcpy(from->s + from->len, uri->port.s, uri->port.len);
+		from->len += uri->port.len;
+	}
+	return 0;
+}
+
+/* broadcast a dmq message
+ * peer - the peer structure on behalf of which we are sending
+ * body - the body of the message
+ * except - we do not send the message to this node
+ * resp_cback - a response callback that gets called when the transaction is complete
+ */
+int bcast_dmq_message(dmq_peer_t* peer, str* body, dmq_node_t* except, dmq_resp_cback_t* resp_cback, int max_forwards) {
+	dmq_node_t* node;
+	
+	lock_get(&node_list->lock);
+	node = node_list->nodes;
+	while(node) {
+		/* we do not send the message to the following:
+		 *   - the except node
+		 *   - itself
+		 *   - any inactive nodes
+		 */
+		if((except && cmp_dmq_node(node, except)) || node->local || node->status != DMQ_NODE_ACTIVE) {
+			LM_DBG("skipping node %.*s\n", STR_FMT(&node->orig_uri));
+			node = node->next;
+			continue;
+		}
+		if(send_dmq_message(peer, body, node, resp_cback, max_forwards) < 0) {
+			LM_ERR("error sending dmq message\n");
+			goto error;
+		}
+		node = node->next;
+	}
+	lock_release(&node_list->lock);
+	return 0;
+error:
+	lock_release(&node_list->lock);
+	return -1;
+}
+
+/* send a dmq message
+ * peer - the peer structure on behalf of which we are sending
+ * body - the body of the message
+ * node - we send the message to this node
+ * resp_cback - a response callback that gets called when the transaction is complete
+ */
+int send_dmq_message(dmq_peer_t* peer, str* body, dmq_node_t* node, dmq_resp_cback_t* resp_cback, int max_forwards) {
+	uac_req_t uac_r;
+	str str_hdr = {0, 0};
+	str from, to, req_uri;
+	dmq_cback_param_t* cb_param = NULL;
+	int result = 0;
+	int len = 0;
+	
+	/* Max-Forwards */
+	str_hdr.len = 18 + CRLF_LEN;
+	str_hdr.s = pkg_malloc(str_hdr.len);
+	len += sprintf(str_hdr.s, "Max-Forwards: %d%s", max_forwards, CRLF);
+	str_hdr.len = len;
+	
+	cb_param = shm_malloc(sizeof(*cb_param));
+	memset(cb_param, 0, sizeof(*cb_param));
+	cb_param->resp_cback = *resp_cback;
+	cb_param->node = shm_dup_node(node);
+	
+	if(build_uri_str(&peer->peer_id, &dmq_server_uri, &from) < 0) {
+		LM_ERR("error building from string [username %.*s]\n", STR_FMT(&peer->peer_id));
+		goto error;
+	}
+	if(build_uri_str(&peer->peer_id, &node->uri, &to) < 0) {
+		LM_ERR("error building to string\n");
+		goto error;
+	}
+	req_uri = to;
+	
+	set_uac_req(&uac_r, &dmq_request_method, &str_hdr, body, NULL, TMCB_LOCAL_COMPLETED,
+			dmq_tm_callback, (void*)cb_param);
+	result = tmb.t_request(&uac_r, &req_uri,
+			       &to, &from,
+			       NULL);
+	if(result < 0) {
+		LM_ERR("error in tmb.t_request_within\n");
+		goto error;
+	}
+	pkg_free(str_hdr.s);
+	return 0;
+error:
+	pkg_free(str_hdr.s);
+	return -1;
+}
+
+/* pings the servers in the nodelist
+ * if the server does not reply to the ping, it is removed from the list
+ * the ping messages are actualy notification requests
+ * this way the ping will have two uses:
+ *   - checks if the servers in the list are up and running
+ *   - updates the list of servers from the other nodes
+ */
+void ping_servers(unsigned int ticks,void *param) {
+	str* body = build_notification_body();
+	int ret;
+	LM_DBG("ping_servers\n");
+	ret = bcast_dmq_message(dmq_notification_peer, body, notification_node, &notification_callback, 1);
+	pkg_free(body->s);
+	pkg_free(body);
+	if(ret < 0) {
+		LM_ERR("error broadcasting message\n");
+	}
+}

+ 28 - 0
modules_k/dmq/dmq_funcs.h

@@ -0,0 +1,28 @@
+#ifndef DMQ_FUNCS_H
+#define DMQ_FUNCS_H
+
+#include "../../str.h"
+#include "../../modules/tm/dlg.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../config.h"
+#include "peer.h"
+#include "worker.h"
+#include "dmqnode.h"
+
+void ping_servers(unsigned int ticks,void *param);
+
+typedef struct dmq_resp_cback {
+	int (*f)(struct sip_msg* msg, int code, dmq_node_t* node, void* param);
+	void* param;
+} dmq_resp_cback_t;
+
+typedef struct dmq_cback_param {
+	dmq_resp_cback_t resp_cback;
+	dmq_node_t* node;
+} dmq_cback_param_t;
+
+dmq_peer_t* register_dmq_peer(dmq_peer_t* peer);
+int send_dmq_message(dmq_peer_t* peer, str* body, dmq_node_t* node, dmq_resp_cback_t* resp_cback, int max_forwards);
+int bcast_dmq_message(dmq_peer_t* peer, str* body, dmq_node_t* except, dmq_resp_cback_t* resp_cback, int max_forwards);
+
+#endif

+ 248 - 0
modules_k/dmq/dmqnode.c

@@ -0,0 +1,248 @@
+#include "../../ut.h"
+#include "dmqnode.h"
+#include "dmq.h"
+
+dmq_node_t* self_node;
+dmq_node_t* notification_node;
+
+/* name */
+str dmq_node_status_str = str_init("status");
+/* possible values */
+str dmq_node_active_str = str_init("active");
+str dmq_node_disabled_str = str_init("disabled");
+str dmq_node_timeout_str = str_init("timeout");
+
+str* get_status_str(int status) {
+	switch(status) {
+		case DMQ_NODE_ACTIVE: {
+			return &dmq_node_active_str;
+		}
+		case DMQ_NODE_DISABLED: {
+			return &dmq_node_disabled_str;
+		}
+		case DMQ_NODE_TIMEOUT: {
+			return &dmq_node_timeout_str;
+		}
+		default: {
+			return 0;
+		}
+	}
+}
+
+dmq_node_list_t* init_dmq_node_list() {
+	dmq_node_list_t* node_list = shm_malloc(sizeof(dmq_node_list_t));
+	memset(node_list, 0, sizeof(dmq_node_list_t));
+	lock_init(&node_list->lock);
+	return node_list;
+}
+
+inline int cmp_dmq_node(dmq_node_t* node, dmq_node_t* cmpnode) {
+	if(!node || !cmpnode) {
+		LM_ERR("cmp_dmq_node - null node received\n");
+		return -1;
+	}
+	return STR_EQ(node->uri.host, cmpnode->uri.host) &&
+	       STR_EQ(node->uri.port, cmpnode->uri.port);
+}
+
+static str* get_param_value(param_t* params, str* param) {
+	while (params) {
+		if ((params->name.len == param->len) &&
+		    (strncmp(params->name.s, param->s, param->len) == 0)) {
+			return &params->body;
+		}
+		params = params->next;
+	}
+	return NULL;
+}
+
+inline int set_dmq_node_params(dmq_node_t* node, param_t* params) {
+	str* status;
+	if(!params) {
+		LM_DBG("no parameters given\n");
+		return 0;
+	}
+	status = get_param_value(params, &dmq_node_status_str);
+	if(status) {
+		if(str_strcmp(status, &dmq_node_active_str)) {
+			node->status = DMQ_NODE_ACTIVE;
+		} else if(str_strcmp(status, &dmq_node_timeout_str)) {
+			node->status = DMQ_NODE_ACTIVE;
+		} else if(str_strcmp(status, &dmq_node_disabled_str)) {
+			node->status = DMQ_NODE_ACTIVE;
+		} else {
+			LM_ERR("invalid status parameter: %.*s\n", STR_FMT(status));
+			goto error;
+		}
+	}
+	return 0;
+error:
+	return -1;
+}
+
+inline int set_default_dmq_node_params(dmq_node_t* node) {
+	node->status = DMQ_NODE_ACTIVE;
+	return 0;
+}
+
+inline dmq_node_t* build_dmq_node(str* uri, int shm) {
+	dmq_node_t* ret;
+	param_hooks_t hooks;
+	param_t* params;
+	
+	LM_DBG("build_dmq_node %.*s with %s memory\n", STR_FMT(uri), shm?"shm":"private");
+	
+	if(shm) {
+		ret = shm_malloc(sizeof(*ret));
+		memset(ret, 0, sizeof(*ret));
+		shm_str_dup(&ret->orig_uri, uri);
+	} else {
+		ret = pkg_malloc(sizeof(*ret));
+		memset(ret, 0, sizeof(*ret));
+		pkg_str_dup(&ret->orig_uri, uri);
+	}
+	set_default_dmq_node_params(ret);
+	if(parse_uri(ret->orig_uri.s, ret->orig_uri.len, &ret->uri) < 0) {
+		LM_ERR("error parsing uri\n");
+		goto error;
+	}
+	/* if any parameters found, parse them */
+	if(parse_params(&ret->uri.params, CLASS_ANY, &hooks, &params) < 0) {
+		LM_ERR("error parsing params\n");
+		goto error;
+	}
+	/* if any params found */
+	if(params) {
+		if(shm) {
+			if(shm_duplicate_params(&ret->params, params) < 0) {
+				LM_ERR("error duplicating params\n");
+				free_params(params);
+				goto error;
+			}
+			free_params(params);
+		} else {
+			ret->params = params;
+		}
+		if(set_dmq_node_params(ret, ret->params) < 0) {
+			LM_ERR("error setting parameters\n");
+			goto error;
+		}
+	} else {
+		LM_DBG("no dmqnode params found\n");		
+	}
+	return ret;
+error:
+	return NULL;
+}
+
+inline dmq_node_t* find_dmq_node_uri(dmq_node_list_t* list, str* uri) {
+	dmq_node_t *ret, *find;
+	find =  build_dmq_node(uri, 0);
+	ret = find_dmq_node(list, find);
+	destroy_dmq_node(find, 0);
+	return ret;
+}
+
+inline void destroy_dmq_node(dmq_node_t* node, int shm) {
+	if(shm) {
+		shm_free_node(node);
+	} else {
+		pkg_free_node(node);
+	}
+}
+
+inline dmq_node_t* find_dmq_node(dmq_node_list_t* list, dmq_node_t* node) {
+	dmq_node_t* cur = list->nodes;
+	while(cur) {
+		if(cmp_dmq_node(cur, node)) {
+			return cur;
+		}
+		cur = cur->next;
+	}
+	return NULL;
+}
+
+inline dmq_node_t* shm_dup_node(dmq_node_t* node) {
+	dmq_node_t* newnode = shm_malloc(sizeof(dmq_node_t));
+	memcpy(newnode, node, sizeof(dmq_node_t));
+	shm_str_dup(&newnode->orig_uri, &node->orig_uri);
+	if(parse_uri(newnode->orig_uri.s, newnode->orig_uri.len, &newnode->uri) < 0) {
+		LM_ERR("error in parsing node uri\n");
+		goto error;
+	}
+	return newnode;
+error:
+	shm_free(newnode->orig_uri.s);
+	shm_free(newnode);
+	return NULL;
+}
+
+inline void shm_free_node(dmq_node_t* node) {
+	shm_free(node->orig_uri.s);
+	shm_free(node);
+}
+
+inline void pkg_free_node(dmq_node_t* node) {
+	pkg_free(node->orig_uri.s);
+	pkg_free(node);
+}
+
+inline int del_dmq_node(dmq_node_list_t* list, dmq_node_t* node) {
+	dmq_node_t *cur, **prev;
+	lock_get(&list->lock);
+	cur = list->nodes;
+	prev = &list->nodes;
+	while(cur) {
+		if(cmp_dmq_node(cur, node)) {
+			*prev = cur->next;
+			shm_free_node(cur);
+			lock_release(&list->lock);
+			return 1;
+		}
+		prev = &cur->next;
+		cur = cur->next;
+	}
+	lock_release(&list->lock);
+	return 0;
+}
+
+inline dmq_node_t* add_dmq_node(dmq_node_list_t* list, str* uri) {
+	dmq_node_t* newnode = build_dmq_node(uri, 1);
+	if(!newnode) {
+		LM_ERR("error creating node\n");
+		goto error;
+	}
+	LM_DBG("dmq node successfully created\n");
+	lock_get(&list->lock);
+	newnode->next = list->nodes;
+	list->nodes = newnode;
+	list->count++;
+	lock_release(&list->lock);
+	return newnode;
+error:
+	return NULL;
+}
+
+int build_node_str(dmq_node_t* node, char* buf, int buflen) {
+	/* sip:host:port;status=[status] */
+	int len = 0;
+	if(buflen < node->orig_uri.len + 32) {
+		LM_ERR("no more space left for node string\n");
+		return -1;
+	}
+	memcpy(buf + len, "sip:", 4);
+	len += 4;
+	memcpy(buf + len, node->uri.host.s, node->uri.host.len);
+	len += node->uri.host.len;
+	memcpy(buf + len, ":", 1);
+	len += 1;
+	memcpy(buf + len, node->uri.port.s, node->uri.port.len);
+	len += node->uri.port.len;
+	memcpy(buf + len, ";", 1);
+	len += 1;
+	memcpy(buf + len, "status=", 7);
+	len += 7;
+	memcpy(buf + len, get_status_str(node->status)->s, get_status_str(node->status)->len);
+	len += get_status_str(node->status)->len;
+	return len;
+}

+ 57 - 0
modules_k/dmq/dmqnode.h

@@ -0,0 +1,57 @@
+#ifndef DMQNODE_H
+#define DMQNODE_H
+
+#include <string.h>
+#include <stdlib.h>
+#include "../../lock_ops.h"
+#include "../../str.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_param.h"
+
+#define NBODY_LEN		1024
+#define DMQ_NODE_ACTIVE		1 << 1
+#define DMQ_NODE_TIMEOUT	1 << 2
+#define DMQ_NODE_DISABLED	1 << 3
+
+typedef struct dmq_node {
+	int local; /* local type set means the dmq dmqnode == self */
+	str orig_uri; /* original uri string - e.g. sip:127.0.0.1:5060;passive=true */
+	struct sip_uri uri; /* parsed uri string */
+	param_t* params; /* uri parameters */
+	int status; /* reserved - maybe something like active,timeout,disabled */
+	int last_notification; /* last notificatino receied from the node */
+	struct dmq_node* next; /* pointer to the next struct dmq_node */
+} dmq_node_t;
+
+typedef struct dmq_node_list {
+	gen_lock_t lock; /* lock for the list - must acquire before manipulating it */
+	struct dmq_node* nodes; /* the nodes in the list */
+	int count; /* the number of nodes in the list */
+} dmq_node_list_t;
+
+extern str dmq_node_status_str;
+extern dmq_node_list_t* node_list;
+
+dmq_node_list_t* init_dmq_node_list();
+dmq_node_t* build_dmq_node(str* uri, int shm);
+int update_node_list(dmq_node_list_t* remote_list);
+dmq_node_t* add_dmq_node(dmq_node_list_t* list, str* uri);
+dmq_node_t* find_dmq_node(dmq_node_list_t* list, dmq_node_t* node);
+dmq_node_t* find_dmq_node_uri(dmq_node_list_t* list, str* uri);
+int del_dmq_node(dmq_node_list_t* list, dmq_node_t* node);
+int cmp_dmq_node(dmq_node_t* node, dmq_node_t* cmpnode);
+dmq_node_t* shm_dup_node(dmq_node_t* node);
+void destroy_dmq_node(dmq_node_t* node, int shm);
+void shm_free_node(dmq_node_t* node);
+void pkg_free_node(dmq_node_t* node);
+int set_dmq_node_params(dmq_node_t* node, param_t* params);
+
+str* get_status_str(int status);
+int build_node_str(dmq_node_t* node, char* buf, int buflen);
+
+extern dmq_node_t* self_node;
+extern dmq_node_t* notification_node;	
+
+#endif

+ 4 - 0
modules_k/dmq/doc/Makefile

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

+ 44 - 0
modules_k/dmq/doc/dmq.xml

@@ -0,0 +1,44 @@
+<?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>Distributed Message Queue Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Marius Ovidiu</firstname>
+		<surname>Bucur</surname>
+		<address>
+		<email>[email protected]</email>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Marius Ovidiu</firstname>
+		<surname>Bucur</surname>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2011</year>
+	    <holder>Marius Bucur</holder>
+	</copyright>
+  </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="dmq_admin.xml"/>
+    <xi:include href="dmq_devel.xml"/>
+    
+</book>
+
+

+ 103 - 0
modules_k/dmq/doc/dmq_admin.xml

@@ -0,0 +1,103 @@
+<?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> The DMQ module implements a distributed message passing system on top of Kamailio.
+	The DMQ nodes within the system are grouped in a logical entity called DMQ bus and are able to
+	communicate with each others by sending/receiving messages (either by broadcast or sending a DMQ
+	message to a specific node).
+	The system transparently deals with node discovery, node consistency within the DMQ bus, retransmissions,
+	etc.
+	</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>sl</emphasis>.
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+				<emphasis>tm</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+
+	<section>
+		<title>External Libraries or Applications</title>
+		<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>
+				Each peer needs to use its own serialization mechanism.
+				Some examples are libtpl, protobuf.
+				</emphasis>.
+			</para>
+			</listitem>
+		</itemizedlist>
+	</section>
+	
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>dmq_server_address</varname>(str)</title>
+		<para>
+		The local server address.
+		</para>
+		<para>
+		The modules needs it to know on which interface the DMQ engine should send and receive messages.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>NULL</quote>.	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>dmq_server_address</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dmq", "dmq_server_address", "&defaultdb;")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>dmq_notification_address</varname>(str)</title>
+		<para>
+		The address of the DMQ node from which the local node should retrieve initial information.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>dmq_notification_address</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dmq", "dmq_notification_address", "&defaultdb;")
+...
+</programlisting>
+		</example>
+	</section>
+</chapter>
+

+ 45 - 0
modules_k/dmq/doc/dmq_devel.xml

@@ -0,0 +1,45 @@
+<?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 Developer's Guide -->
+
+<chapter>
+    <title>&develguide;</title>
+    <para>
+		The module provides the following functions that can be used
+		in other &kamailio; modules.
+   </para>
+ 		<section>
+				<title>
+				<function moreinfo="none">dmq_load_api(dmq_api_t* api)</function>
+				</title>
+			<para>
+				This function binds the dmq modules and fills the structure 
+				with the exported functions
+				-> register_dmq_peer - registers an entity as a DMQ peer which permits receiving/sending
+				messages between nodes which support the same peer,
+				-> bcast_message - broadcast a DMQ message to all peers available in the DMQ bus,
+				-> send_message - sends a DMQ message to a specific peer in the local DMQ bus.
+			</para>
+		<example>
+		<title><function>dmq_api_t</function> structure</title>
+	<programlisting format="linespecific">
+...
+typedef struct dmq_api {
+	register_dmq_peer_t register_dmq_peer;
+	bcast_message_t bcast_message;
+	send_message_t send_message;
+} dmq_api_t;
+...
+</programlisting>
+		</example>
+
+		</section>
+</chapter>
+

+ 50 - 0
modules_k/dmq/message.c

@@ -0,0 +1,50 @@
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_uri.h"
+#include "../../sip_msg_clone.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parse_from.h"
+#include "../../ut.h"
+#include "dmq.h"
+#include "worker.h"
+#include "peer.h"
+#include "message.h"
+
+str dmq_200_rpl  = str_init("OK");
+str dmq_400_rpl  = str_init("Bad request");
+str dmq_500_rpl  = str_init("Server Internal Error");
+str dmq_404_rpl  = str_init("User Not Found");
+
+int handle_dmq_message(struct sip_msg* msg, char* str1, char* str2) {
+	dmq_peer_t* peer;
+	struct sip_msg* cloned_msg = NULL;
+	int cloned_msg_len;
+	if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s)) {
+			LM_ERR("error parsing msg uri\n");
+			goto error;
+	}
+	LM_DBG("handle_dmq_message [%.*s %.*s] [%s %s]\n",
+	       msg->first_line.u.request.method.len, msg->first_line.u.request.method.s,
+	       msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s,
+	       ZSW(str1), ZSW(str2));
+	/* the peer id is given as the userinfo part of the request URI */
+	peer = find_peer(msg->parsed_uri.user);
+	if(!peer) {
+		LM_DBG("no peer found for %.*s\n", msg->parsed_uri.user.len, msg->parsed_uri.user.s);
+		if(slb.freply(msg, 404, &dmq_404_rpl) < 0)
+		{
+			LM_ERR("sending reply\n");
+			goto error;
+		}
+		return 0;
+	}
+	LM_DBG("handle_dmq_message peer found: %.*s\n", msg->parsed_uri.user.len, msg->parsed_uri.user.s);
+	cloned_msg = sip_msg_shm_clone(msg, &cloned_msg_len, 1);
+	if(!cloned_msg) {
+		LM_ERR("error cloning sip message\n");
+		goto error;
+	}
+	add_dmq_job(cloned_msg, peer);
+	return 0;
+error:
+	return -1;
+}

+ 2 - 0
modules_k/dmq/message.h

@@ -0,0 +1,2 @@
+
+int handle_dmq_message(struct sip_msg*, char*, char*);

+ 220 - 0
modules_k/dmq/notification_peer.c

@@ -0,0 +1,220 @@
+#include "notification_peer.h"
+
+static str notification_content_type = str_init("text/plain");
+dmq_resp_cback_t notification_callback = {&notification_resp_callback_f, 0};
+
+int add_notification_peer() {
+	dmq_peer_t not_peer;
+	not_peer.callback = dmq_notification_callback;
+	not_peer.description.s = "notification_peer";
+	not_peer.description.len = 17;
+	not_peer.peer_id.s = "notification_peer";
+	not_peer.peer_id.len = 17;
+	dmq_notification_peer = register_dmq_peer(&not_peer);
+	if(!dmq_notification_peer) {
+		LM_ERR("error in register_dmq_peer\n");
+		goto error;
+	}
+	/* add itself to the node list */
+	self_node = add_dmq_node(node_list, &dmq_server_address);
+	if(!self_node) {
+		LM_ERR("error adding self node\n");
+		goto error;
+	}
+	/* local node - only for self */
+	self_node->local = 1;
+	return 0;
+error:
+	return -1;
+}
+
+dmq_node_t* add_server_and_notify(str* server_address) {
+	/* add the notification server to the node list - if any */
+	dmq_node_t* node = add_dmq_node(node_list, server_address);
+	if(!node) {
+		LM_ERR("error adding notification node\n");
+		goto error;
+	}
+	/* request initial list from the notification server */
+	if(request_nodelist(node, 2) < 0) {
+		LM_ERR("error requesting initial nodelist\n");
+		goto error;
+	}
+	return node;
+error:
+	return NULL;
+}
+
+/**
+ * extract the node list from the body of a notification request SIP message
+ * the SIP request will look something like:
+ * 	KDMQ sip:10.0.0.0:5062
+ * 	To: ...
+ * 	From: ...
+ * 	Max-Forwards: ...
+ * 	Content-Length: 22
+ * 	
+ * 	sip:host1:port1;param1=value1
+ * 	sip:host2:port2;param2=value2
+ * 	...
+ */
+int extract_node_list(dmq_node_list_t* update_list, struct sip_msg* msg) {
+	int content_length, total_nodes = 0;
+	str body;
+	str tmp_uri;
+	dmq_node_t *cur = NULL;
+	char *tmp, *end, *match;
+	if(!msg->content_length) {
+		LM_ERR("no content length header found\n");
+		return -1;
+	}
+	content_length = get_content_length(msg);
+	if(!content_length) {
+		LM_DBG("content length is 0\n");
+		return total_nodes;
+	}
+	body.s = get_body(msg);
+	body.len = content_length;
+	tmp = body.s;
+	end = body.s + body.len;
+	
+	/* acquire big list lock */
+	lock_get(&update_list->lock);
+	while(tmp < end) {
+		match = q_memchr(tmp, '\n', end - tmp);
+		if(match) {
+			match++;
+		} else {
+			/* for the last line - take all of it */
+			match = end;
+		}
+		/* create the orig_uri from the parsed uri line and trim it */
+		tmp_uri.s = tmp;
+		tmp_uri.len = match - tmp - 1;
+		tmp = match;
+		/* trim the \r, \n and \0's */
+		trim_r(tmp_uri);
+		if(!find_dmq_node_uri(update_list, &tmp_uri)) {
+			LM_DBG("found new node %.*s\n", STR_FMT(&tmp_uri));
+			cur = build_dmq_node(&tmp_uri, 1);
+			if(!cur) {
+				LM_ERR("error creating new dmq node\n");
+				goto error;
+			}
+			cur->next = update_list->nodes;
+			update_list->nodes = cur;
+			update_list->count++;
+			total_nodes++;
+		}
+	}
+	/* release big list lock */
+	lock_release(&update_list->lock);
+	return total_nodes;
+error:
+	lock_release(&update_list->lock);
+	return -1;
+}
+
+int dmq_notification_callback(struct sip_msg* msg, peer_reponse_t* resp) {
+	int nodes_recv;
+	str* response_body = NULL;
+	unsigned int maxforwards = 1;
+	/* received dmqnode list */
+	LM_DBG("dmq triggered from dmq_notification_callback\n");
+	/* parse the message headers */
+	if(parse_headers(msg, HDR_EOH_F, 0) < 0) {
+		LM_ERR("error parsing message headers\n");
+		goto error;
+	}
+	
+	/* extract the maxforwards value, if any */
+	if(msg->maxforwards) {
+		LM_DBG("max forwards: %.*s\n", STR_FMT(&msg->maxforwards->body));
+		str2int(&msg->maxforwards->body, &maxforwards);
+	}
+	maxforwards--;
+	
+	nodes_recv = extract_node_list(node_list, msg);
+	LM_DBG("received %d new nodes\n", nodes_recv);
+	response_body = build_notification_body();
+	resp->content_type = notification_content_type;
+	resp->reason = dmq_200_rpl;
+	resp->body = *response_body;
+	resp->resp_code = 200;
+	
+	/* if we received any new nodes tell about them to the others */
+	if(nodes_recv > 0 && maxforwards > 0) {
+		/* maxforwards is set to 0 so that the message is will not be in a spiral */
+		bcast_dmq_message(dmq_notification_peer, response_body, 0, &notification_callback, maxforwards);
+	}
+	LM_DBG("broadcasted message\n");
+	pkg_free(response_body);
+	return 0;
+error:
+	return -1;
+}
+
+/**
+ * builds the body of a notification message from the list of servers 
+ * the result will look something like:
+ * sip:host1:port1;param1=value1\r\n
+ * sip:host2:port2;param2=value2\r\n
+ * sip:host3:port3;param3=value3
+ */
+str* build_notification_body() {
+	/* the length of the current line describing the server */
+	int slen;
+	/* the current length of the body */
+	int clen = 0;
+	dmq_node_t* cur_node = NULL;
+	str* body;
+	body = pkg_malloc(sizeof(str));
+	memset(body, 0, sizeof(str));
+	/* we allocate a chunk of data for the body */
+	body->len = NBODY_LEN;
+	body->s = pkg_malloc(body->len);
+	/* we add each server to the body - each on a different line */
+	lock_get(&node_list->lock);
+	cur_node = node_list->nodes;
+	while(cur_node) {
+		LM_DBG("body_len = %d - clen = %d\n", body->len, clen);
+		/* body->len - clen - 2 bytes left to write - including the \r\n */
+		slen = build_node_str(cur_node, body->s + clen, body->len - clen - 2);
+		if(slen < 0) {
+			LM_ERR("cannot build_node_string\n");
+			goto error;
+		}
+		clen += slen;
+		body->s[clen++] = '\r';
+		body->s[clen++] = '\n';
+		cur_node = cur_node->next;
+	}
+	lock_release(&node_list->lock);
+	body->len = clen;
+	return body;
+error:
+	pkg_free(body->s);
+	pkg_free(body);
+	return NULL;
+}
+
+int request_nodelist(dmq_node_t* node, int forward) {
+	str* body = build_notification_body();
+	int ret;
+	ret = send_dmq_message(dmq_notification_peer, body, node, &notification_callback, forward);
+	pkg_free(body->s);
+	pkg_free(body);
+	return ret;
+}
+
+int notification_resp_callback_f(struct sip_msg* msg, int code, dmq_node_t* node, void* param) {
+	int ret;
+	LM_DBG("notification_callback_f triggered [%p %d %p]\n", msg, code, param);
+	if(code == 408) {
+		/* deleting node - the server did not respond */
+		LM_ERR("deleting server %.*s because of failed request\n", STR_FMT(&node->orig_uri));
+		ret = del_dmq_node(node_list, node);
+		LM_DBG("del_dmq_node returned %d\n", ret);
+	}
+	return 0;
+}

+ 26 - 0
modules_k/dmq/notification_peer.h

@@ -0,0 +1,26 @@
+#include "../../parser/msg_parser.h"
+#include "../../parser/parse_content.h"
+#include "../../ut.h"
+#include "dmq.h"
+#include "dmqnode.h"
+#include "peer.h"
+#include "dmq_funcs.h"
+
+int add_notification_peer();
+int dmq_notification_callback(struct sip_msg* msg, peer_reponse_t* resp);
+int extract_node_list(dmq_node_list_t* update_list, struct sip_msg* msg);
+str* build_notification_body();
+int build_node_str(dmq_node_t* node, char* buf, int buflen);
+/* request a nodelist from a server
+ * this is acomplished by a KDMQ request
+ * KDMQ notification@server:port
+ * node - the node to send to
+ * forward - flag that tells if the node receiving the message is allowed to 
+ *           forward the request to its own list
+ */
+int request_nodelist(dmq_node_t* node, int forward);
+dmq_node_t* add_server_and_notify(str* server_address);
+
+/* helper functions */
+extern int notification_resp_callback_f(struct sip_msg* msg, int code, dmq_node_t* node, void* param);
+extern dmq_resp_cback_t notification_callback;

+ 44 - 0
modules_k/dmq/peer.c

@@ -0,0 +1,44 @@
+#include "peer.h"
+#include "dmq.h"
+
+dmq_peer_list_t* init_peer_list() {
+	dmq_peer_list_t* peer_list = shm_malloc(sizeof(dmq_peer_list_t));
+	memset(peer_list, 0, sizeof(dmq_peer_list_t));
+	lock_init(&peer_list->lock);
+	return peer_list;
+}
+
+dmq_peer_t* search_peer_list(dmq_peer_list_t* peer_list, dmq_peer_t* peer) {
+	dmq_peer_t* cur = peer_list->peers;
+	int len;
+	while(cur) {
+		/* len - the minimum length of the two strings */
+		len = cur->peer_id.len < peer->peer_id.len ? cur->peer_id.len:peer->peer_id.len;
+		if(strncasecmp(cur->peer_id.s, peer->peer_id.s, len) == 0) {
+			return cur;
+		}
+		cur = cur->next;
+	}
+	return 0;
+}
+
+dmq_peer_t* add_peer(dmq_peer_list_t* peer_list, dmq_peer_t* peer) {
+	dmq_peer_t* new_peer = shm_malloc(sizeof(dmq_peer_t));
+	*new_peer = *peer;
+	
+	/* copy the str's */
+	new_peer->peer_id.s = shm_malloc(peer->peer_id.len);
+	memcpy(new_peer->peer_id.s, peer->peer_id.s, peer->peer_id.len);
+	new_peer->description.s = shm_malloc(peer->description.len);
+	memcpy(new_peer->peer_id.s, peer->peer_id.s, peer->peer_id.len);
+	
+	new_peer->next = peer_list->peers;
+	peer_list->peers = new_peer;
+	return new_peer;
+}
+
+dmq_peer_t* find_peer(str peer_id) {
+	dmq_peer_t foo_peer;
+	foo_peer.peer_id = peer_id;
+	return search_peer_list(peer_list, &foo_peer);
+}

+ 44 - 0
modules_k/dmq/peer.h

@@ -0,0 +1,44 @@
+#ifndef PEER_H
+#define PEER_H
+
+#include <string.h>
+#include <stdlib.h>
+#include "../../lock_ops.h"
+#include "../../str.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../parser/msg_parser.h"
+
+typedef struct peer_response {
+	int resp_code;
+	str content_type;
+	str reason;
+	str body;
+} peer_reponse_t;
+
+typedef int(*peer_callback_t)(struct sip_msg*, peer_reponse_t* resp);
+
+typedef struct dmq_peer {
+	str peer_id;
+	str description;
+	peer_callback_t callback;
+	struct dmq_peer* next;
+} dmq_peer_t;
+
+typedef struct dmq_peer_list {
+	gen_lock_t lock;
+	dmq_peer_t* peers;
+	int count;
+} dmq_peer_list_t;
+
+extern dmq_peer_list_t* peer_list;
+
+dmq_peer_list_t* init_peer_list();
+dmq_peer_t* search_peer_list(dmq_peer_list_t* peer_list, dmq_peer_t* peer);
+typedef dmq_peer_t* (*register_dmq_peer_t)(dmq_peer_t*);
+
+dmq_peer_t* add_peer(dmq_peer_list_t* peer_list, dmq_peer_t* peer);
+dmq_peer_t* find_peer(str peer_id);
+
+
+#endif

+ 185 - 0
modules_k/dmq/worker.c

@@ -0,0 +1,185 @@
+#include "dmq.h"
+#include "peer.h"
+#include "worker.h"
+#include "../../data_lump_rpl.h"
+#include "../../mod_fix.h"
+
+/* set the body of a response */
+static int set_reply_body(struct sip_msg* msg, str* body, str* content_type)
+{
+	char* buf;
+	int len;
+	int value_len;
+	str nb = *body;
+	str nc = *content_type;
+
+	/* add content-type */
+	value_len = nc.len;
+	len=sizeof("Content-Type: ") - 1 + value_len + CRLF_LEN;
+	buf=pkg_malloc(sizeof(char)*(len));
+
+	if (buf==0) {
+		LM_ERR("out of pkg memory\n");
+		return -1;
+	}
+	memcpy(buf, "Content-Type: ", sizeof("Content-Type: ") - 1);
+	memcpy(buf+sizeof("Content-Type: ") - 1, nc.s, value_len);
+	memcpy(buf+sizeof("Content-Type: ") - 1 + value_len, CRLF, CRLF_LEN);
+	if (add_lump_rpl(msg, buf, len, LUMP_RPL_HDR) == 0) {
+		LM_ERR("failed to insert content-type lump\n");
+		pkg_free(buf);
+		return -1;
+	}
+	pkg_free(buf);
+
+	/* add body */
+	if (add_lump_rpl(msg, nb.s, nb.len, LUMP_RPL_BODY) == 0) {
+		LM_ERR("cannot add body lump\n");
+		return -1;
+	}
+		
+	return 1;
+}
+
+void worker_loop(int id) {
+	dmq_worker_t* worker = &workers[id];
+	dmq_job_t* current_job;
+	peer_reponse_t peer_response;
+	int ret_value;
+	for(;;) {
+		LM_DBG("dmq_worker [%d %d] getting lock\n", id, my_pid());
+		lock_get(&worker->lock);
+		LM_DBG("dmq_worker [%d %d] lock acquired\n", id, my_pid());
+		/* multiple lock_release calls might be performed, so remove from queue until empty */
+		do {
+			/* fill the response with 0's */
+			memset(&peer_response, 0, sizeof(peer_response));
+			current_job = job_queue_pop(worker->queue);
+			/* job_queue_pop might return NULL if queue is empty */
+			if(current_job) {
+				ret_value = current_job->f(current_job->msg, &peer_response);
+				if(ret_value < 0) {
+					LM_ERR("running job failed\n");
+					continue;
+				}
+				/* add the body to the reply */
+				if(peer_response.body.s) {
+					if(set_reply_body(current_job->msg, &peer_response.body, &peer_response.content_type) < 0) {
+						LM_ERR("error adding lumps\n");
+						continue;
+					}
+				}
+				/* send the reply */
+				if(slb.freply(current_job->msg, peer_response.resp_code, &peer_response.reason) < 0)
+				{
+					LM_ERR("error sending reply\n");
+				}
+				
+				/* if body given, free the lumps and free the body */
+				if(peer_response.body.s) {
+					del_nonshm_lump_rpl(&current_job->msg->reply_lump);
+					pkg_free(peer_response.body.s);
+				}
+				LM_DBG("sent reply\n");
+				shm_free(current_job->msg);
+				shm_free(current_job);
+				worker->jobs_processed++;
+			}
+		} while(job_queue_size(worker->queue) > 0);
+	}
+}
+
+int add_dmq_job(struct sip_msg* msg, dmq_peer_t* peer) {
+	int i, found_available = 0;
+	dmq_job_t new_job;
+	dmq_worker_t* worker;
+	new_job.f = peer->callback;
+	new_job.msg = msg;
+	new_job.orig_peer = peer;
+	if(!num_workers) {
+		LM_ERR("error in add_dmq_job: no workers spawned\n");
+		return -1;
+	}
+	/* initialize the worker with the first one */
+	worker = workers;
+	/* search for an available worker, or, if not possible, for the least busy one */
+	for(i = 0; i < num_workers; i++) {
+		if(job_queue_size(workers[i].queue) == 0) {
+			worker = &workers[i];
+			found_available = 1;
+			break;
+		} else if(job_queue_size(workers[i].queue) < job_queue_size(worker->queue)) {
+			worker = &workers[i];
+		}
+	}
+	if(!found_available) {
+		LM_DBG("no available worker found, passing job to the least busy one [%d %d]\n",
+		       worker->pid, job_queue_size(worker->queue));
+	}
+	job_queue_push(worker->queue, &new_job);
+	lock_release(&worker->lock);
+	return 0;
+}
+
+void init_worker(dmq_worker_t* worker) {
+	memset(worker, 0, sizeof(*worker));
+	lock_init(&worker->lock);
+	// acquire the lock for the first time - so that dmq_worker_loop blocks
+	lock_get(&worker->lock);
+	worker->queue = alloc_job_queue();
+}
+
+job_queue_t* alloc_job_queue() {
+	job_queue_t* queue = shm_malloc(sizeof(job_queue_t));
+	atomic_set(&queue->count, 0);
+	queue->front = NULL;
+	queue->back = NULL;
+	lock_init(&queue->lock);
+	return queue;
+}
+
+void destroy_job_queue(job_queue_t* queue) {
+	shm_free(queue);
+}
+
+int job_queue_size(job_queue_t* queue) {
+	return atomic_get(&queue->count);
+}
+
+void job_queue_push(job_queue_t* queue, dmq_job_t* job) {
+	/* we need to copy the dmq_job into a newly created dmq_job in shm */
+	dmq_job_t* newjob = shm_malloc(sizeof(dmq_job_t));
+	*newjob = *job;
+	
+	lock_get(&queue->lock);
+	newjob->prev = NULL;
+	newjob->next = queue->back;
+	if(queue->back) {
+		queue->back->prev = newjob;
+	}
+	queue->back = newjob;
+	if(!queue->front) {
+		queue->front = newjob;
+	}
+	atomic_inc(&queue->count);
+	lock_release(&queue->lock);
+}
+dmq_job_t* job_queue_pop(job_queue_t* queue) {
+	dmq_job_t* front;
+	lock_get(&queue->lock);
+	if(!queue->front) {
+		lock_release(&queue->lock);
+		return NULL;
+	}
+	front = queue->front;
+	if(front->prev) {
+		queue->front = front->prev;
+		front->prev->next = NULL;
+	} else {
+		queue->front = NULL;
+		queue->back = NULL;
+	}
+	atomic_dec(&queue->count);
+	lock_release(&queue->lock);
+	return front;
+}

+ 43 - 0
modules_k/dmq/worker.h

@@ -0,0 +1,43 @@
+#ifndef DMQ_WORKER_H
+#define DMQ_WORKER_H
+
+#include "peer.h"
+#include "../../locking.h"
+#include "../../atomic_ops.h"
+#include "../../parser/msg_parser.h"
+
+typedef struct dmq_job {
+	peer_callback_t f;
+	struct sip_msg* msg;
+	dmq_peer_t* orig_peer;
+	struct dmq_job* next;
+	struct dmq_job* prev;
+} dmq_job_t;
+
+typedef struct job_queue {
+	atomic_t count;
+	struct dmq_job* back;
+	struct dmq_job* front;
+	gen_lock_t lock;
+} job_queue_t;
+
+struct dmq_worker {
+	job_queue_t* queue;
+	int jobs_processed;
+	gen_lock_t lock;
+	int pid;
+};
+
+typedef struct dmq_worker dmq_worker_t;
+
+void init_worker(dmq_worker_t* worker);
+int add_dmq_job(struct sip_msg*, dmq_peer_t*);
+void worker_loop(int id);
+
+job_queue_t* alloc_job_queue();
+void destroy_job_queue(job_queue_t* queue);
+void job_queue_push(job_queue_t* queue, dmq_job_t* job);
+dmq_job_t* job_queue_pop(job_queue_t* queue);
+int job_queue_size(job_queue_t* queue);
+
+#endif

+ 72 - 40
modules_k/htable/README

@@ -11,7 +11,7 @@ Elena-Ramona Modroiu
 
 
    <[email protected]>
    <[email protected]>
 
 
-   Copyright © 2008-2011 http://www.asipto.com
+   Copyright © 2008-2011 http://www.asipto.com
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -33,10 +33,12 @@ Elena-Ramona Modroiu
               3.4. key_type_column (str)
               3.4. key_type_column (str)
               3.5. value_type_column (str)
               3.5. value_type_column (str)
               3.6. key_value_column (str)
               3.6. key_value_column (str)
-              3.7. array_size_suffix (str)
-              3.8. fetch_rows (integer)
-              3.9. timer_interval (integer)
-              3.10. timer_mode (integer)
+              3.7. expires_column (str)
+              3.8. array_size_suffix (str)
+              3.9. fetch_rows (integer)
+              3.10. timer_interval (integer)
+              3.11. timer_mode (integer)
+              3.12. db_expires (integer)
 
 
         4. Exported Functions
         4. Exported Functions
 
 
@@ -64,13 +66,15 @@ Elena-Ramona Modroiu
    1.6. Set key_type_column parameter
    1.6. Set key_type_column parameter
    1.7. Set value_type_column parameter
    1.7. Set value_type_column parameter
    1.8. Set key_value_column parameter
    1.8. Set key_value_column parameter
-   1.9. Set array_size_suffix parameter
-   1.10. Set fetch_rows parameter
-   1.11. Set timer_interval parameter
-   1.12. Set timer_mode parameter
-   1.13. sht_print usage
-   1.14. sht_rm_name_re usage
-   1.15. sht_rm_value_re usage
+   1.9. Set expires_column parameter
+   1.10. Set array_size_suffix parameter
+   1.11. Set fetch_rows parameter
+   1.12. Set timer_interval parameter
+   1.13. Set timer_mode parameter
+   1.14. Set db_expires parameter
+   1.15. sht_print usage
+   1.16. sht_rm_name_re usage
+   1.17. sht_rm_value_re usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -91,10 +95,12 @@ Chapter 1. Admin Guide
         3.4. key_type_column (str)
         3.4. key_type_column (str)
         3.5. value_type_column (str)
         3.5. value_type_column (str)
         3.6. key_value_column (str)
         3.6. key_value_column (str)
-        3.7. array_size_suffix (str)
-        3.8. fetch_rows (integer)
-        3.9. timer_interval (integer)
-        3.10. timer_mode (integer)
+        3.7. expires_column (str)
+        3.8. array_size_suffix (str)
+        3.9. fetch_rows (integer)
+        3.10. timer_interval (integer)
+        3.11. timer_mode (integer)
+        3.12. db_expires (integer)
 
 
    4. Exported Functions
    4. Exported Functions
 
 
@@ -130,7 +136,7 @@ Chapter 1. Admin Guide
    You can read more about hash tables at:
    You can read more about hash tables at:
    http://en.wikipedia.org/wiki/Hash_table.
    http://en.wikipedia.org/wiki/Hash_table.
 
 
-   The “name� can be a static string or can include pseudo- variables that
+   The "name" can be a static string or can include pseudo- variables that
    will be replaced at runtime.
    will be replaced at runtime.
 
 
    Example 1.1. Accessing $sht(htname=>key)
    Example 1.1. Accessing $sht(htname=>key)
@@ -153,7 +159,7 @@ $sht(a=>$ci::srcip) = $si;
    the failed authentications per user and one for storing the time of
    the failed authentications per user and one for storing the time of
    last authentication attempt. To ensure unique name per user, the hash
    last authentication attempt. To ensure unique name per user, the hash
    table uses a combination of authentication username and text
    table uses a combination of authentication username and text
-   “::auth_count� and “::last_auth�.
+   "::auth_count" and "::last_auth".
 
 
    Example 1.2. Dictionary attack limitation
    Example 1.2. Dictionary attack limitation
 ...
 ...
@@ -239,10 +245,12 @@ if(is_present_hf("Authorization"))
    3.4. key_type_column (str)
    3.4. key_type_column (str)
    3.5. value_type_column (str)
    3.5. value_type_column (str)
    3.6. key_value_column (str)
    3.6. key_value_column (str)
-   3.7. array_size_suffix (str)
-   3.8. fetch_rows (integer)
-   3.9. timer_interval (integer)
-   3.10. timer_mode (integer)
+   3.7. expires_column (str)
+   3.8. array_size_suffix (str)
+   3.9. fetch_rows (integer)
+   3.10. timer_interval (integer)
+   3.11. timer_mode (integer)
+   3.12. db_expires (integer)
 
 
 3.1. htable (str)
 3.1. htable (str)
 
 
@@ -268,7 +276,7 @@ if(is_present_hf("Authorization"))
        database table when the SIP server is stopped (i.e., ensure
        database table when the SIP server is stopped (i.e., ensure
        persistency over restarts). Default value is 0 (no write back to db
        persistency over restarts). Default value is 0 (no write back to db
        table).
        table).
-     * initval - the integer value to be returned insted of $null when a
+     * initval - the integer value to be returned instead of $null when a
        requested key is not set.
        requested key is not set.
 
 
    Default value is NULL.
    Default value is NULL.
@@ -336,58 +344,82 @@ modparam("htable", "value_type_column", "vtype")
 modparam("htable", "key_value_column", "kvalue")
 modparam("htable", "key_value_column", "kvalue")
 ...
 ...
 
 
-3.7. array_size_suffix (str)
+3.7. expires_column (str)
+
+   The name of the column containing expires type.
+
+   Default value is 'expires'.
+
+   Example 1.9. Set expires_column parameter
+...
+modparam("htable", "expires", "expiry")
+...
+
+3.8. array_size_suffix (str)
 
 
    The suffix to be added to store the number of items in an array.
    The suffix to be added to store the number of items in an array.
 
 
    Default value is '::size'.
    Default value is '::size'.
 
 
-   Example 1.9. Set array_size_suffix parameter
+   Example 1.10. Set array_size_suffix parameter
 ...
 ...
 modparam("htable", "array_size_suffix", "-count")
 modparam("htable", "array_size_suffix", "-count")
 ...
 ...
 
 
-3.8. fetch_rows (integer)
+3.9. fetch_rows (integer)
 
 
    How many rows to fetch at once from database.
    How many rows to fetch at once from database.
 
 
    Default value is 100.
    Default value is 100.
 
 
-   Example 1.10. Set fetch_rows parameter
+   Example 1.11. Set fetch_rows parameter
 ...
 ...
 modparam("htable", "fetch_rows", 1000)
 modparam("htable", "fetch_rows", 1000)
 ...
 ...
 
 
-3.9. timer_interval (integer)
+3.10. timer_interval (integer)
 
 
    Interval in seconds to check for expired htable values.
    Interval in seconds to check for expired htable values.
 
 
    Default value is 20.
    Default value is 20.
 
 
-   Example 1.11. Set timer_interval parameter
+   Example 1.12. Set timer_interval parameter
 ...
 ...
 modparam("htable", "timer_interval", 10)
 modparam("htable", "timer_interval", 10)
 ...
 ...
 
 
-3.10. timer_mode (integer)
+3.11. timer_mode (integer)
 
 
    If set to 1, will start a new timer process. If set to 0 will use
    If set to 1, will start a new timer process. If set to 0 will use
    default timer process to check for expired htable values.
    default timer process to check for expired htable values.
 
 
    Default value is 0.
    Default value is 0.
 
 
-   Example 1.12. Set timer_mode parameter
+   Example 1.13. Set timer_mode parameter
 ...
 ...
 modparam("htable", "timer_mode", 1)
 modparam("htable", "timer_mode", 1)
 ...
 ...
 
 
+3.12. db_expires (integer)
+
+   If set to 1, will load/save the expires values of the items in hash
+   table fromm/to database. It applies only to hash tables that have
+   auto-expires attribute defined.
+
+   Default value is 0.
+
+   Example 1.14. Set db_expires parameter
+...
+modparam("htable", "db_expires", 1)
+...
+
 4. Exported Functions
 4. Exported Functions
 
 
    4.1. sht_print()
    4.1. sht_print()
    4.2. sht_rm_name_re(htable=>regexp)
    4.2. sht_rm_name_re(htable=>regexp)
    4.3. sht_rm_value_re(htable=>regexp)
    4.3. sht_rm_value_re(htable=>regexp)
 
 
-4.1.  sht_print()
+4.1. sht_print()
 
 
    Dump content of hash table to L_ERR log level. Intended for debug
    Dump content of hash table to L_ERR log level. Intended for debug
    purposes.
    purposes.
@@ -395,12 +427,12 @@ modparam("htable", "timer_mode", 1)
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
 
-   Example 1.13. sht_print usage
+   Example 1.15. sht_print usage
 ...
 ...
 sht_print();
 sht_print();
 ...
 ...
 
 
-4.2.  sht_rm_name_re(htable=>regexp)
+4.2. sht_rm_name_re(htable=>regexp)
 
 
    Delete all entries in the htable that match the name against regular
    Delete all entries in the htable that match the name against regular
    expression.
    expression.
@@ -408,12 +440,12 @@ sht_print();
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
 
-   Example 1.14. sht_rm_name_re usage
+   Example 1.16. sht_rm_name_re usage
 ...
 ...
 sht_rm_name_re("ha=>.*");
 sht_rm_name_re("ha=>.*");
 ...
 ...
 
 
-4.3.  sht_rm_value_re(htable=>regexp)
+4.3. sht_rm_value_re(htable=>regexp)
 
 
    Delete all entries in the htable that match the value against regular
    Delete all entries in the htable that match the value against regular
    expression.
    expression.
@@ -421,7 +453,7 @@ sht_rm_name_re("ha=>.*");
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
 
-   Example 1.15. sht_rm_value_re usage
+   Example 1.17. sht_rm_value_re usage
 ...
 ...
 sht_rm_value_re("ha=>.*");
 sht_rm_value_re("ha=>.*");
 ...
 ...
@@ -443,7 +475,7 @@ sht_rm_value_re("ha=>.*");
    6.1. sht_reload
    6.1. sht_reload
    6.2. sht_dump
    6.2. sht_dump
 
 
-6.1.  sht_reload
+6.1. sht_reload
 
 
    Reload a hash table from database.
    Reload a hash table from database.
 
 
@@ -456,7 +488,7 @@ sht_rm_value_re("ha=>.*");
                 _hash_table_name_
                 _hash_table_name_
                 _empty_line_
                 _empty_line_
 
 
-6.2.  sht_dump
+6.2. sht_dump
 
 
    Dump content of a hash table via MI.
    Dump content of a hash table via MI.
 
 
@@ -473,7 +505,7 @@ sht_rm_value_re("ha=>.*");
 
 
    7.1. htable:mod-init
    7.1. htable:mod-init
 
 
-7.1.  htable:mod-init
+7.1. htable:mod-init
 
 
    When defined, the module calls event_route[htable:mod-init] after all
    When defined, the module calls event_route[htable:mod-init] after all
    modules have been initialised. A typical use case is to initialise
    modules have been initialised. A typical use case is to initialise

+ 41 - 1
modules_k/htable/doc/htable_admin.xml

@@ -264,7 +264,7 @@ if(is_present_hf("Authorization"))
 		<listitem>
 		<listitem>
 		<para>
 		<para>
 			<emphasis>initval</emphasis> - the integer value to be returned
 			<emphasis>initval</emphasis> - the integer value to be returned
-			insted of $null when a requested key is not set.
+			instead of $null when a requested key is not set.
 		</para>
 		</para>
 		</listitem>
 		</listitem>
 		</itemizedlist>
 		</itemizedlist>
@@ -377,6 +377,25 @@ modparam("htable", "value_type_column", "vtype")
 ...
 ...
 modparam("htable", "key_value_column", "kvalue")
 modparam("htable", "key_value_column", "kvalue")
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>expires_column</varname> (str)</title>
+		<para>
+			The name of the column containing expires type.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 'expires'.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>expires_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("htable", "expires", "expiry")
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
@@ -456,6 +475,27 @@ modparam("htable", "timer_interval", 10)
 ...
 ...
 modparam("htable", "timer_mode", 1)
 modparam("htable", "timer_mode", 1)
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>db_expires</varname> (integer)</title>
+		<para>
+			If set to 1, will load/save the expires values of the items in
+			hash table fromm/to database. It applies only to hash tables that
+			have auto-expires attribute defined.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>db_expires</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("htable", "db_expires", 1)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>

+ 66 - 11
modules_k/htable/ht_db.c

@@ -41,6 +41,7 @@ str ht_db_name_column   = str_init("key_name");
 str ht_db_ktype_column  = str_init("key_type");
 str ht_db_ktype_column  = str_init("key_type");
 str ht_db_vtype_column  = str_init("value_type");
 str ht_db_vtype_column  = str_init("value_type");
 str ht_db_value_column  = str_init("key_value");
 str ht_db_value_column  = str_init("key_value");
+str ht_db_expires_column= str_init("expires");
 int ht_fetch_rows = 100;
 int ht_fetch_rows = 100;
 
 
 /**
 /**
@@ -122,8 +123,8 @@ static char ht_name_buf[HT_NAME_BUF_SIZE];
  */
  */
 int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 {
 {
-	db_key_t db_cols[4] = {&ht_db_name_column, &ht_db_ktype_column,
-		&ht_db_vtype_column, &ht_db_value_column};
+	db_key_t db_cols[5] = {&ht_db_name_column, &ht_db_ktype_column,
+		&ht_db_vtype_column, &ht_db_value_column, &ht_db_expires_column};
 	db_key_t db_ord = &ht_db_name_column;
 	db_key_t db_ord = &ht_db_name_column;
 	db1_res_t* db_res = NULL;
 	db1_res_t* db_res = NULL;
 	str kname;
 	str kname;
@@ -135,9 +136,12 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 	int last_ktype;
 	int last_ktype;
 	int n;
 	int n;
 	int_str val;
 	int_str val;
+	int_str expires;
 	int i;
 	int i;
 	int ret;
 	int ret;
 	int cnt;
 	int cnt;
+	int now;
+	int ncols;
 
 
 	if(ht_db_con==NULL)
 	if(ht_db_con==NULL)
 	{
 	{
@@ -154,9 +158,12 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 	LM_DBG("=============== loading hash table [%.*s] from database [%.*s]\n",
 	LM_DBG("=============== loading hash table [%.*s] from database [%.*s]\n",
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 	cnt = 0;
 	cnt = 0;
+	ncols = 4;
+	if(ht->htexpire > 0 && ht_db_expires_flag!=0)
+		ncols = 5;
 
 
 	if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) {
 	if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) {
-		if(ht_dbf.query(ht_db_con,0,0,0,db_cols,0,4,db_ord,0) < 0)
+		if(ht_dbf.query(ht_db_con,0,0,0,db_cols,0,ncols,db_ord,0) < 0)
 		{
 		{
 			LM_ERR("Error while querying db\n");
 			LM_ERR("Error while querying db\n");
 			return -1;
 			return -1;
@@ -176,7 +183,7 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 		}
 		}
 	} else {
 	} else {
 		if((ret=ht_dbf.query(ht_db_con, NULL, NULL, NULL, db_cols,
 		if((ret=ht_dbf.query(ht_db_con, NULL, NULL, NULL, db_cols,
-				0, 3, db_ord, &db_res))!=0
+				0, ncols, db_ord, &db_res))!=0
 			|| RES_ROW_N(db_res)<=0 )
 			|| RES_ROW_N(db_res)<=0 )
 		{
 		{
 			if( ret==0)
 			if( ret==0)
@@ -193,10 +200,10 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 	pname.s = "";
 	pname.s = "";
 	n = 0;
 	n = 0;
 	last_ktype = 0;
 	last_ktype = 0;
+	now = (int)time(NULL);
 	do {
 	do {
 		for(i=0; i<RES_ROW_N(db_res); i++)
 		for(i=0; i<RES_ROW_N(db_res); i++)
 		{
 		{
-			cnt++;
 			/* not NULL values enforced in table definition ?!?! */
 			/* not NULL values enforced in table definition ?!?! */
 			kname.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
 			kname.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
 			if(kname.s==NULL) {
 			if(kname.s==NULL) {
@@ -204,6 +211,18 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 				goto error;
 				goto error;
 			}
 			}
 			kname.len = strlen(kname.s);
 			kname.len = strlen(kname.s);
+
+			expires.n = 0;
+			if(ht->htexpire > 0 && ht_db_expires_flag!=0) {
+				expires.n = RES_ROWS(db_res)[i].values[4].val.int_val;
+				if (expires.n > 0 && expires.n < now) {
+					LM_DBG("skipping expired entry [%.*s] (%d)\n", kname.len,
+							kname.s, expires.n-now);
+					continue;
+				}
+			}
+
+			cnt++;
 			ktype = RES_ROWS(db_res)[i].values[1].val.int_val;
 			ktype = RES_ROWS(db_res)[i].values[1].val.int_val;
 			if(last_ktype==1)
 			if(last_ktype==1)
 			{
 			{
@@ -260,6 +279,15 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 				LM_ERR("error adding to hash table\n");
 				LM_ERR("error adding to hash table\n");
 				goto error;
 				goto error;
 			}
 			}
+
+			/* set expiry */
+			if (ht->htexpire > 0 && expires.n > 0) {
+				expires.n -= now;
+				if(ht_set_cell_expire(ht, &hname, 0, &expires)) {
+					LM_ERR("error setting expires to hash entry [%*.s]\n", hname.len, hname.s);
+					goto error;
+				}
+			}
 	 	}
 	 	}
 		if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) {
 		if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) {
 			if(ht_dbf.fetch_result(ht_db_con, &db_res, ht_fetch_rows)<0) {
 			if(ht_dbf.fetch_result(ht_db_con, &db_res, ht_fetch_rows)<0) {
@@ -301,12 +329,14 @@ error:
  */
  */
 int ht_db_save_table(ht_t *ht, str *dbtable)
 int ht_db_save_table(ht_t *ht, str *dbtable)
 {
 {
-	db_key_t db_cols[4] = {&ht_db_name_column, &ht_db_ktype_column,
-		&ht_db_vtype_column, &ht_db_value_column};
-	db_val_t db_vals[4];
+	db_key_t db_cols[5] = {&ht_db_name_column, &ht_db_ktype_column,
+		&ht_db_vtype_column, &ht_db_value_column, &ht_db_expires_column};
+	db_val_t db_vals[5];
 	ht_cell_t *it;
 	ht_cell_t *it;
 	str tmp;
 	str tmp;
 	int i;
 	int i;
+	time_t now;
+	int ncols;
 
 
 	if(ht_db_con==NULL)
 	if(ht_db_con==NULL)
 	{
 	{
@@ -323,12 +353,28 @@ int ht_db_save_table(ht_t *ht, str *dbtable)
 	LM_DBG("save the content of hash table [%.*s] to database in [%.*s]\n",
 	LM_DBG("save the content of hash table [%.*s] to database in [%.*s]\n",
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 
 
+	now = time(NULL);
+
 	for(i=0; i<ht->htsize; i++)
 	for(i=0; i<ht->htsize; i++)
 	{
 	{
 		lock_get(&ht->entries[i].lock);
 		lock_get(&ht->entries[i].lock);
 		it = ht->entries[i].first;
 		it = ht->entries[i].first;
 		while(it)
 		while(it)
 		{
 		{
+			if(it->flags&AVP_VAL_STR) {
+				LM_DBG("entry key: [%.*s] value: [%.*s] (str)\n",
+					it->name.len, it->name.s, it->value.s.len, it->value.s.s);
+			} else {
+				LM_DBG("entry key: [%.*s] value: [%d] (int)\n",
+					it->name.len, it->name.s, it->value.n);
+			}
+
+			if (it->expire <= now) {
+				LM_DBG("skipping expired entry");
+				it = it->next;
+				continue;
+			}
+
 			db_vals[0].type = DB1_STR;
 			db_vals[0].type = DB1_STR;
 			db_vals[0].nul  = 0;
 			db_vals[0].nul  = 0;
 			db_vals[0].val.str_val.s   = it->name.s;
 			db_vals[0].val.str_val.s   = it->name.s;
@@ -340,19 +386,28 @@ int ht_db_save_table(ht_t *ht, str *dbtable)
 
 
 			db_vals[2].type = DB1_INT;
 			db_vals[2].type = DB1_INT;
 			db_vals[2].nul = 0;
 			db_vals[2].nul = 0;
-			db_vals[2].val.int_val = 0;
 
 
 			db_vals[3].type = DB1_STR;
 			db_vals[3].type = DB1_STR;
 			db_vals[3].nul  = 0;
 			db_vals[3].nul  = 0;
 			if(it->flags&AVP_VAL_STR) {
 			if(it->flags&AVP_VAL_STR) {
+				db_vals[2].val.int_val = 0;
 				db_vals[3].val.str_val.s   = it->value.s.s;
 				db_vals[3].val.str_val.s   = it->value.s.s;
 				db_vals[3].val.str_val.len = it->value.s.len;
 				db_vals[3].val.str_val.len = it->value.s.len;
 			} else {
 			} else {
+				db_vals[2].val.int_val = 1;
 				tmp.s = sint2str((long)it->value.n, &tmp.len);
 				tmp.s = sint2str((long)it->value.n, &tmp.len);
 				db_vals[3].val.str_val.s   = tmp.s;
 				db_vals[3].val.str_val.s   = tmp.s;
 				db_vals[3].val.str_val.len = tmp.len;
 				db_vals[3].val.str_val.len = tmp.len;
 			}
 			}
-			if(ht_dbf.insert(ht_db_con, db_cols, db_vals, 4) < 0)
+			ncols = 4;
+
+			if(ht_db_expires_flag!=0 && ht->htexpire > 0) {
+				db_vals[4].type = DB1_INT;
+				db_vals[4].nul = 0;
+				db_vals[4].val.int_val = (int)it->expire;
+				ncols = 5;
+			}
+			if(ht_dbf.insert(ht_db_con, db_cols, db_vals, ncols) < 0)
 			{
 			{
 				LM_ERR("failed to store key [%.*s] in table [%.*s]\n",
 				LM_ERR("failed to store key [%.*s] in table [%.*s]\n",
 						it->name.len, it->name.s,
 						it->name.len, it->name.s,
@@ -383,7 +438,7 @@ int ht_db_delete_records(str *dbtable)
 	}
 	}
 
 
 	if(ht_dbf.delete(ht_db_con, NULL, NULL, NULL, 0) < 0)
 	if(ht_dbf.delete(ht_db_con, NULL, NULL, NULL, 0) < 0)
-		LM_ERR("failed to detele db records in [%.*s]\n",
+		LM_ERR("failed to delete db records in [%.*s]\n",
 				dbtable->len, dbtable->s);
 				dbtable->len, dbtable->s);
 	return 0;
 	return 0;
 }
 }

+ 2 - 0
modules_k/htable/ht_db.h

@@ -30,8 +30,10 @@ extern str ht_db_name_column;
 extern str ht_db_ktype_column;
 extern str ht_db_ktype_column;
 extern str ht_db_vtype_column;
 extern str ht_db_vtype_column;
 extern str ht_db_value_column;
 extern str ht_db_value_column;
+extern str ht_db_expires_column;
 extern str ht_array_size_suffix;
 extern str ht_array_size_suffix;
 extern int ht_fetch_rows;
 extern int ht_fetch_rows;
+extern int ht_db_expires_flag;
 
 
 int ht_db_init_params(void);
 int ht_db_init_params(void);
 int ht_db_init_con(void);
 int ht_db_init_con(void);

+ 3 - 0
modules_k/htable/htable.c

@@ -48,6 +48,7 @@
 MODULE_VERSION
 MODULE_VERSION
 
 
 int  ht_timer_interval = 20;
 int  ht_timer_interval = 20;
+int  ht_db_expires_flag = 0;
 
 
 static int htable_init_rpc(void);
 static int htable_init_rpc(void);
 
 
@@ -109,9 +110,11 @@ static param_export_t params[]={
 	{"key_type_column",    STR_PARAM, &ht_db_ktype_column.s},
 	{"key_type_column",    STR_PARAM, &ht_db_ktype_column.s},
 	{"value_type_column",  STR_PARAM, &ht_db_vtype_column.s},
 	{"value_type_column",  STR_PARAM, &ht_db_vtype_column.s},
 	{"key_value_column",   STR_PARAM, &ht_db_value_column.s},
 	{"key_value_column",   STR_PARAM, &ht_db_value_column.s},
+	{"expires_column",     STR_PARAM, &ht_db_expires_column.s},
 	{"array_size_suffix",  STR_PARAM, &ht_array_size_suffix.s},
 	{"array_size_suffix",  STR_PARAM, &ht_array_size_suffix.s},
 	{"fetch_rows",         INT_PARAM, &ht_fetch_rows},
 	{"fetch_rows",         INT_PARAM, &ht_fetch_rows},
 	{"timer_interval",     INT_PARAM, &ht_timer_interval},
 	{"timer_interval",     INT_PARAM, &ht_timer_interval},
+	{"db_expires",         INT_PARAM, &ht_db_expires_flag},
 	{0,0,0}
 	{0,0,0}
 };
 };
 
 

+ 3 - 7
modules_k/presence/event_list.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: presence.c 1953 2007-04-04 08:50:33Z anca_vamanu $
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2007-04-04  initial version (anca)
  *  2007-04-04  initial version (anca)
  */
  */
 
 
-/*! \file
- * \brief Kamailio presence module
- * \ref event_list.h
+/*!
+ * \file
+ * \brief Kamailio presence module :: Events
  * \ingroup presence 
  * \ingroup presence 
  */
  */
 
 

+ 2 - 5
modules_k/presence/event_list.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: event_list.h 1953 2007-04-04 08:50:33Z anca_vamanu $
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2007-04-05  initial version (anca)
  *  2007-04-05  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Events
  * \brief Kamailio presence module :: Events
  * \ingroup presence 
  * \ingroup presence 
  */
  */

+ 10 - 14
modules_k/presence/presence.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id$
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,18 +22,18 @@
  *  2006-08-15  initial version (anca)
  *  2006-08-15  initial version (anca)
  */
  */
 
 
-/*! \defgroup presence Presence :: A generic implementation of the SIP event package (PUBLISH, SUBSCRIBE, NOTIFY)
- *
- *	   The Kamailio presence module is a generic module for SIP event packages, which is much more than presence.
- *	   It is extensible by developing other modules that use the internal developer API.
- *	   Examples:
- *	   - \ref presence_mwi
- *	   - \ref presence_xml
+/*!
+ * \defgroup presence Presence :: A generic implementation of the SIP event package (PUBLISH, SUBSCRIBE, NOTIFY)
+ * The Kamailio presence module is a generic module for SIP event packages, which is much more than presence.
+ * It is extensible by developing other modules that use the internal developer API.
+ * Examples:
+ *- \ref presence_mwi
+ *- \ref presence_xml
  */
  */
 
 
-/*! \file
- * \brief Kamailio presence module
- * 
+/*!
+ * \file
+ * \brief Kamailio presence module :: Core
  * \ingroup presence 
  * \ingroup presence 
  */
  */
 
 

+ 6 - 9
modules_k/presence/presence.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id
- *
- * presence - presence server implementation
- * 
  * Copyright (C) 2006 Voice Sistem SRL
  * Copyright (C) 2006 Voice Sistem SRL
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -27,7 +23,8 @@
  *  2006-10-09  first version (anca)
  *  2006-10-09  first version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Core
  * \brief Kamailio presence module :: Core
  * \ingroup presence 
  * \ingroup presence 
  */
  */
@@ -46,14 +43,14 @@
 
 
 /* DB modes */
 /* DB modes */
 
 
-/* subscriptions are held in memory and periodically updated to db, but retrieved from db only at startup */
+/** subscriptions are held in memory and periodically updated to db, but retrieved from db only at startup */
 #define DB_MEMORY_ONLY 0
 #define DB_MEMORY_ONLY 0
-/* same as memory_only, but if a subscription is not found, it falls back to db */
+/** same as memory_only, but if a subscription is not found, it falls back to db */
 #define DB_FALLBACK 1
 #define DB_FALLBACK 1
-/* subscriptions are held only in database */
+/** subscriptions are held only in database */
 #define DB_ONLY 2
 #define DB_ONLY 2
 
 
-/* TM bind */
+/** TM bind */
 extern struct tm_binds tmb;
 extern struct tm_binds tmb;
 
 
 extern sl_api_t slb;
 extern sl_api_t slb;

+ 2 - 5
modules_k/presence/presentity.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id$
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2006-08-15  initial version (anca)
  *  2006-08-15  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Presentity handling
  * \brief Kamailio presence module :: Presentity handling
  * \ingroup presence 
  * \ingroup presence 
  */
  */

+ 2 - 6
modules_k/presence/presentity.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id$
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2006-08-15  initial version (anca)
  *  2006-08-15  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Presentity handling
  * \brief Kamailio presence module :: Presentity handling
- * \ref presentity.c
  * \ingroup presence 
  * \ingroup presence 
  */
  */
 
 

+ 2 - 5
modules_k/presence/publish.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id$
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2006-08-15  initial version (anca)
  *  2006-08-15  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Support for PUBLISH handling
  * \brief Kamailio presence module :: Support for PUBLISH handling
  * \ingroup presence 
  * \ingroup presence 
  */
  */

+ 2 - 6
modules_k/presence/publish.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id$
- *
- * presence module - presence server implementation
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2006-08-15  initial version (anca)
  *  2006-08-15  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: PUBLISH support
  * \brief Kamailio presence module :: PUBLISH support
- * \ref publish.c
  * \ingroup presence 
  * \ingroup presence 
  */
  */
 
 

+ 5 - 2
modules_k/presence_mwi/presence_mwi.c

@@ -1,6 +1,4 @@
 /*
 /*
- * presence_mwi module - Presence Handling of message-summary events
- *
  * Copyright (C) 2007 Juha Heinanen
  * Copyright (C) 2007 Juha Heinanen
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -24,6 +22,11 @@
  *  2007-05-1  initial version (jih)
  *  2007-05-1  initial version (jih)
  */
  */
 
 
+
+/*!
+ * \defgroup presence_mwi Presence_mwi :: Presence Handling of message-summary events
+ */
+
 /*!
 /*!
  * \file
  * \file
  * \brief SIP-router Presence :: Message waiting indication
  * \brief SIP-router Presence :: Message waiting indication

+ 0 - 2
modules_k/presence_mwi/presence_mwi.h

@@ -1,6 +1,4 @@
 /*
 /*
- * presence_mwi module - presence_mwi header file
- *
  * Copyright (C) 2007 Juha Heinanen
  * Copyright (C) 2007 Juha Heinanen
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.

+ 4 - 9
modules_k/presence_xml/add_events.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: add_events.c 2006-12-07 18:05:05Z anca_vamanu $
- *
- * presence_xml module - 
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,14 +22,13 @@
  *  2007-04-17  initial version (anca)
  *  2007-04-17  initial version (anca)
  */
  */
 
 
-/*! \file
- * \brief Kamailio Presence_XML :: 
+/*!
+ * \file
+ * \brief Kamailio Presence_XML :: Several event packages, presence, presence.winfo, dialog;sla 
  * \ingroup presence_xml
  * \ingroup presence_xml
  */
  */
 
 
-/*
- *	add 3 events: presence, presence.winfo, dialog;sla
- * */
+
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>

+ 3 - 7
modules_k/presence_xml/add_events.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: add_events.h 2006-12-07 18:05:05Z anca_vamanu $
- *
- * presence_xml module - 
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2007-04-18  initial version (anca)
  *  2007-04-18  initial version (anca)
  */
  */
 
 
-/*! \file
- * \brief Kamailio Presence_XML :: 
- * \ref add_events.c
+/*!
+ * \file
+ * \brief Kamailio Presence_XML :: Several event packages, presence, presence.winfo, dialog;sla 
  * \ingroup presence_xml
  * \ingroup presence_xml
  */
  */
 
 

+ 4 - 6
modules_k/presence_xml/presence_xml.c

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: presence_xml.c 2006-12-07 18:05:05Z anca_vamanu$
- *
- * presence_xml module - Presence Handling XML bodies module
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.
@@ -26,12 +22,14 @@
  *  2007-04-12  initial version (anca)
  *  2007-04-12  initial version (anca)
  */
  */
 
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio Presence_XML :: Core
  * \brief Kamailio Presence_XML :: Core
  * \ingroup presence_xml
  * \ingroup presence_xml
  */
  */
 
 
-/*! \defgroup presence_xml Presence_xml :: This module implements a range of XML-based SIP event packages for presence
+/*!
+ * \defgroup presence_xml Presence_xml :: This module implements a range of XML-based SIP event packages for presence
  */
  */
 
 
 
 

+ 0 - 4
modules_k/presence_xml/presence_xml.h

@@ -1,8 +1,4 @@
 /*
 /*
- * $Id: presence_xml.h 2006-12-07 18:05:05Z anca_vamanu$
- *
- * presence_xml module - Presence Handling XML bodies module
- *
  * Copyright (C) 2006 Voice Sistem S.R.L.
  * Copyright (C) 2006 Voice Sistem S.R.L.
  *
  *
  * This file is part of Kamailio, a free SIP server.
  * This file is part of Kamailio, a free SIP server.

+ 58 - 17
modules_k/pua/hash.c

@@ -244,28 +244,31 @@ void insert_htable(ua_pres_t* presentity)
 
 
 }
 }
 
 
+/* This function used to perform a search to find the hash table
+   entry that matches the presentity it is passed.  However,
+   everywhere it is used it is passed a pointer to the correct
+   hash table entry already...  so let's just delete that */
 void delete_htable(ua_pres_t* presentity, unsigned int hash_code)
 void delete_htable(ua_pres_t* presentity, unsigned int hash_code)
 { 
 { 
-	ua_pres_t* p= NULL, *q= NULL;
+	ua_pres_t *q = NULL;
 
 
-	p= search_htable(presentity, hash_code);
-	if(p== NULL)
+	if (presentity == NULL)
 		return;
 		return;
 
 
-	q=HashT->p_records[hash_code].entity;
+	q = HashT->p_records[hash_code].entity;
 
 
-	while(q->next!=p)
-		q= q->next;
-	q->next=p->next;
+	while (q->next != presentity)
+		q = q->next;
+	q->next = presentity->next;
 	
 	
-	if(p->etag.s)
-		shm_free(p->etag.s);
+	if(presentity->etag.s)
+		shm_free(presentity->etag.s);
 	else
 	else
-		if(p->remote_contact.s)
-			shm_free(p->remote_contact.s);
+		if(presentity->remote_contact.s)
+			shm_free(presentity->remote_contact.s);
 
 
-	shm_free(p);
-	p= NULL;
+	shm_free(presentity);
+	presentity = NULL;
 
 
 }
 }
 	
 	
@@ -323,7 +326,7 @@ ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code)
 			if((p->pres_uri->len== dialog->pres_uri->len) &&
 			if((p->pres_uri->len== dialog->pres_uri->len) &&
 				(strncmp(p->pres_uri->s, dialog->pres_uri->s,p->pres_uri->len)==0)&&
 				(strncmp(p->pres_uri->s, dialog->pres_uri->s,p->pres_uri->len)==0)&&
 				(p->watcher_uri->len== dialog->watcher_uri->len) &&
 				(p->watcher_uri->len== dialog->watcher_uri->len) &&
- 	    		(strncmp(p->watcher_uri->s,dialog->watcher_uri->s,p->watcher_uri->len )==0)&&
+				(strncmp(p->watcher_uri->s,dialog->watcher_uri->s,p->watcher_uri->len )==0)&&
 				(strncmp(p->call_id.s, dialog->call_id.s, p->call_id.len)== 0) &&
 				(strncmp(p->call_id.s, dialog->call_id.s, p->call_id.len)== 0) &&
 				(strncmp(p->to_tag.s, dialog->to_tag.s, p->to_tag.len)== 0) &&
 				(strncmp(p->to_tag.s, dialog->to_tag.s, p->to_tag.len)== 0) &&
 				(strncmp(p->from_tag.s, dialog->from_tag.s, p->from_tag.len)== 0) )
 				(strncmp(p->from_tag.s, dialog->from_tag.s, p->from_tag.len)== 0) )
@@ -338,6 +341,39 @@ ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code)
 	return p;
 	return p;
 }
 }
 
 
+/* must lock the record line before calling this function*/
+ua_pres_t* get_temporary_dialog(ua_pres_t* dialog, unsigned int hash_code)
+{
+	ua_pres_t* p= NULL, *L;
+	LM_DBG("core_hash= %u\n", hash_code);
+
+	L= HashT->p_records[hash_code].entity;
+	for(p= L->next; p; p=p->next)
+	{
+		LM_DBG("pres_uri= %.*s\twatcher_uri=%.*s\n\t"
+				"callid= %.*s\tfrom_tag= %.*s\n",
+			p->pres_uri->len, p->pres_uri->s, p->watcher_uri->len,
+			p->watcher_uri->s,p->call_id.len, p->call_id.s,
+			p->from_tag.len, p->from_tag.s);
+
+		if((p->pres_uri->len== dialog->pres_uri->len) &&
+			(strncmp(p->pres_uri->s, dialog->pres_uri->s,p->pres_uri->len)==0)&&
+			(p->watcher_uri->len== dialog->watcher_uri->len) &&
+			(strncmp(p->watcher_uri->s,dialog->watcher_uri->s,p->watcher_uri->len )==0)&&
+			(p->call_id.len == dialog->call_id.len) &&
+			(strncmp(p->call_id.s, dialog->call_id.s, p->call_id.len)== 0) &&
+			(p->from_tag.len == dialog->from_tag.len) &&
+			(strncmp(p->from_tag.s, dialog->from_tag.s, p->from_tag.len)== 0) &&
+			p->to_tag.len == 0)
+			{
+				LM_DBG("FOUND temporary dialog\n");
+				break;
+			}
+	}
+
+	return p;
+}
+
 int get_record_id(ua_pres_t* dialog, str** rec_id)
 int get_record_id(ua_pres_t* dialog, str** rec_id)
 {
 {
 	unsigned int hash_code;
 	unsigned int hash_code;
@@ -352,9 +388,14 @@ int get_record_id(ua_pres_t* dialog, str** rec_id)
 	rec= get_dialog(dialog, hash_code);
 	rec= get_dialog(dialog, hash_code);
 	if(rec== NULL)
 	if(rec== NULL)
 	{
 	{
-		LM_DBG("Record not found\n");
-		lock_release(&HashT->p_records[hash_code].lock);
-		return 0;
+		LM_DBG("Record not found - looking for temporary\n");
+		rec = get_temporary_dialog(dialog, hash_code);
+		if (rec == NULL)
+		{
+			LM_DBG("Temporary record not found\n");
+			lock_release(&HashT->p_records[hash_code].lock);
+			return 0;
+		}
 	}
 	}
 	id= (str*)pkg_malloc(sizeof(str));
 	id= (str*)pkg_malloc(sizeof(str));
 	if(id== NULL)
 	if(id== NULL)

+ 1 - 0
modules_k/pua/hash.h

@@ -125,6 +125,7 @@ void destroy_htable(void);
 int is_dialog(ua_pres_t* dialog);
 int is_dialog(ua_pres_t* dialog);
 
 
 ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code);
 ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code);
+ua_pres_t* get_temporary_dialog(ua_pres_t* dialog, unsigned int hash_code);
 
 
 int get_record_id(ua_pres_t* dialog, str** rec_id);
 int get_record_id(ua_pres_t* dialog, str** rec_id);
 typedef int (*get_record_id_t)(ua_pres_t* dialog, str** rec_id);
 typedef int (*get_record_id_t)(ua_pres_t* dialog, str** rec_id);

+ 48 - 16
modules_k/pua/pua.c

@@ -53,7 +53,7 @@
 #include "pidf.h"
 #include "pidf.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
-#define PUA_TABLE_VERSION 6
+#define PUA_TABLE_VERSION 7
 
 
 struct tm_binds tmb;
 struct tm_binds tmb;
 htable_t* HashT= NULL;
 htable_t* HashT= NULL;
@@ -749,14 +749,14 @@ static void db_update(unsigned int ticks,void *param)
 	db_key_t db_cols[5];
 	db_key_t db_cols[5];
 	db_val_t q_vals[20], db_vals[5];
 	db_val_t q_vals[20], db_vals[5];
 	db_op_t  db_ops[1] ;
 	db_op_t  db_ops[1] ;
-	int n_query_cols= 0, n_query_update= 0;
+	int n_query_cols= 0, n_query_update= 0, n_actual_query_cols= 0;
 	int n_update_cols= 0;
 	int n_update_cols= 0;
 	int i;
 	int i;
 	int puri_col,pid_col,expires_col,flag_col,etag_col,tuple_col,event_col;
 	int puri_col,pid_col,expires_col,flag_col,etag_col,tuple_col,event_col;
 	int watcher_col,callid_col,totag_col,fromtag_col,record_route_col,cseq_col;
 	int watcher_col,callid_col,totag_col,fromtag_col,record_route_col,cseq_col;
 	int no_lock= 0, contact_col, desired_expires_col, extra_headers_col;
 	int no_lock= 0, contact_col, desired_expires_col, extra_headers_col;
 	int remote_contact_col, version_col;
 	int remote_contact_col, version_col;
-	
+
 	if(ticks== 0 && param == NULL)
 	if(ticks== 0 && param == NULL)
 		no_lock= 1;
 		no_lock= 1;
 
 
@@ -765,7 +765,7 @@ static void db_update(unsigned int ticks,void *param)
 	q_vals[puri_col].type = DB1_STR;
 	q_vals[puri_col].type = DB1_STR;
 	q_vals[puri_col].nul = 0;
 	q_vals[puri_col].nul = 0;
 	n_query_cols++;
 	n_query_cols++;
-	
+
 	q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
 	q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
 	q_vals[pid_col].type = DB1_STR;
 	q_vals[pid_col].type = DB1_STR;
 	q_vals[pid_col].nul = 0;
 	q_vals[pid_col].nul = 0;
@@ -1003,21 +1003,43 @@ static void db_update(unsigned int ticks,void *param)
 					q_vals[puri_col].val.str_val = *(p->pres_uri);
 					q_vals[puri_col].val.str_val = *(p->pres_uri);
 					q_vals[pid_col].val.str_val = p->id;
 					q_vals[pid_col].val.str_val = p->id;
 					q_vals[flag_col].val.int_val = p->flag;
 					q_vals[flag_col].val.int_val = p->flag;
-					if((p->watcher_uri))
-						q_vals[watcher_col].val.str_val = *(p->watcher_uri);
-					else
-						memset(& q_vals[watcher_col].val.str_val ,0, sizeof(str));
-					q_vals[tuple_col].val.str_val = p->tuple_id;
-					q_vals[etag_col].val.str_val = p->etag;
 					q_vals[callid_col].val.str_val = p->call_id;
 					q_vals[callid_col].val.str_val = p->call_id;
-					q_vals[totag_col].val.str_val = p->to_tag;
 					q_vals[fromtag_col].val.str_val = p->from_tag;
 					q_vals[fromtag_col].val.str_val = p->from_tag;
 					q_vals[cseq_col].val.int_val= p->cseq;
 					q_vals[cseq_col].val.int_val= p->cseq;
 					q_vals[expires_col].val.int_val = p->expires;
 					q_vals[expires_col].val.int_val = p->expires;
 					q_vals[desired_expires_col].val.int_val = p->desired_expires;
 					q_vals[desired_expires_col].val.int_val = p->desired_expires;
 					q_vals[event_col].val.int_val = p->event;
 					q_vals[event_col].val.int_val = p->event;
 					q_vals[version_col].val.int_val = p->version;
 					q_vals[version_col].val.int_val = p->version;
-					
+
+					if((p->watcher_uri))
+						q_vals[watcher_col].val.str_val = *(p->watcher_uri);
+					else
+						memset(& q_vals[watcher_col].val.str_val ,0, sizeof(str));
+
+					if(p->tuple_id.s == NULL)
+					{
+						q_vals[tuple_col].val.str_val.s="";
+						q_vals[tuple_col].val.str_val.len=0;
+					}
+					else
+						q_vals[tuple_col].val.str_val = p->tuple_id;
+
+					if(p->etag.s == NULL)
+					{
+						q_vals[etag_col].val.str_val.s="";
+						q_vals[etag_col].val.str_val.len=0;
+					}
+					else
+						q_vals[etag_col].val.str_val = p->etag;
+
+					if (p->to_tag.s == NULL)
+					{
+						q_vals[totag_col].val.str_val.s="";
+						q_vals[totag_col].val.str_val.len=0;
+					}
+					else
+						q_vals[totag_col].val.str_val = p->to_tag;
+
 					if(p->record_route.s== NULL)
 					if(p->record_route.s== NULL)
 					{
 					{
 						q_vals[record_route_col].val.str_val.s= "";
 						q_vals[record_route_col].val.str_val.s= "";
@@ -1025,8 +1047,15 @@ static void db_update(unsigned int ticks,void *param)
 					}
 					}
 					else
 					else
 						q_vals[record_route_col].val.str_val = p->record_route;
 						q_vals[record_route_col].val.str_val = p->record_route;
-					
-					q_vals[contact_col].val.str_val = p->contact;
+
+					if(p->contact.s == NULL)
+					{
+						q_vals[contact_col].val.str_val.s = "";
+						q_vals[contact_col].val.str_val.len = 0;
+					}
+					else
+						q_vals[contact_col].val.str_val = p->contact;
+
 					if(p->remote_contact.s)
 					if(p->remote_contact.s)
 					{
 					{
 						q_vals[remote_contact_col].val.str_val = p->remote_contact;
 						q_vals[remote_contact_col].val.str_val = p->remote_contact;
@@ -1039,11 +1068,14 @@ static void db_update(unsigned int ticks,void *param)
 					}
 					}
 
 
 					if(p->extra_headers)
 					if(p->extra_headers)
+					{
+						n_actual_query_cols = n_query_cols;
 						q_vals[extra_headers_col].val.str_val = *(p->extra_headers);
 						q_vals[extra_headers_col].val.str_val = *(p->extra_headers);
+					}
 					else
 					else
-						n_query_cols--;
+						n_actual_query_cols = n_query_cols - 1;
 						
 						
-					if(pua_dbf.insert(pua_db, q_cols, q_vals,n_query_cols )<0)
+					if(pua_dbf.insert(pua_db, q_cols, q_vals,n_actual_query_cols )<0)
 					{
 					{
 						LM_ERR("while inserting in db table pua\n");
 						LM_ERR("while inserting in db table pua\n");
 						if(!no_lock)
 						if(!no_lock)

+ 73 - 2
modules_k/pua/send_subscribe.c

@@ -344,7 +344,6 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 		hentity->call_id=  msg->callid->body;
 		hentity->call_id=  msg->callid->body;
 		hentity->to_tag= pto->tag_value;
 		hentity->to_tag= pto->tag_value;
 		hentity->from_tag= pfrom->tag_value;
 		hentity->from_tag= pfrom->tag_value;
-	
 	}
 	}
 
 
 	/* extract the other necesary information for inserting a new record */		
 	/* extract the other necesary information for inserting a new record */		
@@ -608,6 +607,12 @@ done:
 		run_pua_callbacks( hentity, msg);
 		run_pua_callbacks( hentity, msg);
 	}
 	}
 error:	
 error:	
+	lock_get(&HashT->p_records[hash_code].lock);
+	presentity = get_temporary_dialog(hentity, hash_code);
+	if (presentity!=NULL)
+		delete_htable(presentity, hash_code);
+	lock_release(&HashT->p_records[hash_code].lock);
+
 	if(hentity)
 	if(hentity)
 	{	
 	{	
 		shm_free(hentity);
 		shm_free(hentity);
@@ -858,6 +863,7 @@ int send_subscribe(subs_info_t* subs)
 	
 	
 	if(presentity== NULL )
 	if(presentity== NULL )
 	{
 	{
+		int size;
 insert:
 insert:
 		lock_release(&HashT->p_records[hash_code].lock); 
 		lock_release(&HashT->p_records[hash_code].lock); 
 		if(subs->flag & UPDATE_TYPE)
 		if(subs->flag & UPDATE_TYPE)
@@ -887,7 +893,7 @@ insert:
 
 
 		set_uac_req(&uac_r, &met, str_hdr, 0, 0, TMCB_LOCAL_COMPLETED,
 		set_uac_req(&uac_r, &met, str_hdr, 0, 0, TMCB_LOCAL_COMPLETED,
 				subs_cback_func, (void*)hentity);
 				subs_cback_func, (void*)hentity);
-		result= tmb.t_request
+		result= tmb.t_request_outside
 			(&uac_r,						  /* Type of the message */
 			(&uac_r,						  /* Type of the message */
 		subs->remote_target?subs->remote_target:subs->pres_uri,/* Request-URI*/
 		subs->remote_target?subs->remote_target:subs->pres_uri,/* Request-URI*/
 			subs->pres_uri,				  /* To */
 			subs->pres_uri,				  /* To */
@@ -897,9 +903,74 @@ insert:
 		if(result< 0)
 		if(result< 0)
 		{
 		{
 			LM_ERR("while sending request with t_request\n");
 			LM_ERR("while sending request with t_request\n");
+			if (uac_r.dialog != NULL)
+			{
+				uac_r.dialog->rem_target.s = 0;
+				uac_r.dialog->dst_uri.s = 0;
+				tmb.free_dlg(uac_r.dialog);
+				uac_r.dialog = 0;
+			}
 			shm_free(hentity);
 			shm_free(hentity);
 			goto  done;
 			goto  done;
 		}
 		}
+
+		/* Now create a temporary hash table entry.
+		   This is needed to deal with the race-hazard when NOTIFYs
+		   arrive before the 2xx response to the SUBSCRIBE. */
+		size = sizeof(ua_pres_t)+ 2 * sizeof(str) + (
+			subs->pres_uri->len +
+			subs->watcher_uri->len +
+			uac_r.dialog->id.loc_tag.len +
+			uac_r.dialog->id.call_id.len +
+			subs->id.len) * sizeof(char);
+
+		presentity= (ua_pres_t*)shm_malloc(size);
+		if(presentity== NULL)
+		{
+			LM_ERR("no more share memory\n");
+			goto done;
+		}
+		memset(presentity, 0, size);
+		size= sizeof(ua_pres_t);
+
+		presentity->pres_uri = (str *) ((char *) presentity + size);
+		size += sizeof(str);
+		presentity->pres_uri->s= (char *) presentity + size;
+		memcpy(presentity->pres_uri->s, subs->pres_uri->s, subs->pres_uri->len);
+		presentity->pres_uri->len= subs->pres_uri->len;
+		size+= subs->pres_uri->len;
+
+		presentity->watcher_uri= (str *) ((char *) presentity + size);
+		size += sizeof(str);
+		presentity->watcher_uri->s= (char *) presentity + size;
+		memcpy(presentity->watcher_uri->s, subs->watcher_uri->s, subs->watcher_uri->len);
+		presentity->watcher_uri->len = subs->watcher_uri->len;
+		size += subs->watcher_uri->len;
+
+		presentity->call_id.s = (char *) presentity + size;
+		memcpy(presentity->call_id.s, uac_r.dialog->id.call_id.s, uac_r.dialog->id.call_id.len);
+		presentity->call_id.len = uac_r.dialog->id.call_id.len;
+		size += uac_r.dialog->id.call_id.len;
+
+		presentity->from_tag.s = (char *) presentity + size;
+		memcpy(presentity->from_tag.s, uac_r.dialog->id.loc_tag.s, uac_r.dialog->id.loc_tag.len);
+		presentity->from_tag.len= uac_r.dialog->id.loc_tag.len;
+		size += uac_r.dialog->id.loc_tag.len;
+
+		presentity->id.s = (char *) presentity+ size;
+		memcpy(presentity->id.s, subs->id.s, subs->id.len);
+		presentity->id.len = subs->id.len;
+		size += subs->id.len;
+
+		/* Set the temporary record expiry for 2 * 64T1 seconds from now */
+		presentity->expires= (int)time(NULL) + 64;
+
+		insert_htable(presentity);
+
+		uac_r.dialog->rem_target.s = 0;
+		uac_r.dialog->dst_uri.s = 0;
+		tmb.free_dlg(uac_r.dialog);
+		uac_r.dialog = 0;
 	}
 	}
 	else
 	else
 	{
 	{

+ 10 - 5
modules_k/pua_xmpp/simple2xmpp.c

@@ -87,7 +87,7 @@ int Notify2Xmpp(struct sip_msg* msg, char* s1, char* s2)
 	if(msg->to->parsed != NULL)
 	if(msg->to->parsed != NULL)
 	{
 	{
 		pto = (struct to_body*)msg->to->parsed;
 		pto = (struct to_body*)msg->to->parsed;
-		LM_ERR("'To' header ALREADY PARSED:<%.*s>\n",pto->uri.len,pto->uri.s);
+		LM_DBG("'To' header ALREADY PARSED:<%.*s>\n",pto->uri.len,pto->uri.s);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -415,7 +415,8 @@ int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id,
 		goto error;
 		goto error;
 	}
 	}
 
 
-	if(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"away")== 0)
+	if((xmlStrcasecmp((unsigned char*)note, (unsigned char*)"away")== 0)||
+			(xmlStrcasecmp((unsigned char*)note, (unsigned char*)"On the phone")== 0))
 	{
 	{
 		new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show",
 		new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show",
 				BAD_CAST "away");
 				BAD_CAST "away");
@@ -456,12 +457,14 @@ int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id,
 					LM_ERR("while adding node: idle\n");
 					LM_ERR("while adding node: idle\n");
 					goto error;
 					goto error;
 				}	
 				}	
-			}
-			else */	
+			}*/
+			else
 				if((xmlStrcasecmp((unsigned char*)note,
 				if((xmlStrcasecmp((unsigned char*)note,
 					(unsigned char*)"dnd")== 0)||
 					(unsigned char*)"dnd")== 0)||
 					(xmlStrcasecmp((unsigned char*)note,
 					(xmlStrcasecmp((unsigned char*)note,
-						(unsigned char*)"do not disturb")== 0))
+						(unsigned char*)"do not disturb")== 0)||
+					(xmlStrcasecmp((unsigned char*)note,
+					(unsigned char*)"Busy (DND)")== 0))
 				{
 				{
 					new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show",
 					new_node = xmlNewChild(xmpp_root, NULL, BAD_CAST "show",
 							BAD_CAST "dnd");
 							BAD_CAST "dnd");
@@ -471,6 +474,8 @@ int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id,
 						goto error;
 						goto error;
 					}		
 					}		
 				}
 				}
+				else
+					LM_DBG("Not Found Status\n");
 
 
 	
 	
 	/* adding status node */
 	/* adding status node */

+ 13 - 29
modules_k/pua_xmpp/xmpp2simple.c

@@ -76,13 +76,13 @@ void pres_Xmpp2Sip(char *msg, int type, void *param)
 	{
 	{
 		LM_DBG("type attribut not present\n");
 		LM_DBG("type attribut not present\n");
 		build_publish(pres_node, -1);
 		build_publish(pres_node, -1);
-		if(presence_subscribe(pres_node, 3600, XMPP_SUBSCRIBE)< 0)
+	/*	if(presence_subscribe(pres_node, 3600, XMPP_SUBSCRIBE)< 0)
 		{
 		{
 				LM_ERR("when sending subscribe for presence");
 				LM_ERR("when sending subscribe for presence");
 				xmlFree(pres_type);
 				xmlFree(pres_type);
 				goto error;
 				goto error;
 		}
 		}
-
+	*/
 
 
 		/* send subscribe after publish because in xmpp subscribe message
 		/* send subscribe after publish because in xmpp subscribe message
 		 * comes only when a new contact is inserted in buddy list */
 		 * comes only when a new contact is inserted in buddy list */
@@ -91,13 +91,13 @@ void pres_Xmpp2Sip(char *msg, int type, void *param)
 	if(strcmp(pres_type, "unavailable")== 0)
 	if(strcmp(pres_type, "unavailable")== 0)
 	{
 	{
 		build_publish(pres_node, 0);
 		build_publish(pres_node, 0);
-		if(presence_subscribe(pres_node, 3600, XMPP_SUBSCRIBE)< 0)
-				/* else subscribe for one hour*/
+	/*	if(presence_subscribe(pres_node, 0, XMPP_SUBSCRIBE)< 0)
 		{
 		{
 				LM_ERR("when unsubscribing for presence");
 				LM_ERR("when unsubscribing for presence");
 				xmlFree(pres_type);
 				xmlFree(pres_type);
 				goto error;
 				goto error;
 		}
 		}
+	*/
 
 
 	}		
 	}		
 	else
 	else
@@ -243,13 +243,13 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 	if(show_cont)
 	if(show_cont)
 	{
 	{
 		if(strcmp(show_cont, "xa")== 0)
 		if(strcmp(show_cont, "xa")== 0)
-			status= "not available";
+			status= "Away";
 		else
 		else
 			if(strcmp(show_cont, "chat")== 0)
 			if(strcmp(show_cont, "chat")== 0)
-				status= "free for chat";
+			status= "Online";
 		else
 		else
 			if(strcmp(show_cont, "dnd")== 0)
 			if(strcmp(show_cont, "dnd")== 0)
-				status= "do not disturb";
+			status= "Busy (DND)";
 		else
 		else
 			status= show_cont;
 			status= show_cont;
 	}
 	}
@@ -264,24 +264,25 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 			goto error;
 			goto error;
 		}
 		}
 		*/
 		*/
-		node = xmlNewChild(root_node, NULL, BAD_CAST "note",
-				BAD_CAST status_cont);
+		node = xmlNewChild(tuple_node, NULL, BAD_CAST "note",
+				BAD_CAST status);
 		if(node== NULL)
 		if(node== NULL)
 		{
 		{
 			LM_ERR("while adding node\n");
 			LM_ERR("while adding node\n");
 			goto error;
 			goto error;
 		}
 		}
-	}else
+	} else {
 		if(show_cont)
 		if(show_cont)
 		{
 		{
-			node = xmlNewChild(root_node, NULL, BAD_CAST "note", 
+			node = xmlNewChild(tuple_node, NULL, BAD_CAST "note", 
 					BAD_CAST status);
 					BAD_CAST status);
 			if(node== NULL)
 			if(node== NULL)
 			{
 			{
 				LM_ERR("while adding node\n");
 				LM_ERR("while adding node\n");
 				goto error;
 				goto error;
 			}	
 			}	
-		}	
+		}
+	}
 
 
 	if(show_cont)
 	if(show_cont)
 	{
 	{
@@ -295,23 +296,6 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 				goto error;
 				goto error;
 			}
 			}
 		}
 		}
-		node=  xmlNewChild(person_node, NULL, BAD_CAST "activities", 
-				BAD_CAST 0);
-		if(node== NULL)
-		{
-			LM_ERR("while adding node\n");
-			goto error;
-		}
-
-						
-		if( xmlNewChild(person_node, NULL, BAD_CAST "note", 
-					BAD_CAST status )== NULL)
-		{
-			LM_ERR("while adding node\n");
-			goto error;
-		}
-
-
 	}
 	}
 		
 		
 	
 	

+ 3 - 3
modules_k/pv/pv_trans.c

@@ -72,7 +72,7 @@ int tr_eval_string(struct sip_msg *msg, tr_param_t *tp, int subtype,
 	char *p, *s;
 	char *p, *s;
 	str st, st2;
 	str st, st2;
 	pv_value_t v, w;
 	pv_value_t v, w;
-	void *vp;
+	time_t t;
 
 
 	if(val==NULL || (val->flags&PV_VAL_NULL && subtype != TR_S_SQL))
 	if(val==NULL || (val->flags&PV_VAL_NULL && subtype != TR_S_SQL))
 		return -1;
 		return -1;
@@ -586,9 +586,9 @@ int tr_eval_string(struct sip_msg *msg, tr_param_t *tp, int subtype,
 			}
 			}
 			memcpy(s, st.s, st.len);
 			memcpy(s, st.s, st.len);
 			s[st.len] = '\0';
 			s[st.len] = '\0';
-			vp = (void*)&val->ri;
+			t = val->ri;
 			val->rs.len = strftime(_tr_buffer, TR_BUFFER_SIZE-1, s,
 			val->rs.len = strftime(_tr_buffer, TR_BUFFER_SIZE-1, s,
-					localtime((time_t*)vp));
+			                localtime(&t));
 			pkg_free(s);
 			pkg_free(s);
 			val->flags = PV_VAL_STR;
 			val->flags = PV_VAL_STR;
 			val->rs.s = _tr_buffer;
 			val->rs.s = _tr_buffer;

+ 114 - 7
modules_k/rls/notify.c

@@ -48,6 +48,8 @@
 #include "../../lib/kcore/hash_func.h"
 #include "../../lib/kcore/hash_func.h"
 #include "rls.h"
 #include "rls.h"
 #include "notify.h"
 #include "notify.h"
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
 
 
 typedef struct res_param
 typedef struct res_param
 {
 {
@@ -1131,6 +1133,7 @@ int parse_xcap_uri(char *uri, str *host, unsigned short *port, str *path)
 	return 1;
 	return 1;
 }
 }
 
 
+#define MAX_PATH_LEN	127
 int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		xmlNodePtr *rl_node, xmlDocPtr *xmldoc)
 		xmlNodePtr *rl_node, xmlDocPtr *xmldoc)
 {
 {
@@ -1144,6 +1147,12 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	db_val_t *row_vals;
 	db_val_t *row_vals;
 	int xcap_col;
 	int xcap_col;
 	str body;
 	str body;
+	int checked = 0;
+	str root, path = {0, 0};
+	char path_str[MAX_PATH_LEN + 1];
+	xmlXPathContextPtr xpathCtx = NULL;
+	xmlXPathObjectPtr xpathObj = NULL;
+
 
 
 	if (rl_uri==NULL || username==NULL || domain==NULL)
 	if (rl_uri==NULL || username==NULL || domain==NULL)
 	{
 	{
@@ -1151,6 +1160,56 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		return -1;
 		return -1;
 	}
 	}
 
 
+	LM_DBG("rl_uri: %.*s", rl_uri->len, rl_uri->s);
+
+	root.s = rl_uri->s;
+	root.len = rl_uri->len;
+	while (checked < rl_uri->len)
+	{
+		if (checked < rl_uri->len - 3 && strncmp(rl_uri->s + checked, "/~~", 3) == 0)
+		{
+			root.len = checked;
+			checked += 3;
+			break;
+		}
+		checked++;
+	}
+	LM_DBG("doc: %.*s", root.len, root.s);
+
+	memset (path_str, '\0', MAX_PATH_LEN + 1);
+	path.s = path_str;
+	path.len = 0;
+	while (checked < rl_uri->len && path.len <= MAX_PATH_LEN)
+	{
+		if (rl_uri->s[checked] == '/')
+		{
+			strcat(path.s, "/xmlns:");
+			path.len += 7;
+			checked++;
+		}
+		else if (checked <= rl_uri->len - 3 && strncmp(rl_uri->s + checked, "\%5b", 3) == 0)
+		{
+			path.s[path.len++] = '[';
+			checked += 3;
+		}
+		else if (checked <= rl_uri->len - 3 && strncmp(rl_uri->s + checked, "\%5d", 3) == 0)
+		{
+			path.s[path.len++] = ']';
+			checked += 3;
+		}
+		else if (checked <= rl_uri->len - 3 && strncmp(rl_uri->s + checked, "\%22", 3) == 0)
+		{
+			path.s[path.len++] = '\"';
+			checked += 3;
+		}
+		else
+		{
+			path.s[path.len++] = rl_uri->s[checked];
+			checked++;
+		}
+	}
+	LM_DBG("path: %.*s", path.len, path.s);
+
 	query_cols[n_query_cols] = &str_username_col;
 	query_cols[n_query_cols] = &str_username_col;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].nul = 0;
@@ -1172,7 +1231,7 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	query_cols[n_query_cols] = &str_doc_uri_col;
 	query_cols[n_query_cols] = &str_doc_uri_col;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].nul = 0;
-	query_vals[n_query_cols].val.str_val = *rl_uri;
+	query_vals[n_query_cols].val.str_val = root;
 	n_query_cols++;
 	n_query_cols++;
 
 
 	if(rls_dbf.use_table(rls_db, &rls_xcap_table) < 0)
 	if(rls_dbf.use_table(rls_db, &rls_xcap_table) < 0)
@@ -1187,8 +1246,8 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols,
 	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols,
 				n_query_cols, n_result_cols, 0, &result)<0)
 				n_query_cols, n_result_cols, 0, &result)<0)
 	{
 	{
-		LM_ERR("failed querying table xcap for document [rl_uri]=%.*s\n",
-				rl_uri->len, rl_uri->s);
+		LM_ERR("failed querying table xcap for document: %.*s\n",
+				root.len, root.s);
 		if(result)
 		if(result)
 			rls_dbf.free_result(rls_db, result);
 			rls_dbf.free_result(rls_db, result);
 		return -1;
 		return -1;
@@ -1225,19 +1284,67 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		goto error;
 		goto error;
 	}
 	}
 
 
-	*rl_node = XMLDocGetNodeByName(*xmldoc,"resource-lists", NULL);
-	if(rl_node==NULL)
+	if (path.len == 0)
 	{
 	{
-		LM_ERR("no resource-lists node in XML document\n");
-		goto error;
+		/* No path specified - use all resource-lists. */
+		*rl_node = XMLDocGetNodeByName(*xmldoc,"resource-lists", NULL);
+		if(rl_node==NULL)
+		{
+			LM_ERR("no resource-lists node in XML document\n");
+			goto error;
+		}
 	}
 	}
+	else if (path.s != NULL)
+	{
+		xpathCtx = xmlXPathNewContext(*xmldoc);
+		if (xpathCtx == NULL)
+		{
+			LM_ERR("unable to create new XPath context");
+			goto error;
+		}
+
+		if (xmlXPathRegisterNs(xpathCtx, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:resource-lists") != 0)
+		{
+			LM_ERR("unable to register xmlns\n");
+			goto error;
+		}
+
+		xpathObj = xmlXPathEvalExpression(BAD_CAST path.s, xpathCtx);
+		if (xpathObj == NULL)
+		{
+			LM_ERR("unable to evaluate path\n");
+			goto error;
+		}
 
 
+		if (xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr <= 0)
+		{
+			LM_ERR("no nodes found\n");
+			goto error;
+		}
+		if (xpathObj->nodesetval->nodeTab[0] != NULL && xpathObj->nodesetval->nodeTab[0]->type != XML_ELEMENT_NODE)
+		{
+			LM_ERR("no nodes of the correct type found\n");
+			goto error;
+
+		}
+
+		*rl_node = xpathObj->nodesetval->nodeTab[0];
+
+		xmlXPathFreeObject(xpathObj);
+		xmlXPathFreeContext(xpathCtx);
+	}
+	
 	rls_dbf.free_result(rls_db, result);
 	rls_dbf.free_result(rls_db, result);
 	return 1;
 	return 1;
 
 
 error:
 error:
 	if(result!=NULL)
 	if(result!=NULL)
 		rls_dbf.free_result(rls_db, result);
 		rls_dbf.free_result(rls_db, result);
+	if(xpathObj!=NULL)
+		xmlXPathFreeObject(xpathObj);
+	
+	if(xpathCtx!=NULL)
+		xmlXPathFreeContext(xpathCtx);
 	if(xmldoc!=NULL)
 	if(xmldoc!=NULL)
 		xmlFreeDoc(*xmldoc);
 		xmlFreeDoc(*xmldoc);
 
 

+ 1 - 1
modules_k/rls/rls.c

@@ -57,7 +57,7 @@
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
-#define P_TABLE_VERSION 0
+#define P_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
 
 
 /** database connection */
 /** database connection */

+ 85 - 0
modules_k/siptrace/doc/siptrace_admin.xml

@@ -229,6 +229,31 @@ modparam("siptrace", "trace_table_avp", "$avp(s:siptrace_table)")
 ...
 ...
 modparam("siptrace", "duplicate_uri", "sip:10.1.1.1:5888")
 modparam("siptrace", "duplicate_uri", "sip:10.1.1.1:5888")
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>trace_to_database</varname> (integer)</title>
+		<para>
+		Parameter to enable/disable inserts to the Database from this
+		Kamailio. 
+		</para>
+		<para>
+		In case we only want to send the SIP-Messages to the 
+		duplicate_uri and not store the information to the local 
+		database we can set this to "0".  
+		</para>
+		<para>
+		<emphasis>
+			Default value is "1".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>trace_to_database</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("siptrace", "trace_to_database", 0)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
@@ -277,6 +302,66 @@ modparam("siptrace", "trace_local_ip", "10.1.1.1:5064")
 ...
 ...
 modparam("siptrace", "trace_sl_acks", 0)
 modparam("siptrace", "trace_sl_acks", 0)
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>xheaders_write</varname> (integer)</title>
+		<para>
+		Parameter to enable/disable writing of x-headers.
+		</para>
+		<para>
+		Stores fromip, toip, method and direction in X-Siptrace-* headers.
+		This allows to transmit them to a second kamailio server
+		using the duplicate_uri feature.
+		Because the headers are added after the data is written to the database,
+		the headers only show up in the packets sent by duplicate_uri.
+		</para>
+		<para>
+		See <varname>xheaders_read</varname>, it should be used on the receiving
+		side.
+		</para>
+		<para>
+		Note: The headers are first read, then written. This allows to relay
+		the information over more then two kamailio servers by setting both
+		<varname>xheaders_write</varname> and <varname>xheaders_read</varname>
+		to "1" on the servers in the middle.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "0".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xheaders_write</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("siptrace", "xheaders_write", 0)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>xheaders_read</varname> (integer)</title>
+		<para>
+		Parameter to enable/disable reading of x-headers.
+		</para>
+		<para>
+		Reads and removes the X-Siptrace-* headers. Packets not containing the
+		headers are neither stored to the database nor relayed (duplicate_uri).
+		See <varname>xheaders_write</varname> for further information.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "0".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xheaders_read</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("siptrace", "xheaders_read", 0)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>

+ 218 - 10
modules_k/siptrace/siptrace.c

@@ -70,6 +70,7 @@ struct _siptrace_data {
 	str toip;
 	str toip;
 	char toip_buff[IP_ADDR_MAX_STR_SIZE+12];
 	char toip_buff[IP_ADDR_MAX_STR_SIZE+12];
 	char fromip_buff[IP_ADDR_MAX_STR_SIZE+12];
 	char fromip_buff[IP_ADDR_MAX_STR_SIZE+12];
+	struct timeval tv;
 #ifdef STATISTICS
 #ifdef STATISTICS
 	stat_var *stat;
 	stat_var *stat;
 #endif
 #endif
@@ -86,6 +87,7 @@ static int child_init(int rank);
 static void destroy(void);
 static void destroy(void);
 static int sip_trace(struct sip_msg*, char*, char*);
 static int sip_trace(struct sip_msg*, char*, char*);
 
 
+static int sip_trace_store_db(struct _siptrace_data* sto);
 static int trace_send_duplicate(char *buf, int len);
 static int trace_send_duplicate(char *buf, int len);
 
 
 static void trace_onreq_in(struct cell* t, int type, struct tmcb_params *ps);
 static void trace_onreq_in(struct cell* t, int type, struct tmcb_params *ps);
@@ -109,17 +111,29 @@ static str fromip_column      = str_init("fromip");      /* 06 */
 static str toip_column        = str_init("toip");        /* 07 */
 static str toip_column        = str_init("toip");        /* 07 */
 static str fromtag_column     = str_init("fromtag");     /* 08 */
 static str fromtag_column     = str_init("fromtag");     /* 08 */
 static str direction_column   = str_init("direction");   /* 09 */
 static str direction_column   = str_init("direction");   /* 09 */
+static str time_us_column     = str_init("time_us");     /* 10 */
 
 
-#define NR_KEYS 10
+#define NR_KEYS 11
+
+#define XHEADERS_BUFSIZE 512
 
 
 int trace_flag = -1;
 int trace_flag = -1;
 int trace_on   = 0;
 int trace_on   = 0;
 int trace_sl_acks = 1;
 int trace_sl_acks = 1;
 
 
+int trace_to_database = 1;
+
+int xheaders_write = 0;
+int xheaders_read = 0;
+
 str    dup_uri_str      = {0, 0};
 str    dup_uri_str      = {0, 0};
 struct sip_uri *dup_uri = 0;
 struct sip_uri *dup_uri = 0;
 
 
 int *trace_on_flag = NULL;
 int *trace_on_flag = NULL;
+int *trace_to_database_flag = NULL;
+
+int *xheaders_write_flag = NULL;
+int *xheaders_read_flag = NULL;
 
 
 static unsigned short traced_user_avp_type = 0;
 static unsigned short traced_user_avp_type = 0;
 static int_str traced_user_avp;
 static int_str traced_user_avp;
@@ -165,8 +179,11 @@ static param_export_t params[] = {
 	{"traced_user_avp",    STR_PARAM, &traced_user_avp_str.s},
 	{"traced_user_avp",    STR_PARAM, &traced_user_avp_str.s},
 	{"trace_table_avp",    STR_PARAM, &trace_table_avp_str.s},
 	{"trace_table_avp",    STR_PARAM, &trace_table_avp_str.s},
 	{"duplicate_uri",      STR_PARAM, &dup_uri_str.s        },
 	{"duplicate_uri",      STR_PARAM, &dup_uri_str.s        },
+	{"trace_to_database",  INT_PARAM, &trace_to_database    },
 	{"trace_local_ip",     STR_PARAM, &trace_local_ip.s     },
 	{"trace_local_ip",     STR_PARAM, &trace_local_ip.s     },
 	{"trace_sl_acks",      INT_PARAM, &trace_sl_acks        },
 	{"trace_sl_acks",      INT_PARAM, &trace_sl_acks        },
+	{"xheaders_write",     INT_PARAM, &xheaders_write       },
+	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -281,6 +298,23 @@ static int mod_init(void)
 	
 	
 	*trace_on_flag = trace_on;
 	*trace_on_flag = trace_on;
 	
 	
+	trace_to_database_flag = (int*)shm_malloc(sizeof(int));
+	if(trace_to_database_flag==NULL) {
+		LM_ERR("no more shm memory left\n");
+		return -1;
+	}
+	
+	*trace_to_database_flag = trace_to_database;
+	
+	xheaders_write_flag = (int*)shm_malloc(sizeof(int));
+	xheaders_read_flag = (int*)shm_malloc(sizeof(int));
+	if (!(xheaders_write_flag && xheaders_read_flag)) {
+		LM_ERR("no more shm memory left\n");
+		return -1;
+	}
+	*xheaders_write_flag = xheaders_write;
+	*xheaders_read_flag = xheaders_read;
+
 	/* register callbacks to TM */
 	/* register callbacks to TM */
 	if (load_tm_api(&tmb)!=0)
 	if (load_tm_api(&tmb)!=0)
 	{
 	{
@@ -461,16 +495,188 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
-static int sip_trace_store(struct _siptrace_data *sto)
+// Appends x-headers to the message in sto->body containing data from sto
+static int sip_trace_xheaders_write(struct _siptrace_data *sto)
 {
 {
-	db_key_t db_keys[NR_KEYS];
-	db_val_t db_vals[NR_KEYS];
+	if(xheaders_write_flag==NULL || *xheaders_write_flag==0)
+		return 0;
+
+	// Memory for the message with some additional headers.
+	// It gets free()ed in sip_trace_xheaders_free().
+	char* buf = malloc(sto->body.len + XHEADERS_BUFSIZE);
+	if (buf == NULL) {
+		LM_ERR("sip_trace_xheaders_write: out of memory\n");
+		return -1;
+	}
+
+	// Copy the whole message to buf first; it must be \0-terminated for
+	// strstr() to work. Then search for the end-of-header sequence.
+	memcpy(buf, sto->body.s, sto->body.len);
+	buf[sto->body.len] = '\0';
+	char* eoh = strstr(buf, "\r\n\r\n");
+	if (eoh == NULL) {
+		LM_ERR("sip_trace_xheaders_write: malformed message\n");
+		return -1;
+	}
+	eoh += 2; // the first \r\n belongs to the last header => skip it
+
+	// Write the new headers a the end-of-header position. This overwrites
+	// the \r\n terminating the old headers and the beginning of the message
+	// body. Both will be recovered later.
+	int bytes_written = snprintf(eoh, XHEADERS_BUFSIZE,
+		"X-Siptrace-Fromip: %.*s\r\n"
+		"X-Siptrace-Toip: %.*s\r\n"
+		"X-Siptrace-Time: %llu %llu\r\n"
+		"X-Siptrace-Method: %.*s\r\n"
+		"X-Siptrace-Dir: %s\r\n",
+		sto->fromip.len, sto->fromip.s,
+		sto->toip.len, sto->toip.s,
+		(unsigned long long)sto->tv.tv_sec, (unsigned long long)sto->tv.tv_usec,
+		sto->method.len, sto->method.s,
+		sto->dir);
+	if (bytes_written >= XHEADERS_BUFSIZE) {
+		LM_ERR("sip_trace_xheaders_write: string too long\n");
+		return -1;
+	}
+
+	// Copy the \r\n terminating the old headers and the message body from the
+	// old buffer in sto->body.s to the new end-of-header in buf.
+	int eoh_offset = eoh - buf;
+	char* new_eoh = eoh + bytes_written;
+	memcpy(new_eoh, sto->body.s + eoh_offset, sto->body.len - eoh_offset);
+
+	// Change sto to point to the new buffer.
+	sto->body.s = buf;
+	sto->body.len += bytes_written;
+	return 0;
+}
+
+// Parses x-headers, saves the data back to sto, and removes the x-headers
+// from the message in sto->buf
+static int sip_trace_xheaders_read(struct _siptrace_data *sto)
+{
+	if(xheaders_read_flag==NULL || *xheaders_read_flag==0)
+		return 0;
+
+	// Find the end-of-header marker \r\n\r\n
+	char* searchend = sto->body.s + sto->body.len - 3;
+	char* eoh = memchr(sto->body.s, '\r', searchend - eoh);
+	while (eoh != NULL && eoh < searchend) {
+		if (memcmp(eoh, "\r\n\r\n", 4) == 0)
+			break;
+		eoh = memchr(eoh + 1, '\r', searchend - eoh);
+	}
+	if (eoh == NULL) {
+		LM_ERR("sip_trace_xheaders_read: malformed message\n");
+		return -1;
+	}
+
+	// Find x-headers: eoh will be overwritten by \0 to allow the use of
+	// strstr(). The byte at eoh will later be recovered, when the
+	// message body is shifted towards the beginning of the message
+	// to remove the x-headers.
+	*eoh = '\0';
+	char* xheaders = strstr(sto->body.s, "\r\nX-Siptrace-Fromip: ");
+	if (xheaders == NULL) {
+		LM_ERR("sip_trace_xheaders_read: message without x-headers "
+			"from %.*s, callid %.*s\n",
+			sto->fromip.len, sto->fromip.s, sto->callid.len, sto->callid.s);
+		return -1;
+	}
+
+	// Allocate memory for new strings in sto
+	// (gets free()ed in sip_trace_xheaders_free() )
+	sto->fromip.s = malloc(51);
+	sto->toip.s = malloc(51);
+	sto->method.s = malloc(51);
+	sto->dir = malloc(4);
+	if (!(sto->fromip.s && sto->toip.s && sto->method.s && sto->dir)) {
+		LM_ERR("sip_trace_xheaders_read: out of memory\n");
+		goto erroraftermalloc;
+	}
+
+	// Parse the x-headers: scanf()
+	long long unsigned int tv_sec, tv_usec;
+	if (sscanf(xheaders, "\r\n"
+			"X-Siptrace-Fromip: %50s\r\n"
+			"X-Siptrace-Toip: %50s\r\n"
+			"X-Siptrace-Time: %llu %llu\r\n"
+			"X-Siptrace-Method: %50s\r\n"
+			"X-Siptrace-Dir: %3s",
+			sto->fromip.s, sto->toip.s,
+			&tv_sec, &tv_usec,
+			sto->method.s,
+			sto->dir) == EOF) {
+		LM_ERR("sip_trace_xheaders_read: malformed x-headers\n");
+		goto erroraftermalloc;
+	}
+	sto->fromip.len = strlen(sto->fromip.s);
+	sto->toip.len = strlen(sto->toip.s);
+	sto->tv.tv_sec = (time_t)tv_sec;
+	sto->tv.tv_usec = (suseconds_t)tv_usec;
+	sto->method.len = strlen(sto->method.s);
+
+	// Remove the x-headers: the message body is shifted towards the beginning
+	// of the message, overwriting the x-headers. Before that, the byte at eoh
+	// is recovered.
+	*eoh = '\r';
+	memmove(xheaders, eoh, sto->body.len - (eoh - sto->body.s));
+	sto->body.len -= eoh - xheaders;
+
+	return 0;
+
+erroraftermalloc:
+	if (sto->fromip.s) free(sto->fromip.s);
+	if (sto->toip.s) free(sto->toip.s);
+	if (sto->method.s) free(sto->method.s);
+	if (sto->dir) free(sto->dir);
+	return -1;
+}
 
 
+// Frees the memory allocated by sip_trace_xheaders_{write,read}
+static int sip_trace_xheaders_free(struct _siptrace_data *sto)
+{
+	if (xheaders_write_flag != NULL && *xheaders_write_flag != 0) {
+		free(sto->body.s);
+	}
+
+	if (xheaders_read_flag != NULL && *xheaders_read_flag != 0) {
+		free(sto->fromip.s);
+		free(sto->toip.s);
+		free(sto->dir);
+	}
+
+	return 0;
+}
+
+static int sip_trace_store(struct _siptrace_data *sto)
+{
 	if(sto==NULL)
 	if(sto==NULL)
 	{
 	{
 		LM_DBG("invalid parameter\n");
 		LM_DBG("invalid parameter\n");
 		return -1;
 		return -1;
 	}
 	}
+	
+	gettimeofday(&sto->tv, NULL);
+	
+	if (sip_trace_xheaders_read(sto) != 0) return -1;
+	int ret = sip_trace_store_db(sto);
+
+	if (sip_trace_xheaders_write(sto) != 0) return -1;
+	trace_send_duplicate(sto->body.s, sto->body.len);
+
+	if (sip_trace_xheaders_free(sto) != 0) return -1;
+
+	return ret;
+}
+
+static int sip_trace_store_db(struct _siptrace_data *sto)
+{
+	if(trace_to_database_flag==NULL || *trace_to_database_flag==0)
+		goto done;
+	
+	db_key_t db_keys[NR_KEYS];
+	db_val_t db_vals[NR_KEYS];
 
 
 	db_keys[0] = &msg_column;
 	db_keys[0] = &msg_column;
 	db_vals[0].type = DB1_BLOB;
 	db_vals[0].type = DB1_BLOB;
@@ -505,7 +711,7 @@ static int sip_trace_store(struct _siptrace_data *sto)
 	db_keys[6] = &date_column;
 	db_keys[6] = &date_column;
 	db_vals[6].type = DB1_DATETIME;
 	db_vals[6].type = DB1_DATETIME;
 	db_vals[6].nul = 0;
 	db_vals[6].nul = 0;
-	db_vals[6].val.time_val = time(NULL);
+	db_vals[6].val.time_val = sto->tv.tv_sec;
 	
 	
 	db_keys[7] = &direction_column;
 	db_keys[7] = &direction_column;
 	db_vals[7].type = DB1_STRING;
 	db_vals[7].type = DB1_STRING;
@@ -517,11 +723,16 @@ static int sip_trace_store(struct _siptrace_data *sto)
 	db_vals[8].nul = 0;
 	db_vals[8].nul = 0;
 	db_vals[8].val.str_val = sto->fromtag;
 	db_vals[8].val.str_val = sto->fromtag;
 	
 	
-	db_funcs.use_table(db_con, siptrace_get_table());
-	
 	db_keys[9] = &traced_user_column;
 	db_keys[9] = &traced_user_column;
 	db_vals[9].type = DB1_STR;
 	db_vals[9].type = DB1_STR;
 	db_vals[9].nul = 0;
 	db_vals[9].nul = 0;
+	
+	db_keys[10] = &time_us_column;
+	db_vals[10].type = DB1_INT;
+	db_vals[10].nul = 0;
+	db_vals[10].val.int_val = sto->tv.tv_usec;
+	
+	db_funcs.use_table(db_con, siptrace_get_table());
 
 
 	if(trace_on_flag!=NULL && *trace_on_flag!=0) {
 	if(trace_on_flag!=NULL && *trace_on_flag!=0) {
 		db_vals[9].val.str_val.s   = "";
 		db_vals[9].val.str_val.s   = "";
@@ -540,9 +751,6 @@ static int sip_trace_store(struct _siptrace_data *sto)
 	if(sto->avp==NULL)
 	if(sto->avp==NULL)
 		goto done;
 		goto done;
 	
 	
-	trace_send_duplicate(db_vals[0].val.blob_val.s,
-			db_vals[0].val.blob_val.len);
-	
 	db_vals[9].val.str_val = sto->avp_value.s;
 	db_vals[9].val.str_val = sto->avp_value.s;
 
 
 	LM_DBG("storing info...\n");
 	LM_DBG("storing info...\n");

+ 80 - 53
modules_k/textops/README

@@ -66,15 +66,16 @@ Juha Heinanen
               3.23. is_present_hf(hf_name)
               3.23. is_present_hf(hf_name)
               3.24. is_present_hf_re(hf_name_re)
               3.24. is_present_hf_re(hf_name_re)
               3.25. append_time()
               3.25. append_time()
-              3.26. is_method(name)
-              3.27. remove_hf(hname)
-              3.28. remove_hf_re(re)
-              3.29. has_body(), has_body(mime)
-              3.30. is_audio_on_hold()
-              3.31. is_privacy(privacy_type)
-              3.32. cmp_str(str1, str2)
-              3.33. cmp_istr(str1, str2)
-              3.34. starts_with(str1, str2)
+              3.26. append_time_to_request()
+              3.27. is_method(name)
+              3.28. remove_hf(hname)
+              3.29. remove_hf_re(re)
+              3.30. has_body(), has_body(mime)
+              3.31. is_audio_on_hold()
+              3.32. is_privacy(privacy_type)
+              3.33. cmp_str(str1, str2)
+              3.34. cmp_istr(str1, str2)
+              3.35. starts_with(str1, str2)
 
 
         4. Known Limitations
         4. Known Limitations
 
 
@@ -111,15 +112,16 @@ Juha Heinanen
    1.23. is_present_hf usage
    1.23. is_present_hf usage
    1.24. is_present_hf_re usage
    1.24. is_present_hf_re usage
    1.25. append_time usage
    1.25. append_time usage
-   1.26. is_method usage
-   1.27. remove_hf usage
-   1.28. remove_hf_re usage
-   1.29. has_body usage
-   1.30. is_audio_on_hold usage
-   1.31. is_privacy usage
-   1.32. cmp_str usage
+   1.26. append_time_to_request usage
+   1.27. is_method usage
+   1.28. remove_hf usage
+   1.29. remove_hf_re usage
+   1.30. has_body usage
+   1.31. is_audio_on_hold usage
+   1.32. is_privacy usage
    1.33. cmp_str usage
    1.33. cmp_str usage
-   1.34. starts_with usage
+   1.34. cmp_str usage
+   1.35. starts_with usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -161,15 +163,16 @@ Chapter 1. Admin Guide
         3.23. is_present_hf(hf_name)
         3.23. is_present_hf(hf_name)
         3.24. is_present_hf_re(hf_name_re)
         3.24. is_present_hf_re(hf_name_re)
         3.25. append_time()
         3.25. append_time()
-        3.26. is_method(name)
-        3.27. remove_hf(hname)
-        3.28. remove_hf_re(re)
-        3.29. has_body(), has_body(mime)
-        3.30. is_audio_on_hold()
-        3.31. is_privacy(privacy_type)
-        3.32. cmp_str(str1, str2)
-        3.33. cmp_istr(str1, str2)
-        3.34. starts_with(str1, str2)
+        3.26. append_time_to_request()
+        3.27. is_method(name)
+        3.28. remove_hf(hname)
+        3.29. remove_hf_re(re)
+        3.30. has_body(), has_body(mime)
+        3.31. is_audio_on_hold()
+        3.32. is_privacy(privacy_type)
+        3.33. cmp_str(str1, str2)
+        3.34. cmp_istr(str1, str2)
+        3.35. starts_with(str1, str2)
 
 
    4. Known Limitations
    4. Known Limitations
 
 
@@ -234,15 +237,16 @@ From: medabeda
    3.23. is_present_hf(hf_name)
    3.23. is_present_hf(hf_name)
    3.24. is_present_hf_re(hf_name_re)
    3.24. is_present_hf_re(hf_name_re)
    3.25. append_time()
    3.25. append_time()
-   3.26. is_method(name)
-   3.27. remove_hf(hname)
-   3.28. remove_hf_re(re)
-   3.29. has_body(), has_body(mime)
-   3.30. is_audio_on_hold()
-   3.31. is_privacy(privacy_type)
-   3.32. cmp_str(str1, str2)
-   3.33. cmp_istr(str1, str2)
-   3.34. starts_with(str1, str2)
+   3.26. append_time_to_request()
+   3.27. is_method(name)
+   3.28. remove_hf(hname)
+   3.29. remove_hf_re(re)
+   3.30. has_body(), has_body(mime)
+   3.31. is_audio_on_hold()
+   3.32. is_privacy(privacy_type)
+   3.33. cmp_str(str1, str2)
+   3.34. cmp_istr(str1, str2)
+   3.35. starts_with(str1, str2)
 
 
 3.1.  search(re)
 3.1.  search(re)
 
 
@@ -719,7 +723,30 @@ if (is_present_hf_re("^P-")) log(1, "There are headers starting with P-\n");
 append_time();
 append_time();
 ...
 ...
 
 
-3.26.  is_method(name)
+3.26.  append_time_to_request()
+
+   Adds a time header to the request. Header format is: “Date: %a, %d %b
+   %Y %H:%M:%S GMT”, with the legend:
+     * %a abbreviated week of day name (locale)
+     * %d day of month as decimal number
+     * %b abbreviated month name (locale)
+     * %Y year with century
+     * %H hour
+     * %M minutes
+     * %S seconds
+
+   Return true if a header was succesfully appended.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.26. append_time_to_request usage
+...
+if(!is_present_hf("Date"))
+    append_time_to_request();
+...
+
+3.27.  is_method(name)
 
 
    Check if the method of the message matches the name. If name is a known
    Check if the method of the message matches the name. If name is a known
    method (invite, cancel, ack, bye, options, info, update, register,
    method (invite, cancel, ack, bye, options, info, update, register,
@@ -744,7 +771,7 @@ append_time();
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE, and BRANCH_ROUTE.
    FAILURE_ROUTE, and BRANCH_ROUTE.
 
 
-   Example 1.26. is_method usage
+   Example 1.27. is_method usage
 ...
 ...
 if(is_method("INVITE"))
 if(is_method("INVITE"))
 {
 {
@@ -756,7 +783,7 @@ if(is_method("OPTION|UPDATE"))
 }
 }
 ...
 ...
 
 
-3.27.  remove_hf(hname)
+3.28.  remove_hf(hname)
 
 
    Remove from message all headers with name “hname”. Header matching is
    Remove from message all headers with name “hname”. Header matching is
    case-insensitive. Matches and removes also the compact header forms.
    case-insensitive. Matches and removes also the compact header forms.
@@ -769,7 +796,7 @@ if(is_method("OPTION|UPDATE"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.27. remove_hf usage
+   Example 1.28. remove_hf usage
 ...
 ...
 if(remove_hf("User-Agent"))
 if(remove_hf("User-Agent"))
 {
 {
@@ -781,7 +808,7 @@ remove_hf("Contact")
 remove_hf("m")
 remove_hf("m")
 ...
 ...
 
 
-3.28.  remove_hf_re(re)
+3.29.  remove_hf_re(re)
 
 
    Remove from message all headers with name matching regular expression
    Remove from message all headers with name matching regular expression
    “re”
    “re”
@@ -794,7 +821,7 @@ remove_hf("m")
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.28. remove_hf_re usage
+   Example 1.29. remove_hf_re usage
 ...
 ...
 if(remove_hf_re("^P-"))
 if(remove_hf_re("^P-"))
 {
 {
@@ -802,7 +829,7 @@ if(remove_hf_re("^P-"))
 }
 }
 ...
 ...
 
 
-3.29.  has_body(), has_body(mime)
+3.30.  has_body(), has_body(mime)
 
 
    The function returns true if the SIP message has a body attached. The
    The function returns true if the SIP message has a body attached. The
    checked includes also the “Content-Lenght” header presence and value.
    checked includes also the “Content-Lenght” header presence and value.
@@ -817,7 +844,7 @@ if(remove_hf_re("^P-"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.29. has_body usage
+   Example 1.30. has_body usage
 ...
 ...
 if(has_body("application/sdp"))
 if(has_body("application/sdp"))
 {
 {
@@ -825,7 +852,7 @@ if(has_body("application/sdp"))
 }
 }
 ...
 ...
 
 
-3.30.  is_audio_on_hold()
+3.31.  is_audio_on_hold()
 
 
    The function returns true if the SIP message has a body attached and at
    The function returns true if the SIP message has a body attached and at
    least one audio stream in on hold.
    least one audio stream in on hold.
@@ -833,7 +860,7 @@ if(has_body("application/sdp"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.30. is_audio_on_hold usage
+   Example 1.31. is_audio_on_hold usage
 ...
 ...
 if(is_audio_on_hold())
 if(is_audio_on_hold())
 {
 {
@@ -841,7 +868,7 @@ if(is_audio_on_hold())
 }
 }
 ...
 ...
 
 
-3.31.  is_privacy(privacy_type)
+3.32.  is_privacy(privacy_type)
 
 
    The function returns true if the SIP message has a Privacy header field
    The function returns true if the SIP message has a Privacy header field
    that includes the given privacy_type among its privacy values. See
    that includes the given privacy_type among its privacy values. See
@@ -851,7 +878,7 @@ if(is_audio_on_hold())
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.31. is_privacy usage
+   Example 1.32. is_privacy usage
 ...
 ...
 if(is_privacy("id"))
 if(is_privacy("id"))
 {
 {
@@ -859,7 +886,7 @@ if(is_privacy("id"))
 }
 }
 ...
 ...
 
 
-3.32.  cmp_str(str1, str2)
+3.33.  cmp_str(str1, str2)
 
 
    The function returns true if the two parameters matches as string case
    The function returns true if the two parameters matches as string case
    sensitive comparison.
    sensitive comparison.
@@ -867,7 +894,7 @@ if(is_privacy("id"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.32. cmp_str usage
+   Example 1.33. cmp_str usage
 ...
 ...
 if(cmp_str("$rU", "kamailio"))
 if(cmp_str("$rU", "kamailio"))
 {
 {
@@ -875,7 +902,7 @@ if(cmp_str("$rU", "kamailio"))
 }
 }
 ...
 ...
 
 
-3.33.  cmp_istr(str1, str2)
+3.34.  cmp_istr(str1, str2)
 
 
    The function returns true if the two parameters matches as string case
    The function returns true if the two parameters matches as string case
    insensitive comparison.
    insensitive comparison.
@@ -883,7 +910,7 @@ if(cmp_str("$rU", "kamailio"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.33. cmp_str usage
+   Example 1.34. cmp_str usage
 ...
 ...
 if(cmp_istr("$rU@you", "kamailio@YOU"))
 if(cmp_istr("$rU@you", "kamailio@YOU"))
 {
 {
@@ -891,7 +918,7 @@ if(cmp_istr("$rU@you", "kamailio@YOU"))
 }
 }
 ...
 ...
 
 
-3.34.  starts_with(str1, str2)
+3.35.  starts_with(str1, str2)
 
 
    The function returns true if the first string starts with the second
    The function returns true if the first string starts with the second
    string.
    string.
@@ -899,7 +926,7 @@ if(cmp_istr("$rU@you", "kamailio@YOU"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
    FAILURE_ROUTE and BRANCH_ROUTE.
 
 
-   Example 1.34. starts_with usage
+   Example 1.35. starts_with usage
 ...
 ...
 if (starts_with("$rU", "+358"))
 if (starts_with("$rU", "+358"))
 {
 {

+ 56 - 0
modules_k/textops/doc/textops_admin.xml

@@ -966,6 +966,62 @@ append_time();
 		</example>
 		</example>
 	</section>
 	</section>
 
 
+	<section>
+		<title>
+		<function moreinfo="none">append_time_to_request()</function>
+		</title>
+		<para>
+		Adds a time header to the request. Header format is: 
+		<quote>Date: %a, %d %b %Y %H:%M:%S GMT</quote>, with the legend:
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>%a</emphasis> abbreviated week of day name (locale)
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%d</emphasis> day of month as decimal number
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%b</emphasis> abbreviated month name (locale)
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%Y</emphasis> year with century
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%H</emphasis> hour
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%M</emphasis> minutes
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>%S</emphasis> seconds
+			</para>
+		</listitem>
+		</itemizedlist>
+		</para>
+		<para>
+		Return true if a header was succesfully appended.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>append_time_to_request</function> usage</title>
+		<programlisting format="linespecific">
+...
+if(!is_present_hf("Date"))
+    append_time_to_request();
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section>
 	<section>
 		<title>
 		<title>
 		<function moreinfo="none">is_method(name)</function>
 		<function moreinfo="none">is_method(name)</function>

+ 49 - 0
modules_k/textops/textops.c

@@ -118,6 +118,7 @@ static int insert_hf_1(struct sip_msg* msg, char* str1, char* str2);
 static int insert_hf_2(struct sip_msg* msg, char* str1, char* str2);
 static int insert_hf_2(struct sip_msg* msg, char* str1, char* str2);
 static int append_urihf(struct sip_msg* msg, char* str1, char* str2);
 static int append_urihf(struct sip_msg* msg, char* str1, char* str2);
 static int append_time_f(struct sip_msg* msg, char* , char *);
 static int append_time_f(struct sip_msg* msg, char* , char *);
+static int append_time_request_f(struct sip_msg* msg, char* , char *);
 static int set_body_f(struct sip_msg* msg, char*, char *);
 static int set_body_f(struct sip_msg* msg, char*, char *);
 static int set_rpl_body_f(struct sip_msg* msg, char*, char *);
 static int set_rpl_body_f(struct sip_msg* msg, char*, char *);
 static int is_method_f(struct sip_msg* msg, char* , char *);
 static int is_method_f(struct sip_msg* msg, char* , char *);
@@ -253,6 +254,9 @@ static cmd_export_t cmds[]={
 	{"is_audio_on_hold",  (cmd_function)is_audio_on_hold_f, 0,
 	{"is_audio_on_hold",  (cmd_function)is_audio_on_hold_f, 0,
 		0, 0,
 		0, 0,
 		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
 		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"append_time_to_request", (cmd_function)append_time_request_f, 0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
 
 
 	{"bind_textops",      (cmd_function)bind_textops,       0, 0, 0,
 	{"bind_textops",      (cmd_function)bind_textops,       0, 0, 0,
 		0},
 		0},
@@ -1158,6 +1162,51 @@ static int append_time_f(struct sip_msg* msg, char* p1, char *p2)
 	return 1;
 	return 1;
 }
 }
 
 
+static int append_time_request_f(struct sip_msg* msg, char* p1, char *p2)
+{
+	str time_str = {0, 0};
+	time_t now;
+	struct tm *bd_time;
+	struct hdr_field *hf = msg->headers;
+	struct lump *anchor = anchor_lump(msg, hf->name.s + hf->len - msg->buf, 0, 0);
+
+	now=time(0);
+
+	bd_time=gmtime(&now);
+	if (bd_time==NULL) {
+		LM_ERR("gmtime failed\n");
+		goto error;
+	}
+
+	time_str.s = pkg_malloc(MAX_TIME);
+	time_str.len=strftime(time_str.s, MAX_TIME, TIME_FORMAT, bd_time);
+	if (time_str.len>MAX_TIME-2 || time_str.len==0) {
+		LM_ERR("unexpected time length\n");
+		goto error;
+	}
+
+	time_str.s[time_str.len++]='\r';
+	time_str.s[time_str.len++]='\n';
+
+	if (anchor == NULL)
+	{
+		LM_ERR("Problem with getting anchor");
+		goto error;
+	}
+
+	if (insert_new_lump_after(anchor, time_str.s, time_str.len, 0) == 0)
+	{
+		LM_ERR("unable to add lump\n");
+		goto error;
+	}
+
+	return 1;
+error:
+	if (time_str.s != NULL)
+		pkg_free(time_str.s);
+
+	return -1;
+}
 
 
 static int set_body_f(struct sip_msg* msg, char* p1, char* p2)
 static int set_body_f(struct sip_msg* msg, char* p1, char* p2)
 {
 {

+ 31 - 6
parser/msg_parser.c

@@ -825,9 +825,9 @@ void reset_path_vector(struct sip_msg* msg)
 }
 }
 
 
 
 
-struct hdr_field* get_hdr(struct sip_msg *msg, enum _hdr_types_t ht)
+hdr_field_t* get_hdr(sip_msg_t *msg, enum _hdr_types_t ht)
 {
 {
-	struct hdr_field *hdr;
+	hdr_field_t *hdr;
 
 
 	if (msg->parsed_flag & HDR_T2F(ht))
 	if (msg->parsed_flag & HDR_T2F(ht))
 		for(hdr = msg->headers; hdr; hdr = hdr->next) {
 		for(hdr = msg->headers; hdr; hdr = hdr->next) {
@@ -837,12 +837,37 @@ struct hdr_field* get_hdr(struct sip_msg *msg, enum _hdr_types_t ht)
 }
 }
 
 
 
 
-struct hdr_field* next_sibling_hdr(struct hdr_field *hf)
-{	
-	struct hdr_field *hdr;
-	
+hdr_field_t* next_sibling_hdr(hdr_field_t *hf)
+{
+	hdr_field_t *hdr;
+
 	for(hdr = hf->next; hdr; hdr = hdr->next) {
 	for(hdr = hf->next; hdr; hdr = hdr->next) {
 		if(hdr->type == hf->type) return hdr;
 		if(hdr->type == hf->type) return hdr;
 	}
 	}
 	return NULL;
 	return NULL;
 }
 }
+
+hdr_field_t* get_hdr_by_name(sip_msg_t *msg, char *name, int name_len)
+{
+	hdr_field_t *hdr;
+
+	for(hdr = msg->headers; hdr; hdr = hdr->next) {
+		if(hdr->name.len == name_len && *hdr->name.s==*name
+				&& strncmp(hdr->name.s, name, name_len)==0)
+			return hdr;
+	}
+	return NULL;
+}
+
+
+hdr_field_t* next_sibling_hdr_by_name(hdr_field_t *hf)
+{
+	hdr_field_t *hdr;
+
+	for(hdr = hf->next; hdr; hdr = hdr->next) {
+		if(hdr->name.len == hf->name.len && *hdr->name.s==*hf->name.s
+				&& strncmp(hdr->name.s, hf->name.s, hf->name.len)==0)
+			return hdr;
+	}
+	return NULL;
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů