Merge branch 'master' into treimann/acc-cdr
authorTimo Reimann <timo.reimann@1und1.de>
Mon, 15 Aug 2011 12:58:20 +0000 (14:58 +0200)
committerTimo Reimann <timo.reimann@1und1.de>
Mon, 15 Aug 2011 12:58:20 +0000 (14:58 +0200)
* 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

139 files changed:
Makefile
action.c
cfg.lex
cfg.y
events.c
events.h
lib/srdb1/schema/htable.xml
lib/srdb1/schema/pr_pua.xml
lib/srdb1/schema/rls_presentity.xml
lib/srdb1/schema/sip_trace.xml
modules/async/async_mod.c
modules/db_postgres/km_pg_con.c
modules/db_postgres/pg_cmd.h
modules/db_postgres/pg_fld.c
modules/db_postgres/pg_fld.h
modules/lcr/README
modules/lcr/doc/lcr_admin.xml
modules/tls/tls_rpc.c
modules/tm/README
modules/tm/config.c
modules/tm/config.h
modules/tm/doc/params.xml
modules/tm/h_table.c
modules/tm/h_table.h
modules/tm/t_funcs.h
modules/tm/t_fwd.c
modules/tm/t_lookup.c
modules/tm/t_reply.c
modules/tm/t_reply.h
modules/tm/t_serial.c
modules/tm/timer.c
modules/tm/timer.h
modules/tm/tm.c
modules/tm/uac.c
modules/tm/uac.h
modules_k/auth_radius/README
modules_k/auth_radius/authorize.c
modules_k/auth_radius/authorize.h
modules_k/auth_radius/authrad_mod.c
modules_k/auth_radius/doc/auth_radius_admin.xml
modules_k/dialog/dialog.c
modules_k/dialog/dlg_hash.c
modules_k/dialog/dlg_load.h
modules_k/dialog/dlg_timer.c
modules_k/dialog/doc/dialog_devel.xml
modules_k/dmq/Makefile [new file with mode: 0644]
modules_k/dmq/bind_dmq.c [new file with mode: 0644]
modules_k/dmq/bind_dmq.h [new file with mode: 0644]
modules_k/dmq/dmq.c [new file with mode: 0644]
modules_k/dmq/dmq.h [new file with mode: 0644]
modules_k/dmq/dmq_funcs.c [new file with mode: 0644]
modules_k/dmq/dmq_funcs.h [new file with mode: 0644]
modules_k/dmq/dmqnode.c [new file with mode: 0644]
modules_k/dmq/dmqnode.h [new file with mode: 0644]
modules_k/dmq/doc/Makefile [new file with mode: 0644]
modules_k/dmq/doc/dmq.xml [new file with mode: 0644]
modules_k/dmq/doc/dmq_admin.xml [new file with mode: 0644]
modules_k/dmq/doc/dmq_devel.xml [new file with mode: 0644]
modules_k/dmq/message.c [new file with mode: 0644]
modules_k/dmq/message.h [new file with mode: 0644]
modules_k/dmq/notification_peer.c [new file with mode: 0644]
modules_k/dmq/notification_peer.h [new file with mode: 0644]
modules_k/dmq/peer.c [new file with mode: 0644]
modules_k/dmq/peer.h [new file with mode: 0644]
modules_k/dmq/worker.c [new file with mode: 0644]
modules_k/dmq/worker.h [new file with mode: 0644]
modules_k/htable/README
modules_k/htable/doc/htable_admin.xml
modules_k/htable/ht_db.c
modules_k/htable/ht_db.h
modules_k/htable/htable.c
modules_k/presence/event_list.c
modules_k/presence/event_list.h
modules_k/presence/presence.c
modules_k/presence/presence.h
modules_k/presence/presentity.c
modules_k/presence/presentity.h
modules_k/presence/publish.c
modules_k/presence/publish.h
modules_k/presence_mwi/presence_mwi.c
modules_k/presence_mwi/presence_mwi.h
modules_k/presence_xml/add_events.c
modules_k/presence_xml/add_events.h
modules_k/presence_xml/presence_xml.c
modules_k/presence_xml/presence_xml.h
modules_k/pua/hash.c
modules_k/pua/hash.h
modules_k/pua/pua.c
modules_k/pua/send_subscribe.c
modules_k/pua_xmpp/simple2xmpp.c
modules_k/pua_xmpp/xmpp2simple.c
modules_k/pv/pv_trans.c
modules_k/rls/notify.c
modules_k/rls/rls.c
modules_k/siptrace/doc/siptrace_admin.xml
modules_k/siptrace/siptrace.c
modules_k/textops/README
modules_k/textops/doc/textops_admin.xml
modules_k/textops/textops.c
parser/msg_parser.c
parser/msg_parser.h
pkg/kamailio/deb/debian/kamailio.default
pkg/kamailio/deb/debian/kamailio.init
pkg/kamailio/deb/lenny/kamailio.default
pkg/kamailio/deb/lenny/kamailio.init
pkg/kamailio/deb/lucid/kamailio.default
pkg/kamailio/deb/lucid/kamailio.init
pkg/kamailio/deb/squeeze/kamailio.default
pkg/kamailio/deb/squeeze/kamailio.init
route_struct.h
udp_server.c
utils/kamctl/db_berkeley/kamailio/htable
utils/kamctl/db_berkeley/kamailio/sip_trace
utils/kamctl/db_berkeley/kamailio/version
utils/kamctl/db_sqlite/htable-create.sql
utils/kamctl/db_sqlite/pipelimit-create.sql
utils/kamctl/db_sqlite/presence-create.sql
utils/kamctl/db_sqlite/rls-create.sql
utils/kamctl/db_sqlite/siptrace-create.sql
utils/kamctl/dbtext/kamailio/htable
utils/kamctl/dbtext/kamailio/sip_trace
utils/kamctl/dbtext/kamailio/version
utils/kamctl/mysql/htable-create.sql
utils/kamctl/mysql/pipelimit-create.sql
utils/kamctl/mysql/presence-create.sql
utils/kamctl/mysql/rls-create.sql
utils/kamctl/mysql/siptrace-create.sql
utils/kamctl/oracle/htable-create.sql
utils/kamctl/oracle/pipelimit-create.sql
utils/kamctl/oracle/presence-create.sql
utils/kamctl/oracle/rls-create.sql
utils/kamctl/oracle/siptrace-create.sql
utils/kamctl/postgres/htable-create.sql
utils/kamctl/postgres/pipelimit-create.sql
utils/kamctl/postgres/presence-create.sql
utils/kamctl/postgres/rls-create.sql
utils/kamctl/postgres/siptrace-create.sql
utils/pdbt/carrier.c
utils/pdbt/pdbt.c

index e24b846..01f8f41 100644 (file)
--- a/Makefile
+++ b/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
index 8ca143a..6d22bf2 100644 (file)
--- a/action.c
+++ b/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) {
diff --git a/cfg.lex b/cfg.lex
index bfcf832..ff35a8a 100644 (file)
--- a/cfg.lex
+++ b/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; }
diff --git a/cfg.y b/cfg.y
index e5679e5..0dc8b74 100644 (file)
--- a/cfg.y
+++ b/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"); }
index fccfd1c..048cb45 100644 (file)
--- a/events.c
+++ b/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;
        }
index fdbcb45..465976f 100644 (file)
--- a/events.h
+++ b/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);
index 8de8ac3..913bcc1 100644 (file)
@@ -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">
         <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>
index 55b4767..79e5986 100644 (file)
@@ -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>
 
index f413de4..fc29c2d 100644 (file)
@@ -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>
index 630f0dc..39f6b58 100644 (file)
@@ -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.
         <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">
index 38374ce..17c7179 100644 (file)
@@ -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;
index 98dbb43..472e8de 100644 (file)
@@ -1,6 +1,4 @@
 /* 
- * $Id$
- *
  * Copyright (C) 2001-2004 iptel.org
  * Copyright (C) 2008 1&1 Internet AG
  *
index 1398d16..c48c31e 100644 (file)
@@ -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
 #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 */
index 685d73b..76ad4df 100644 (file)
@@ -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
  * 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;
 }
-
-
-/** @} */
index 917f22d..09b9926 100644 (file)
@@ -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
 #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 */
index 9b50e80..ebbdb63 100644 (file)
@@ -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
index 57f0ea8..50a0aa8 100644 (file)
@@ -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>
index 3fabcf6..1dfa6ac 100644 (file)
@@ -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);
index 82ba505..f68afef 100644 (file)
@@ -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 {
 ...
index 8a4e456..a3760fe 100644 (file)
@@ -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 "
index b740ec4..0971bc4 100644 (file)
@@ -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;
index 9a67b68..74a7b56 100644 (file)
@@ -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>
index 4870920..aff447d 100644 (file)
@@ -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)
index 2ad42ce..df4f979 100644 (file)
@@ -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
 
index 04ca2d7..1cb3ca7 100644 (file)
@@ -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
 
index 6385812..30558ab 100644 (file)
@@ -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
index b626a1f..397d3a5 100644 (file)
@@ -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;
 }
index e65d1b9..2efaa69 100644 (file)
@@ -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;
                }
index da7e124..1fe81ad 100644 (file)
@@ -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);
 
index 899cb5b..bd5ae27 100644 (file)
@@ -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 */
 
index 349d6a8..b834f11 100644 (file)
 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", 
index e7847d3..0d782c0 100644 (file)
 
 
 #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) \
 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;
                        }
                }
        }
index baf2d81..d9b8bbf 100644 (file)
@@ -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     },
index 0f36a4c..15add04 100644 (file)
@@ -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:
index 2f98aa8..619d43e 100644 (file)
@@ -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
index 3222d50..e40fa57 100644 (file)
@@ -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.
 
index c84deec..9dfc9b3 100644 (file)
@@ -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);
+}
index 33cfb5f..014ed9d 100644 (file)
 
 
 /*
- * 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 */
index 59e27c5..f0ef531 100644 (file)
@@ -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},
index eb99ca8..699d454 100644 (file)
@@ -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.
index 7f029d8..d31135a 100644 (file)
@@ -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;
index 4fb01ed..ed474af 100644 (file)
@@ -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;
 
index 821c946..f26115f 100644 (file)
 #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;
index 03cc5b3..7308eee 100644 (file)
@@ -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 );
 
index f9d6357..f83a05d 100644 (file)
                </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>
diff --git a/modules_k/dmq/Makefile b/modules_k/dmq/Makefile
new file mode 100644 (file)
index 0000000..a361bed
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/bind_dmq.c b/modules_k/dmq/bind_dmq.c
new file mode 100644 (file)
index 0000000..88e02ff
--- /dev/null
@@ -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;
+}
\ No newline at end of file
diff --git a/modules_k/dmq/bind_dmq.h b/modules_k/dmq/bind_dmq.h
new file mode 100644 (file)
index 0000000..f15e673
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/dmq.c b/modules_k/dmq/dmq.c
new file mode 100644 (file)
index 0000000..c8fbff9
--- /dev/null
@@ -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;
+}
\ No newline at end of file
diff --git a/modules_k/dmq/dmq.h b/modules_k/dmq/dmq.h
new file mode 100644 (file)
index 0000000..e4c639c
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/dmq_funcs.c b/modules_k/dmq/dmq_funcs.c
new file mode 100644 (file)
index 0000000..afb50f4
--- /dev/null
@@ -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");
+       }
+}
diff --git a/modules_k/dmq/dmq_funcs.h b/modules_k/dmq/dmq_funcs.h
new file mode 100644 (file)
index 0000000..8ba2503
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/dmqnode.c b/modules_k/dmq/dmqnode.c
new file mode 100644 (file)
index 0000000..25334bc
--- /dev/null
@@ -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;
+}
\ No newline at end of file
diff --git a/modules_k/dmq/dmqnode.h b/modules_k/dmq/dmqnode.h
new file mode 100644 (file)
index 0000000..e2c2437
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/doc/Makefile b/modules_k/dmq/doc/Makefile
new file mode 100644 (file)
index 0000000..d50daab
--- /dev/null
@@ -0,0 +1,4 @@
+docs = dmq.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module
diff --git a/modules_k/dmq/doc/dmq.xml b/modules_k/dmq/doc/dmq.xml
new file mode 100644 (file)
index 0000000..7d9620a
--- /dev/null
@@ -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>bucur_marius_ovidiu@yahoo.com</email>
+               </address>
+           </author>
+           <editor>
+               <firstname>Marius Ovidiu</firstname>
+               <surname>Bucur</surname>
+               <address>
+                   <email>bucur_marius_ovidiu@yahoo.com</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>
+
+
diff --git a/modules_k/dmq/doc/dmq_admin.xml b/modules_k/dmq/doc/dmq_admin.xml
new file mode 100644 (file)
index 0000000..9692d4f
--- /dev/null
@@ -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>
+
diff --git a/modules_k/dmq/doc/dmq_devel.xml b/modules_k/dmq/doc/dmq_devel.xml
new file mode 100644 (file)
index 0000000..afa561b
--- /dev/null
@@ -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>
+
diff --git a/modules_k/dmq/message.c b/modules_k/dmq/message.c
new file mode 100644 (file)
index 0000000..434f80d
--- /dev/null
@@ -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;
+}
\ No newline at end of file
diff --git a/modules_k/dmq/message.h b/modules_k/dmq/message.h
new file mode 100644 (file)
index 0000000..7e0cb95
--- /dev/null
@@ -0,0 +1,2 @@
+
+int handle_dmq_message(struct sip_msg*, char*, char*);
\ No newline at end of file
diff --git a/modules_k/dmq/notification_peer.c b/modules_k/dmq/notification_peer.c
new file mode 100644 (file)
index 0000000..ea12dd8
--- /dev/null
@@ -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;
+}
diff --git a/modules_k/dmq/notification_peer.h b/modules_k/dmq/notification_peer.h
new file mode 100644 (file)
index 0000000..78cf190
--- /dev/null
@@ -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;
\ No newline at end of file
diff --git a/modules_k/dmq/peer.c b/modules_k/dmq/peer.c
new file mode 100644 (file)
index 0000000..39ae836
--- /dev/null
@@ -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);
+}
\ No newline at end of file
diff --git a/modules_k/dmq/peer.h b/modules_k/dmq/peer.h
new file mode 100644 (file)
index 0000000..0d543bd
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/modules_k/dmq/worker.c b/modules_k/dmq/worker.c
new file mode 100644 (file)
index 0000000..cf206f0
--- /dev/null
@@ -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;
+}
\ No newline at end of file
diff --git a/modules_k/dmq/worker.h b/modules_k/dmq/worker.h
new file mode 100644 (file)
index 0000000..61eda09
--- /dev/null
@@ -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
\ No newline at end of file
index 7cd42e2..112adef 100644 (file)
@@ -11,7 +11,7 @@ Elena-Ramona Modroiu
 
    <ramona@rosdev.ro>
 
-   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
index b5c8065..3b14fa3 100644 (file)
@@ -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>
index 66402fe..e1a46b2 100644 (file)
@@ -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;
 }
index 1edb345..40a37c8 100644 (file)
@@ -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);
index 7d387cc..58e9755 100644 (file)
@@ -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}
 };
 
index a8d3eac..0d628b6 100644 (file)
@@ -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 
  */
 
index 5082a73..23fdeba 100644 (file)
@@ -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 
  */
index e97df6c..d211b53 100644 (file)
@@ -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.
  *  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 
  */
 
index 7fb3271..88520f3 100644 (file)
@@ -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 
  */
 
 /* 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;
index 871850b..427a572 100644 (file)
@@ -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 
  */
index 7253f83..2d5e9e5 100644 (file)
@@ -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 
  */
 
index 839ed01..c2738bb 100644 (file)
@@ -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 
  */
index 95bdf84..5960e24 100644 (file)
@@ -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 
  */
 
index fc03c7f..1b2e8f3 100644 (file)
@@ -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.
  *  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
index f77ab8b..6767dd1 100644 (file)
@@ -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.
index 21317cd..347cf24 100644 (file)
@@ -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.
  *  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>
index 4bce3af..3108302 100644 (file)
@@ -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
  */
 
index e3bb8f2..2ab267b 100644 (file)
@@ -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.
  *  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
  */
 
 
index 4f0d007..8678bc3 100644 (file)
@@ -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.
index c5e996c..1a4e67f 100644 (file)
@@ -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)
index 3479792..141296f 100644 (file)
@@ -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);
index c507797..586cafe 100644 (file)
@@ -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)
index 93ae42a..151a4ff 100644 (file)
@@ -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
        {
index 040d36c..e041bcb 100644 (file)
@@ -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 */
index 43fa9a0..d0bdc1e 100644 (file)
@@ -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;
-               }
-
-
        }
                
        
index 7b7b564..43ff05d 100644 (file)
@@ -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;
index dd1e58e..e964520 100644 (file)
@@ -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);
 
index d82e76a..41d845b 100644 (file)
@@ -57,7 +57,7 @@
 
 MODULE_VERSION
 
-#define P_TABLE_VERSION 0
+#define P_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
 
 /** database connection */
index c02ec57..3924f1e 100644 (file)
@@ -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>