Selaa lähdekoodia

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 14 vuotta sitten
vanhempi
commit
83c9d7187a
100 muutettua tiedostoa jossa 3328 lisäystä ja 603 poistoa
  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 \
 				iptrtpproxy lcr mediaproxy mi_rpc pdb sanity tm topoh \
 				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
 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();
 			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 */
 		case LEN_GT_T:
 			if (a->val[0].type!=NUMBER_ST) {

+ 6 - 0
cfg.lex

@@ -237,6 +237,8 @@ STRIP			"strip"
 STRIP_TAIL		"strip_tail"
 SET_USERPHONE		"userphone"
 APPEND_BRANCH	"append_branch"
+REMOVE_BRANCH	"remove_branch"
+CLEAR_BRANCHES	"clear_branches"
 IF				"if"
 ELSE			"else"
 SET_ADV_ADDRESS	"set_advertised_address"
@@ -623,6 +625,10 @@ IMPORTFILE      "import_file"
 <INITIAL>{STRIP_TAIL}	{ count(); yylval.strval=yytext; return STRIP_TAIL; }
 <INITIAL>{APPEND_BRANCH}	{ count(); yylval.strval=yytext;
 								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;
 								return SET_USERPHONE; }
 <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 SET_USERPHONE
 %token APPEND_BRANCH
+%token REMOVE_BRANCH
+%token CLEAR_BRANCHES
 %token SET_USER
 %token SET_USERPASS
 %token SET_PORT
@@ -3198,6 +3200,17 @@ cmd:
 							NUMBER_ST, (void *)Q_UNSPECIFIED);
 		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 error { $$=0; yyerror("missing '(' or ')' ?"); }
 	| 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;
 				else return -1;
 			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:
 			return -1;
 	}
@@ -154,6 +159,12 @@ int sr_event_exec(int type, void *data)
 					ret = _sr_events_list.pkg_set_real_used(data);
 					return ret;
 				} 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:
 			return -1;
 	}

+ 2 - 0
events.h

@@ -29,6 +29,7 @@
 #define SREV_CFG_RUN_ACTION		4
 #define SREV_PKG_SET_USED		5
 #define SREV_PKG_SET_REAL_USED	6
+#define SREV_NET_DGRAM_IN		7
 
 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 pkg_set_used;
 	sr_event_cb_f pkg_set_real_used;
+	sr_event_cb_f net_dgram_in;
 } sr_event_cb_t;
 
 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">
     <name>htable</name>
-    <version>1</version>
+    <version>2</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <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
@@ -45,7 +45,7 @@
         <name>value_type</name>
         <type>int</type>
         <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 id="attribute">
@@ -56,4 +56,10 @@
         <description>The value of the key</description>
     </column>
 
+    <column id="expires">
+        <name>expires</name>
+        <type>string</type>
+		<default>0</default>
+        <description>The epoch at which the key expires</description>
+    </column>
 </table>

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

@@ -9,7 +9,7 @@
 
 <table id="pua" xmlns:db="http://docbook.org/ns/docbook">
     <name>pua</name>
-    <version>6</version>
+    <version>7</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
         <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>
         <name>call_id</name>
         <type>string</type>
-        <size>&uri_len;</size>
+        <size>&callid_len;</size>
         <description>Call ID</description>
     </column>
 

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

@@ -9,7 +9,7 @@
 
 <table id="rls_presentity" xmlns:db="http://docbook.org/ns/docbook">
     <name>rls_presentity</name>
-    <version>0</version>
+    <version>1</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
         <db:para>Table for the RLS module.
@@ -44,8 +44,8 @@
     <column>
         <name>content_type</name>
         <type>string</type>
-        <size>&domain_len;</size>
-        <description>Event</description>
+        <size>255</size>
+        <description>Content type</description>
     </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">
     <name>sip_trace</name>
-    <version>2</version>
+    <version>3</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <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.
@@ -31,7 +31,14 @@
         <type>datetime</type>
         <default>&DEFAULT_DATETIME;</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 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->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");
 			return -1;

+ 0 - 2
modules/db_postgres/km_pg_con.c

@@ -1,6 +1,4 @@
 /* 
- * $Id$
- *
  * Copyright (C) 2001-2004 iptel.org
  * 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
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -31,13 +27,15 @@
 #ifndef _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.
+ * \ingroup db_postgres
+ * Module: \ref db_postgres
  */
 
 #include "pg_oid.h"
@@ -90,6 +88,7 @@ int pg_cmd(db_cmd_t* cmd);
  * necessary.
  * @param res A pointer to (optional) result structure if the command returns
  *            a result.
+ * @param cmd executed command
  * @retval 0 if executed successfully
  * @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)
@@ -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.
  * @retval 0 If executed successfully.
  * @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);
 
@@ -120,7 +119,7 @@ int pg_cmd_first(db_res_t* res);
  * @param res A result set retrieved from PostgreSQL server.
  * @retval 0 If executed successfully.
  * @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);
 
@@ -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);
 
-/** @} */
-
 #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
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -28,12 +24,12 @@
  * 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"
@@ -250,22 +246,6 @@ static inline void db_int2pg_bit(struct pg_params* dst, int i, db_fld_t* src)
    	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,
 									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;
 }
 
+
 static inline void db_cstr2pg_string(struct pg_params* dst, int i,
 									 db_fld_t* src)
 {
@@ -934,6 +915,3 @@ int pg_pg2fld(db_fld_t* dst, PGresult* src, int row,
 		type, dst[i].type);
 	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
  * Copyright (C) 2003 August.Net Services, LLC
  * Portions Copyright (C) 2005-2008 iptelorg GmbH
@@ -31,13 +27,14 @@
 #ifndef _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"
@@ -75,8 +72,7 @@ struct pg_fld {
  * attaches the structure to the generic db_fld structure.
  * @param fld A generic db_fld structure to be exended.
  * @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);
 
@@ -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.
  * The function converts fields in SER db_fld format to parameters suitable
  * 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.
- * @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 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.
  * @todo Implement support for bit fields with size bigger than 32 
  * @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)
  * into the internal format used in SER. The function converts one row at a
  * 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.
- * @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 types A type conversion table.
  * @param flags Connection flags controlling how values are converted.
  * @retval 0 on success
  * @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 
  * types on the server.
  * 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 types An array used to map internal field types to Oids.
  * @retval 0 on success
  * @retval A negative number on error.
  */
 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);
 
 
-/** @} */
-
 #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
    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.
 
    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
 		holding an 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,
+		constant (0 = ANY, 1 = UDP, 2 = TCP,
 		3 = TLS, 4 = SCTP) or a pseudo variable holding such an
 		integer value.
 		</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_port", con->rcv.dst_port);
 			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),
 									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 */
 				state = "unknown/error";
 				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.36. local_ack_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
 
@@ -1129,7 +1130,30 @@ modparam("tm", "local_ack_mode", 1)
 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
    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.
 
-   Example 38. Set local_cancel_reason parameter
+   Example 39. Set local_cancel_reason parameter
 ...
 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
    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.
 
-   Example 39. Set e2e_cancel_reason parameter
+   Example 40. Set e2e_cancel_reason parameter
 ...
 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
    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)
         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
    negative reply upstream statelessly not to leave upstream UAC in lurch.
 
-   Example 41. t_relay usage
+   Example 42. t_relay usage
 ...
 if (!t_relay())
 {
@@ -1243,7 +1267,7 @@ if (!t_relay())
    Meaning of the parameters is as follows:
      * failure_route - Failure route block to be called.
 
-   Example 42. t_on_failure usage
+   Example 43. t_on_failure usage
 ...
 route {
     t_on_failure("1");
@@ -1269,7 +1293,7 @@ failure_route[1] {
    Meaning of the parameters is as follows:
      * 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"
 ...
@@ -1301,7 +1325,7 @@ es');
    Meaning of the parameters is as follows:
      * branch_route - branch route block to be called.
 
-   Example 44. t_on_branch usage
+   Example 45. t_on_branch usage
 ...
 route {
         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
    difference is that current URI is taken as new entry.
 
-   Example 45. append_branch usage
+   Example 46. append_branch usage
 ...
 set_user("john");
 t_fork();
@@ -1334,7 +1358,7 @@ t_relay();
    the only way a script can add a new transaction in an atomic way.
    Typically, it is used to deploy a UAS.
 
-   Example 46. t_newtran usage
+   Example 47. t_newtran usage
 ...
 if (t_newtran()) {
     log("UAS logic");
@@ -1353,7 +1377,7 @@ if (t_newtran()) {
      * code - Reply code number.
      * reason_phrase - Reason string.
 
-   Example 47. t_reply usage
+   Example 48. t_reply usage
 ...
 t_reply("404", "Not found");
 ...
@@ -1366,7 +1390,7 @@ t_reply("404", "Not found");
    none was found. However this is safely (atomically) done using
    t_newtran.
 
-   Example 48. t_lookup_request usage
+   Example 49. t_lookup_request usage
 ...
 if (t_lookup_request()) {
     ...
@@ -1377,7 +1401,7 @@ if (t_lookup_request()) {
 
    Retransmits a reply sent previously by UAS transaction.
 
-   Example 49. t_retransmit_reply usage
+   Example 50. t_retransmit_reply usage
 ...
 t_retransmit_reply();
 ...
@@ -1387,7 +1411,7 @@ t_retransmit_reply();
    Remove transaction from memory (it will be first put on a wait timer to
    absorb delayed messages).
 
-   Example 50. t_release usage
+   Example 51. t_release usage
 ...
 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.
      * port - Port number.
 
-   Example 51. t_forward_nonack usage
+   Example 52. t_forward_nonack usage
 ...
 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().
 
-   Example 52. t_set_fr usage
+   Example 53. t_set_fr usage
 ...
 route {
         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.
 
-   Example 53. t_reset_fr usage
+   Example 54. t_reset_fr usage
 ...
 route {
 ...
@@ -1478,7 +1502,7 @@ route {
 
    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 {
     if (src_ip=1.2.3.4)
@@ -1500,7 +1524,7 @@ route {
 
    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 {
 ...
@@ -1538,7 +1562,7 @@ route {
 
    See also: retr_timer1, retr_timer2, t_reset_retr().
 
-   Example 56. t_set_retr usage
+   Example 57. t_set_retr usage
 ...
 route {
         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.
 
-   Example 57. t_reset_retr usage
+   Example 58. t_reset_retr usage
 ...
 route {
 ...
@@ -1581,7 +1605,7 @@ route {
 
    See also: auto_inv_100.
 
-   Example 58. t_set_auto_inv_100 usage
+   Example 59. t_set_auto_inv_100 usage
 ...
 route {
 ...
@@ -1595,7 +1619,7 @@ route {
    Returns true if the failure route is executed for a branch that did
    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]{
         if (t_branch_timeout()){
@@ -1610,7 +1634,7 @@ failure_route[0]{
    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.
 
-   Example 60. t_branch_replied usage
+   Example 61. t_branch_replied usage
 ...
 failure_route[0]{
         if (t_branch_timeout()){
@@ -1627,7 +1651,7 @@ failure_route[0]{
    Returns true if at least one of the current transactions branches did
    timeout.
 
-   Example 61. t_any_timeout usage
+   Example 62. t_any_timeout usage
 ...
 failure_route[0]{
         if (!t_branch_timeout()){
@@ -1644,7 +1668,7 @@ failure_route[0]{
    receive some reply in the past. If called from a failure or onreply
    route, the "current" reply is not taken into account.
 
-   Example 62. t_any_replied usage
+   Example 63. t_any_replied usage
 ...
 onreply_route[0]{
         if (!t_any_replied()){
@@ -1658,7 +1682,7 @@ onreply_route[0]{
    Returns true if "code" is the final reply received (or locally
    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]{
         if (t_grep_status("486")){
@@ -1671,7 +1695,7 @@ onreply_route[0]{
 
    Returns true if the current transaction was canceled.
 
-   Example 64. t_is_canceled usage
+   Example 65. t_is_canceled usage
 ...
 failure_route[0]{
         if (t_is_canceled()){
@@ -1685,7 +1709,7 @@ failure_route[0]{
    Returns true if the current transaction has already been expired, i.e.
    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]{
         if (t_is_expired()){
@@ -1706,7 +1730,7 @@ failure_route[0]{
    CANCELs were successfully sent to the pending branches, true if the
    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 (!t_relay_cancel()) {  # implicit drop if relaying was successful,
                                   # nothing to do
@@ -1733,7 +1757,7 @@ if (method == CANCEL) {
    overwritten with the flags of the INVITE. isflagset() can be used to
    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 (t_lookup_cancel()) {
                 log("INVITE transaction exists");
@@ -1763,7 +1787,7 @@ if (method == CANCEL) {
    Dropping replies works only if a new branch is added to the
    transaction, or it is explicitly replied in the script!
 
-   Example 68. t_drop_replies() usage
+   Example 69. t_drop_replies() usage
 ...
 failure_route[0]{
         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
    t_save_lumps().
 
-   Example 69. t_save_lumps() usage
+   Example 70. t_save_lumps() usage
 route {
         ...
         t_newtran();
@@ -1864,7 +1888,7 @@ failure_route[1] {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 70. t_load_contacts usage
+   Example 71. t_load_contacts usage
 ...
 if (!t_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
    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
 # and contacts should be available
@@ -1969,7 +1993,7 @@ Note
 
    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())
         sl_reply("403", "cancel out of the blue forbidden");
 # 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.
 
-   Example 73. t_set_disable_6xx usage
+   Example 74. t_set_disable_6xx usage
 ...
 route {
 ...
@@ -1999,7 +2023,7 @@ route {
 
    See also: use_dns_failover.
 
-   Example 74. t_set_disable_failover usage
+   Example 75. t_set_disable_failover usage
 ...
 route {
 ...
@@ -2030,7 +2054,7 @@ route {
      * hostport - address in "host:port" format. It can be given via an
        AVP.
 
-   Example 75. t_replicate usage
+   Example 76. t_replicate usage
 ...
 # sent to 1.2.3.4:5060 over 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).
           + 0x04 - disable dns failover.
 
-   Example 76. t_replicate usage
+   Example 77. t_replicate usage
 ...
 # sent to 1.2.3.4:5060 over tcp
 t_relay_to("tcp:1.2.3.4:5060");
@@ -2088,7 +2112,7 @@ t_relay_to("0x01");
 
    See also: e2e_cancel_reason.
 
-   Example 77. t_set_no_e2e_cancel_reason usage
+   Example 78. t_set_no_e2e_cancel_reason usage
 ...
 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 */
 	WT_TIME_OUT,	/* wait_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
 	 * 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,
 		"time after which a to-be-deleted transaction currently "
 		"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)"},
-	{"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)"},
 	{"max_inv_lifetime",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, timer_fixup, 0,
 		"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	wait_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_noninv_lifetime;
 	int	noisy_ctimer;

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

@@ -1168,6 +1168,46 @@ modparam("tm", "failure_reply_mode", 3)
 	</example>
 	</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">
 		<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 = 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;
 	tm_xdata_t *x;
 
-	x = &_txdata;
+	if(xd==NULL)
+		x = &_txdata;
+	else
+		x = xd;
 
 	if(mode==0) {
 		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)
 #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;
 
 
@@ -406,8 +405,8 @@ typedef struct cell
 	ticks_t fr_timeout;     /* final response interval for retr_bufs */
 	ticks_t fr_inv_timeout; /* final inv. response interval for retr_bufs */
 #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
 	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
  */
-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
 

+ 6 - 4
modules/tm/t_funcs.h

@@ -169,19 +169,21 @@ int fr_inv_avp2timer(unsigned int* timer);
 #ifdef TIMER_DEBUG
 #define start_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__)
 
 #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
 #define start_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) \
-	_set_fr_retr((rb), RT_T1_TIMEOUT(rb))
+	_set_fr_retr((rb), RT_T1_TIMEOUT_MS(rb))
 
 #endif
 

+ 12 - 7
modules/tm/t_fwd.c

@@ -151,6 +151,8 @@ unsigned int get_on_branch(void)
 
 /* prepare_new_uac flags */
 #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".
@@ -176,7 +178,7 @@ unsigned int get_on_branch(void)
  * @param fsocket - forced send socket for forwarding.
  * @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 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.
  */
@@ -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.len=0;
 		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;
 		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->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) 
 			added_branches |= 1<<branch_ret;
 		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,
 							(dst_uri.len) ? (&dst_uri) : &current_uri,
 							&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;
 		   note that picking lowest error is just as good as
 		   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
-	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
 	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"
  * 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;
 	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;
 	}
-	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",
-				t1_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t1_timeout))); 
+				t1_ms, MAX_UVAR_VALUE(t->rt_t1_timeout_ms)); 
 		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;
 	}
-	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",
-				t2_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t2_timeout))); 
+				t2_ms, MAX_UVAR_VALUE(t->rt_t2_timeout_ms)); 
 		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
 	 * created; if not -> use the static variables */
 	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{
-		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;
 }
@@ -1863,13 +1865,14 @@ int t_reset_retr()
 	 * in REQUEST_ROUTE T will be set only if the transaction was already
 	 * created; if not -> use the static variables */
 	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{
+		 /* change running uac timers */
 		change_retr(t,
 			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;
 }

+ 29 - 8
modules/tm/t_reply.c

@@ -201,6 +201,14 @@ static unsigned short resp_class_prio[]={
 			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)
 {
@@ -1007,18 +1015,26 @@ inline static short int get_4xx_prio(unsigned char xx)
  *  6xx                          1000+xx              (high)
  *  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 xx;
+	int prio;
 	
 	class=resp/100;
 
 	if (class<7){
 		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 best_b, best_s, b;
+	sip_msg_t *rpl;
 
 	best_b=-1; best_s=0;
 	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
+		rpl = t->uac[b].reply;
+
 		/* "fake" for the currently processed 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_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 )
 			return -2;
 		/* 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_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 best_b, best_s, b;
+	sip_msg_t *rpl;
 
 	best_b=-1; best_s=0;
 	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 )
 			return -2;
 		/* 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_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 faked_reply_prio;
+
+extern int tm_rich_redirect;
+ 
 /* has this to-tag been never seen in previous 200/INVs? */
 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;
-
-    /* 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 */
     for(idx = first_idx; (tmp.s = get_branch(idx, &tmp.len, &q, 0, 0, 0, 0))
@@ -272,15 +278,24 @@ rest:
 		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 */
 

+ 49 - 36
modules/tm/timer.c

@@ -150,8 +150,8 @@
 struct msgid_var user_fr_timeout;
 struct msgid_var user_fr_inv_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
 struct msgid_var user_inv_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.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.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_noninv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_noninv_lifetime);
 	/* 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.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.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_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_inv_timeout, default_tm_cfg.fr_inv_timeout, "fr_inv_timer");
 #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
 	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");
@@ -212,8 +212,8 @@ int tm_init_timers(void)
 	memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
 	memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_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
 	memset(&user_inv_max_lifetime, 0, sizeof(user_inv_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",
 			default_tm_cfg.fr_timeout, default_tm_cfg.fr_inv_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);
 	return 0;
 error:
@@ -263,10 +263,6 @@ int timer_fixup(void *handle, str *gname, str *name, void **val)
 	/* size fix checks */
 	IF_IS_TIMER_NAME(fr_timeout, "fr_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_noninv_lifetime")
 
@@ -277,6 +273,30 @@ error:
 	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 ***************************/
 
 
@@ -528,7 +548,8 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 	ticks_t fr_remainder;
 	ticks_t retr_remainder;
 	ticks_t retr_interval;
-	ticks_t new_retr_interval;
+	unsigned long new_retr_interval_ms;
+	unsigned long crt_retr_interval_ms;
 	struct cell *t;
 
 	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 (rbuf->flags & F_RB_RETR_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. */
-				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{
-					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
-				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
 				/* we could race with the reply_received code, but the 
 				 * 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;
 				/* set new interval to -1 on error, or retr_int. on success */
 				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 */
-				tl->data=(void*)(unsigned long)(new_retr_interval);
+				tl->data=(void*)(new_retr_interval_ms);
 			}else{
 				retr_remainder= rbuf->retr_expire-ticks;
 				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
-#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
-#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
 
 #define TM_REQ_TIMEOUT(t) \
@@ -142,8 +142,8 @@
 extern struct msgid_var user_fr_timeout;
 extern struct msgid_var user_fr_inv_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
 extern struct msgid_var user_inv_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
  */
 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 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) \
 	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
  * 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
  */
 #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,
 								unsigned line)
 #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
 {
 	ticks_t timeout;
 	ticks_t ticks;
 	ticks_t eol;
+	ticks_t retr_ticks;
 	int ret;
 	
 	ticks=get_ticks_raw();
 	timeout=rb->my_T->fr_timeout;
 	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)){
 		/* we could have set_fr_retr called in the same time (acceptable 
 		 * 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",
 					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
-	/* 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
 	/* 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) */
@@ -232,10 +236,10 @@ inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
 		return 0;
 	}
 #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);
 #else
-	ret=timer_add(&(rb)->timer, (timeout<retr)?timeout:retr);
+	ret=timer_add(&(rb)->timer, (timeout<retr_ticks)?timeout:retr_ticks);
 #endif
 	if (ret==0) rb->t_active=1;
 	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) \
 	do{ \
 		(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)
 
 
@@ -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
  */
 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;
 
-	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){
 		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
 						 * 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        },
 	{"wt_timer",            PARAM_INT, &default_tm_cfg.wait_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_noninv_lifetime", PARAM_INT, &default_tm_cfg.tm_max_noninv_lifetime},
 	{"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           },
 	{"local_ack_mode",      PARAM_INT, &default_tm_cfg.local_ack_mode        },
 	{"failure_reply_mode",  PARAM_INT, &failure_reply_mode                   },
+	{"faked_reply_prio",    PARAM_INT, &faked_reply_prio                     },
 #ifdef CANCEL_REASON_SUPPORT
 	{"local_cancel_reason", PARAM_INT, &default_tm_cfg.local_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;
 #endif
 	snd_flags_t snd_flags;
+	tm_xdata_t backup_xd;
 
 	ret=-1;
 	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;
 #ifdef TM_DIFF_RT_TIMEOUT
 	/* 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
 
 	set_kr(REQ_FWDED);
@@ -353,7 +354,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 				lreq.rcv.comp=dst.comp;
 			#endif /* USE_COMP */
 				sflag_bk = getsflags();
-				tm_xdata_swap(new_cell, 0);
+				tm_xdata_swap(new_cell, &backup_xd, 0);
 
 				/* run the route */
 				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 );
 
 				/* restore original environment */
-				tm_xdata_swap(new_cell, 1);
+				tm_xdata_swap(new_cell, &backup_xd, 1);
 				setsflagsval(sflag_bk);
 
 				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
  * 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;
 
@@ -720,6 +721,15 @@ int req_outside(uac_req_t *uac_r, str* to, str* from)
 		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);
 
  err:

+ 2 - 2
modules/tm/uac.h

@@ -83,7 +83,7 @@ extern int goto_on_local_req;
  * Function prototypes
  */
 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 (*t_uac_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
  */
-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

+ 9 - 4
modules_k/auth_radius/README

@@ -49,7 +49,7 @@ Jan Janak
 
         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])
 
    List of Examples
@@ -82,7 +82,7 @@ Chapter 1. Admin Guide
 
    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])
 
 1. Overview
@@ -206,10 +206,10 @@ modparam("auth_radius", "use_ruri_flag", 22)
 
 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.1. radius_www_authorize(realm)
+5.1. radius_www_authorize(realm [, uri_user])
 
    The function verifies credentials according to RFC2617. If the
    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
        case of REGISTER requests it is usually hostpart of To URI.
        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.
 

+ 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,
 			 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);
 
 
 /*
- * 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 */

+ 3 - 1
modules_k/auth_radius/authrad_mod.c

@@ -73,7 +73,9 @@ struct extra_attr *auth_extra = 0;
  * Exported functions
  */
 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},
 	{"radius_proxy_authorize", (cmd_function)radius_proxy_authorize_1, 1, auth_fixup,
 			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>
 	<title>Exported Functions</title>
 	<section>
-		<title><function moreinfo="none">radius_www_authorize(realm)</function></title>
+		<title><function moreinfo="none">radius_www_authorize(realm [, uri_user])</function></title>
 		<para>
 		The function verifies credentials according to 
 		<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.
 			</para>
 		</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>
 		<para>
 		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 )
 {
 	dlgb->register_dlgcb = register_dlgcb;
+	dlgb->terminate_dlg = dlg_bye_all;
 	dlgb->set_dlg_var = set_dlg_variable;
 	dlgb->get_dlg_var = get_dlg_variable;
 	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)
 		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);
-	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,
 			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)
 		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)
 		goto error;
 

+ 4 - 1
modules_k/dialog/dlg_load.h

@@ -32,8 +32,12 @@
 #include "dlg_cb.h"
 #include "../../sr_module.h"
 
+/* terminate_dlg function prototype */
+typedef int (*terminate_dlg_f)(struct dlg_cell* dlg, str *hdrs);
+
 struct dlg_binds {
 	register_dlgcb_f  register_dlgcb;
+	terminate_dlg_f terminate_dlg;
     set_dlg_variable_f set_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 );
 int load_dlg( struct dlg_binds *dlgb);
 
-
 static inline int load_dlg_api( struct dlg_binds *dlgb )
 {
 	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);
 
 	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",
 			tl, tl->next, tl->prev);
+		lock_release( d_timer->lock);
 		return -1;
 	}
 	tl->timeout = get_ticks()+interval;
@@ -202,14 +202,13 @@ int update_dlg_timer(struct dlg_tl *tl, int timeout)
 {
 	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;
 	insert_dialog_timer_unsafe( tl );
 

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

@@ -135,6 +135,29 @@
 		</itemizedlist>
 	</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>
 
 </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]>
 
-   Copyright © 2008-2011 http://www.asipto.com
+   Copyright © 2008-2011 http://www.asipto.com
      __________________________________________________________________
 
    Table of Contents
@@ -33,10 +33,12 @@ Elena-Ramona Modroiu
               3.4. key_type_column (str)
               3.5. value_type_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
 
@@ -64,13 +66,15 @@ Elena-Ramona Modroiu
    1.6. Set key_type_column parameter
    1.7. Set value_type_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
 
@@ -91,10 +95,12 @@ Chapter 1. Admin Guide
         3.4. key_type_column (str)
         3.5. value_type_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
 
@@ -130,7 +136,7 @@ Chapter 1. Admin Guide
    You can read more about hash tables at:
    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.
 
    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
    last authentication attempt. To ensure unique name per user, the hash
    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
 ...
@@ -239,10 +245,12 @@ if(is_present_hf("Authorization"))
    3.4. key_type_column (str)
    3.5. value_type_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)
 
@@ -268,7 +276,7 @@ if(is_present_hf("Authorization"))
        database table when the SIP server is stopped (i.e., ensure
        persistency over restarts). Default value is 0 (no write back to db
        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.
 
    Default value is NULL.
@@ -336,58 +344,82 @@ modparam("htable", "value_type_column", "vtype")
 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.
 
    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")
 ...
 
-3.8. fetch_rows (integer)
+3.9. fetch_rows (integer)
 
    How many rows to fetch at once from database.
 
    Default value is 100.
 
-   Example 1.10. Set fetch_rows parameter
+   Example 1.11. Set fetch_rows parameter
 ...
 modparam("htable", "fetch_rows", 1000)
 ...
 
-3.9. timer_interval (integer)
+3.10. timer_interval (integer)
 
    Interval in seconds to check for expired htable values.
 
    Default value is 20.
 
-   Example 1.11. Set timer_interval parameter
+   Example 1.12. Set timer_interval parameter
 ...
 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
    default timer process to check for expired htable values.
 
    Default value is 0.
 
-   Example 1.12. Set timer_mode parameter
+   Example 1.13. Set timer_mode parameter
 ...
 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.1. sht_print()
    4.2. sht_rm_name_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
    purposes.
@@ -395,12 +427,12 @@ modparam("htable", "timer_mode", 1)
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
-   Example 1.13. sht_print usage
+   Example 1.15. sht_print usage
 ...
 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
    expression.
@@ -408,12 +440,12 @@ sht_print();
    This function can be used from REQUEST_ROUTE, FAILURE_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=>.*");
 ...
 
-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
    expression.
@@ -421,7 +453,7 @@ sht_rm_name_re("ha=>.*");
    This function can be used from REQUEST_ROUTE, FAILURE_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=>.*");
 ...
@@ -443,7 +475,7 @@ sht_rm_value_re("ha=>.*");
    6.1. sht_reload
    6.2. sht_dump
 
-6.1.  sht_reload
+6.1. sht_reload
 
    Reload a hash table from database.
 
@@ -456,7 +488,7 @@ sht_rm_value_re("ha=>.*");
                 _hash_table_name_
                 _empty_line_
 
-6.2.  sht_dump
+6.2. sht_dump
 
    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
 
    When defined, the module calls event_route[htable:mod-init] after all
    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>
 		<para>
 			<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>
 		</listitem>
 		</itemizedlist>
@@ -377,6 +377,25 @@ modparam("htable", "value_type_column", "vtype")
 ...
 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>
 		</example>
 	</section>
@@ -456,6 +475,27 @@ modparam("htable", "timer_interval", 10)
 ...
 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>
 		</example>
 	</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_vtype_column  = str_init("value_type");
 str ht_db_value_column  = str_init("key_value");
+str ht_db_expires_column= str_init("expires");
 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)
 {
-	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;
 	db1_res_t* db_res = NULL;
 	str kname;
@@ -135,9 +136,12 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 	int last_ktype;
 	int n;
 	int_str val;
+	int_str expires;
 	int i;
 	int ret;
 	int cnt;
+	int now;
+	int ncols;
 
 	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",
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 	cnt = 0;
+	ncols = 4;
+	if(ht->htexpire > 0 && ht_db_expires_flag!=0)
+		ncols = 5;
 
 	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");
 			return -1;
@@ -176,7 +183,7 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 		}
 	} else {
 		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 )
 		{
 			if( ret==0)
@@ -193,10 +200,10 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 	pname.s = "";
 	n = 0;
 	last_ktype = 0;
+	now = (int)time(NULL);
 	do {
 		for(i=0; i<RES_ROW_N(db_res); i++)
 		{
-			cnt++;
 			/* not NULL values enforced in table definition ?!?! */
 			kname.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
 			if(kname.s==NULL) {
@@ -204,6 +211,18 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode)
 				goto error;
 			}
 			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;
 			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");
 				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(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)
 {
-	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;
 	str tmp;
 	int i;
+	time_t now;
+	int ncols;
 
 	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",
 			ht->name.len, ht->name.s, dbtable->len, dbtable->s);
 
+	now = time(NULL);
+
 	for(i=0; i<ht->htsize; i++)
 	{
 		lock_get(&ht->entries[i].lock);
 		it = ht->entries[i].first;
 		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].nul  = 0;
 			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].nul = 0;
-			db_vals[2].val.int_val = 0;
 
 			db_vals[3].type = DB1_STR;
 			db_vals[3].nul  = 0;
 			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.len = it->value.s.len;
 			} else {
+				db_vals[2].val.int_val = 1;
 				tmp.s = sint2str((long)it->value.n, &tmp.len);
 				db_vals[3].val.str_val.s   = tmp.s;
 				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",
 						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)
-		LM_ERR("failed to detele db records in [%.*s]\n",
+		LM_ERR("failed to delete db records in [%.*s]\n",
 				dbtable->len, dbtable->s);
 	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_vtype_column;
 extern str ht_db_value_column;
+extern str ht_db_expires_column;
 extern str ht_array_size_suffix;
 extern int ht_fetch_rows;
+extern int ht_db_expires_flag;
 
 int ht_db_init_params(void);
 int ht_db_init_con(void);

+ 3 - 0
modules_k/htable/htable.c

@@ -48,6 +48,7 @@
 MODULE_VERSION
 
 int  ht_timer_interval = 20;
+int  ht_db_expires_flag = 0;
 
 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},
 	{"value_type_column",  STR_PARAM, &ht_db_vtype_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},
 	{"fetch_rows",         INT_PARAM, &ht_fetch_rows},
 	{"timer_interval",     INT_PARAM, &ht_timer_interval},
+	{"db_expires",         INT_PARAM, &ht_db_expires_flag},
 	{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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2007-04-04  initial version (anca)
  */
 
-/*! \file
- * \brief Kamailio presence module
- * \ref event_list.h
+/*!
+ * \file
+ * \brief Kamailio presence module :: Events
  * \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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2007-04-05  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Events
  * \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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,18 +22,18 @@
  *  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 
  */
 

+ 6 - 9
modules_k/presence/presence.h

@@ -1,8 +1,4 @@
 /*
- * $Id
- *
- * presence - presence server implementation
- * 
  * Copyright (C) 2006 Voice Sistem SRL
  *
  * This file is part of Kamailio, a free SIP server.
@@ -27,7 +23,8 @@
  *  2006-10-09  first version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Core
  * \ingroup presence 
  */
@@ -46,14 +43,14 @@
 
 /* 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
-/* 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
-/* subscriptions are held only in database */
+/** subscriptions are held only in database */
 #define DB_ONLY 2
 
-/* TM bind */
+/** TM bind */
 extern struct tm_binds tmb;
 
 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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2006-08-15  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Presentity handling
  * \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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2006-08-15  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Presentity handling
- * \ref presentity.c
  * \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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,7 +22,8 @@
  *  2006-08-15  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: Support for PUBLISH handling
  * \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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  2006-08-15  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio presence module :: PUBLISH support
- * \ref publish.c
  * \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
  *
  * This file is part of Kamailio, a free SIP server.
@@ -24,6 +22,11 @@
  *  2007-05-1  initial version (jih)
  */
 
+
+/*!
+ * \defgroup presence_mwi Presence_mwi :: Presence Handling of message-summary events
+ */
+
 /*!
  * \file
  * \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
  *
  * 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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,14 +22,13 @@
  *  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
  */
 
-/*
- *	add 3 events: presence, presence.winfo, dialog;sla
- * */
+
 #include <stdio.h>
 #include <stdlib.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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,9 +22,9 @@
  *  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
  */
 

+ 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.
  *
  * This file is part of Kamailio, a free SIP server.
@@ -26,12 +22,14 @@
  *  2007-04-12  initial version (anca)
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Kamailio Presence_XML :: Core
  * \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.
  *
  * 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)
 { 
-	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;
 
-	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
-		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) &&
 				(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)&&
+				(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->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) )
@@ -338,6 +341,39 @@ ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code)
 	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)
 {
 	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);
 	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));
 	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);
 
 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);
 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"
 
 MODULE_VERSION
-#define PUA_TABLE_VERSION 6
+#define PUA_TABLE_VERSION 7
 
 struct tm_binds tmb;
 htable_t* HashT= NULL;
@@ -749,14 +749,14 @@ static void db_update(unsigned int ticks,void *param)
 	db_key_t db_cols[5];
 	db_val_t q_vals[20], db_vals[5];
 	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 i;
 	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 no_lock= 0, contact_col, desired_expires_col, extra_headers_col;
 	int remote_contact_col, version_col;
-	
+
 	if(ticks== 0 && param == NULL)
 		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].nul = 0;
 	n_query_cols++;
-	
+
 	q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
 	q_vals[pid_col].type = DB1_STR;
 	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[pid_col].val.str_val = p->id;
 					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[totag_col].val.str_val = p->to_tag;
 					q_vals[fromtag_col].val.str_val = p->from_tag;
 					q_vals[cseq_col].val.int_val= p->cseq;
 					q_vals[expires_col].val.int_val = p->expires;
 					q_vals[desired_expires_col].val.int_val = p->desired_expires;
 					q_vals[event_col].val.int_val = p->event;
 					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)
 					{
 						q_vals[record_route_col].val.str_val.s= "";
@@ -1025,8 +1047,15 @@ static void db_update(unsigned int ticks,void *param)
 					}
 					else
 						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)
 					{
 						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)
+					{
+						n_actual_query_cols = n_query_cols;
 						q_vals[extra_headers_col].val.str_val = *(p->extra_headers);
+					}
 					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");
 						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->to_tag= pto->tag_value;
 		hentity->from_tag= pfrom->tag_value;
-	
 	}
 
 	/* extract the other necesary information for inserting a new record */		
@@ -608,6 +607,12 @@ done:
 		run_pua_callbacks( hentity, msg);
 	}
 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)
 	{	
 		shm_free(hentity);
@@ -858,6 +863,7 @@ int send_subscribe(subs_info_t* subs)
 	
 	if(presentity== NULL )
 	{
+		int size;
 insert:
 		lock_release(&HashT->p_records[hash_code].lock); 
 		if(subs->flag & UPDATE_TYPE)
@@ -887,7 +893,7 @@ insert:
 
 		set_uac_req(&uac_r, &met, str_hdr, 0, 0, TMCB_LOCAL_COMPLETED,
 				subs_cback_func, (void*)hentity);
-		result= tmb.t_request
+		result= tmb.t_request_outside
 			(&uac_r,						  /* Type of the message */
 		subs->remote_target?subs->remote_target:subs->pres_uri,/* Request-URI*/
 			subs->pres_uri,				  /* To */
@@ -897,9 +903,74 @@ insert:
 		if(result< 0)
 		{
 			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);
 			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
 	{

+ 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)
 	{
 		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
 	{
@@ -415,7 +415,8 @@ int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id,
 		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",
 				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");
 					goto error;
 				}	
-			}
-			else */	
+			}*/
+			else
 				if((xmlStrcasecmp((unsigned char*)note,
 					(unsigned char*)"dnd")== 0)||
 					(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",
 							BAD_CAST "dnd");
@@ -471,6 +474,8 @@ int build_xmpp_content(str* to_uri, str* from_uri, str* body, str* id,
 						goto error;
 					}		
 				}
+				else
+					LM_DBG("Not Found Status\n");
 
 	
 	/* 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");
 		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");
 				xmlFree(pres_type);
 				goto error;
 		}
-
+	*/
 
 		/* send subscribe after publish because in xmpp subscribe message
 		 * 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)
 	{
 		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");
 				xmlFree(pres_type);
 				goto error;
 		}
+	*/
 
 	}		
 	else
@@ -243,13 +243,13 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 	if(show_cont)
 	{
 		if(strcmp(show_cont, "xa")== 0)
-			status= "not available";
+			status= "Away";
 		else
 			if(strcmp(show_cont, "chat")== 0)
-				status= "free for chat";
+			status= "Online";
 		else
 			if(strcmp(show_cont, "dnd")== 0)
-				status= "do not disturb";
+			status= "Busy (DND)";
 		else
 			status= show_cont;
 	}
@@ -264,24 +264,25 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 			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)
 		{
 			LM_ERR("while adding node\n");
 			goto error;
 		}
-	}else
+	} else {
 		if(show_cont)
 		{
-			node = xmlNewChild(root_node, NULL, BAD_CAST "note", 
+			node = xmlNewChild(tuple_node, NULL, BAD_CAST "note", 
 					BAD_CAST status);
 			if(node== NULL)
 			{
 				LM_ERR("while adding node\n");
 				goto error;
 			}	
-		}	
+		}
+	}
 
 	if(show_cont)
 	{
@@ -295,23 +296,6 @@ str* build_pidf(xmlNodePtr pres_node, char* uri, char* resource)
 				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;
 	str st, st2;
 	pv_value_t v, w;
-	void *vp;
+	time_t t;
 
 	if(val==NULL || (val->flags&PV_VAL_NULL && subtype != TR_S_SQL))
 		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);
 			s[st.len] = '\0';
-			vp = (void*)&val->ri;
+			t = val->ri;
 			val->rs.len = strftime(_tr_buffer, TR_BUFFER_SIZE-1, s,
-					localtime((time_t*)vp));
+			                localtime(&t));
 			pkg_free(s);
 			val->flags = PV_VAL_STR;
 			val->rs.s = _tr_buffer;

+ 114 - 7
modules_k/rls/notify.c

@@ -48,6 +48,8 @@
 #include "../../lib/kcore/hash_func.h"
 #include "rls.h"
 #include "notify.h"
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
 
 typedef struct res_param
 {
@@ -1131,6 +1133,7 @@ int parse_xcap_uri(char *uri, str *host, unsigned short *port, str *path)
 	return 1;
 }
 
+#define MAX_PATH_LEN	127
 int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		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;
 	int xcap_col;
 	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)
 	{
@@ -1151,6 +1160,56 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		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_vals[n_query_cols].type = DB1_STR;
 	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_vals[n_query_cols].type = DB1_STR;
 	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++;
 
 	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,
 				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)
 			rls_dbf.free_result(rls_db, result);
 		return -1;
@@ -1225,19 +1284,67 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		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);
 	return 1;
 
 error:
 	if(result!=NULL)
 		rls_dbf.free_result(rls_db, result);
+	if(xpathObj!=NULL)
+		xmlXPathFreeObject(xpathObj);
+	
+	if(xpathCtx!=NULL)
+		xmlXPathFreeContext(xpathCtx);
 	if(xmldoc!=NULL)
 		xmlFreeDoc(*xmldoc);
 

+ 1 - 1
modules_k/rls/rls.c

@@ -57,7 +57,7 @@
 
 MODULE_VERSION
 
-#define P_TABLE_VERSION 0
+#define P_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
 
 /** 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")
 ...
+</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>
 		</example>
 	</section>
@@ -277,6 +302,66 @@ modparam("siptrace", "trace_local_ip", "10.1.1.1:5064")
 ...
 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>
 		</example>
 	</section>

+ 218 - 10
modules_k/siptrace/siptrace.c

@@ -70,6 +70,7 @@ struct _siptrace_data {
 	str toip;
 	char toip_buff[IP_ADDR_MAX_STR_SIZE+12];
 	char fromip_buff[IP_ADDR_MAX_STR_SIZE+12];
+	struct timeval tv;
 #ifdef STATISTICS
 	stat_var *stat;
 #endif
@@ -86,6 +87,7 @@ static int child_init(int rank);
 static void destroy(void);
 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 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 fromtag_column     = str_init("fromtag");     /* 08 */
 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_on   = 0;
 int trace_sl_acks = 1;
 
+int trace_to_database = 1;
+
+int xheaders_write = 0;
+int xheaders_read = 0;
+
 str    dup_uri_str      = {0, 0};
 struct sip_uri *dup_uri = 0;
 
 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 int_str traced_user_avp;
@@ -165,8 +179,11 @@ static param_export_t params[] = {
 	{"traced_user_avp",    STR_PARAM, &traced_user_avp_str.s},
 	{"trace_table_avp",    STR_PARAM, &trace_table_avp_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_sl_acks",      INT_PARAM, &trace_sl_acks        },
+	{"xheaders_write",     INT_PARAM, &xheaders_write       },
+	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{0, 0, 0}
 };
 
@@ -281,6 +298,23 @@ static int mod_init(void)
 	
 	*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 */
 	if (load_tm_api(&tmb)!=0)
 	{
@@ -461,16 +495,188 @@ error:
 	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)
 	{
 		LM_DBG("invalid parameter\n");
 		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_vals[0].type = DB1_BLOB;
@@ -505,7 +711,7 @@ static int sip_trace_store(struct _siptrace_data *sto)
 	db_keys[6] = &date_column;
 	db_vals[6].type = DB1_DATETIME;
 	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_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].val.str_val = sto->fromtag;
 	
-	db_funcs.use_table(db_con, siptrace_get_table());
-	
 	db_keys[9] = &traced_user_column;
 	db_vals[9].type = DB1_STR;
 	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) {
 		db_vals[9].val.str_val.s   = "";
@@ -540,9 +751,6 @@ static int sip_trace_store(struct _siptrace_data *sto)
 	if(sto->avp==NULL)
 		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;
 
 	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.24. is_present_hf_re(hf_name_re)
               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
 
@@ -111,15 +112,16 @@ Juha Heinanen
    1.23. is_present_hf usage
    1.24. is_present_hf_re 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.34. starts_with usage
+   1.34. cmp_str usage
+   1.35. starts_with usage
 
 Chapter 1. Admin Guide
 
@@ -161,15 +163,16 @@ Chapter 1. Admin Guide
         3.23. is_present_hf(hf_name)
         3.24. is_present_hf_re(hf_name_re)
         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
 
@@ -234,15 +237,16 @@ From: medabeda
    3.23. is_present_hf(hf_name)
    3.24. is_present_hf_re(hf_name_re)
    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)
 
@@ -719,7 +723,30 @@ if (is_present_hf_re("^P-")) log(1, "There are headers starting with P-\n");
 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
    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,
    FAILURE_ROUTE, and BRANCH_ROUTE.
 
-   Example 1.26. is_method usage
+   Example 1.27. is_method usage
 ...
 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
    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,
    FAILURE_ROUTE and BRANCH_ROUTE.
 
-   Example 1.27. remove_hf usage
+   Example 1.28. remove_hf usage
 ...
 if(remove_hf("User-Agent"))
 {
@@ -781,7 +808,7 @@ remove_hf("Contact")
 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
    “re”
@@ -794,7 +821,7 @@ remove_hf("m")
    This function can be used from REQUEST_ROUTE, ONREPLY_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-"))
 {
@@ -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
    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,
    FAILURE_ROUTE and BRANCH_ROUTE.
 
-   Example 1.29. has_body usage
+   Example 1.30. has_body usage
 ...
 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
    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,
    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())
 {
@@ -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
    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,
    FAILURE_ROUTE and BRANCH_ROUTE.
 
-   Example 1.31. is_privacy usage
+   Example 1.32. is_privacy usage
 ...
 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
    sensitive comparison.
@@ -867,7 +894,7 @@ if(is_privacy("id"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
 
-   Example 1.32. cmp_str usage
+   Example 1.33. cmp_str usage
 ...
 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
    insensitive comparison.
@@ -883,7 +910,7 @@ if(cmp_str("$rU", "kamailio"))
    This function can be used from REQUEST_ROUTE, ONREPLY_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"))
 {
@@ -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
    string.
@@ -899,7 +926,7 @@ if(cmp_istr("$rU@you", "kamailio@YOU"))
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
 
-   Example 1.34. starts_with usage
+   Example 1.35. starts_with usage
 ...
 if (starts_with("$rU", "+358"))
 {

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

@@ -966,6 +966,62 @@ append_time();
 		</example>
 	</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>
 		<title>
 		<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 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_request_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 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,
 		0, 0,
 		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,
 		0},
@@ -1158,6 +1162,51 @@ static int append_time_f(struct sip_msg* msg, char* p1, char *p2)
 	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)
 {

+ 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))
 		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) {
 		if(hdr->type == hf->type) return hdr;
 	}
 	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;
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä