Merge branch 'rpc_async'
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 18 Sep 2009 11:16:26 +0000 (13:16 +0200)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 18 Sep 2009 11:16:26 +0000 (13:16 +0200)
* rpc_async:
  tm: async t_uac (t_uac_wait) support
  xmlrpc(s): basic support for delayed replies
  core: rpc capabilities and delayed reply api
  core+tm: moved sip msg clone functions to the core

89 files changed:
Makefile
NEWS
action.c
cfg.lex
cfg.y
cfg/cfg.h
cfg/cfg_ctx.c
cfg/cfg_struct.c
cfg/cfg_struct.h
doc/cfg.txt
etc/sip-router-oob.cfg
forward.c
ip_addr.h
modules/carrierroute/README
modules/carrierroute/carrierroute.c
modules/carrierroute/cr_fifo.c
modules/carrierroute/cr_fixup.c
modules/carrierroute/cr_func.c
modules/carrierroute/cr_func.h
modules/carrierroute/doc/carrierroute_admin.xml
modules/carrierroute/prime_hash.c
modules/carrierroute/prime_hash.h
modules/pdb/Makefile [new file with mode: 0644]
modules/pdb/README [new file with mode: 0644]
modules/pdb/doc/pdb.xml [new file with mode: 0644]
modules/pdb/doc/pdb_admin.xml [new file with mode: 0644]
modules/pdb/pdb.c [new file with mode: 0644]
modules/tm/h_table.c
modules/tm/t_fwd.c
modules/tm/t_lookup.c
modules/tm/uac.c
modules/tm/ut.h
modules_k/domain/README
modules_k/domain/doc/domain_admin.xml
modules_k/domain/domain_mod.c
modules_k/imc/README
modules_k/imc/doc/imc_admin.xml
modules_k/imc/imc.c
modules_k/imc/imc.h
modules_k/imc/imc_cmd.c
modules_k/imc/imc_cmd.h
modules_k/registrar/save.c
modules_k/sl/sl_funcs.c
modules_k/textops/README
modules_k/textops/doc/textops_admin.xml
modules_k/textops/textops.c
modules_k/userblacklist/README
modules_k/userblacklist/db.c
modules_k/userblacklist/doc/userblacklist_admin.xml
modules_k/userblacklist/userblacklist.c
modules_s/iptrtpproxy/README
modules_s/iptrtpproxy/doc/iptrtpproxy.xml
modules_s/iptrtpproxy/examples/ser.cfg
modules_s/iptrtpproxy/iptrtpproxy.c
modules_s/sl/sl_funcs.c
parser/msg_parser.h
parser/parse_to.c
route_struct.h
tcp_conn.h
tcp_main.c
test/unit/2.cfg
utils/pdbt/Makefile [new file with mode: 0644]
utils/pdbt/carrier.c [new file with mode: 0644]
utils/pdbt/carrier.h [new file with mode: 0644]
utils/pdbt/common.h [new file with mode: 0644]
utils/pdbt/debian/NEWS [new file with mode: 0644]
utils/pdbt/debian/changelog [new file with mode: 0644]
utils/pdbt/debian/compat [new file with mode: 0644]
utils/pdbt/debian/control [new file with mode: 0644]
utils/pdbt/debian/copyright [new file with mode: 0644]
utils/pdbt/debian/pdb-server.dirs [new file with mode: 0644]
utils/pdbt/debian/pdb-server.install [new file with mode: 0644]
utils/pdbt/debian/pdb-server.postinst [new file with mode: 0644]
utils/pdbt/debian/pdb-tools.install [new file with mode: 0644]
utils/pdbt/debian/rules [new file with mode: 0755]
utils/pdbt/docs/data_format.txt [new file with mode: 0644]
utils/pdbt/docs/network_protocol.txt [new file with mode: 0644]
utils/pdbt/dt.c [new file with mode: 0644]
utils/pdbt/dt.h [new file with mode: 0644]
utils/pdbt/dtm.c [new file with mode: 0644]
utils/pdbt/dtm.h [new file with mode: 0644]
utils/pdbt/log.c [new file with mode: 0644]
utils/pdbt/log.h [new file with mode: 0644]
utils/pdbt/pdb_server.c [new file with mode: 0644]
utils/pdbt/pdb_server.conf [new file with mode: 0644]
utils/pdbt/pdb_server_backend.c [new file with mode: 0644]
utils/pdbt/pdb_server_backend.h [new file with mode: 0644]
utils/pdbt/pdbt.c [new file with mode: 0644]
utils/pdbt/scripts/get_carrier_names_germany.sh [new file with mode: 0755]

index 7a478c7..b7d2bbe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -180,7 +180,7 @@ module_group_presence=dialog pa presence_b2b rls xcap
 # jabber => expat (library)
 # osp => OSP Toolkit (sipfoundry)
 # sms => none (external modem)
-module_group_stable=cpl-c dbtext jabber osp sms
+module_group_stable=cpl-c dbtext jabber osp sms pdb
 
 # Modules in this group are either not complete, untested, or without enough
 # reports of usage to allow the module into the stable group. They may or may
diff --git a/NEWS b/NEWS
index 9eb07b6..3cec56d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -62,13 +62,30 @@ config script changes:
   - support for kamailio style pvars
   - C-like switch()/case (integer only)
   - while()
+  - include file support: include_file "somefile"
+  - event route support: event_route[module_name:eventid]
+
 build system:
   - multiple modules directories are now supported (defined in Makefile.dirs)
 
 new config variables:
   - max_while_loops - maximum iterations allowed for a while  (can be changed
        at runtime). Default 100.
+  - log_name - set the application name used when printing to syslog.
 
+new script commands:
+  add_local_rport() - adds the rport parameter to the added via header
+       (rfc3581).
+  set_forward_no_connect() - the message will be forwarded only if there is
+       already an existing connection to the destination (it applies only to
+       connection oriented protocols like tcp, tls and in the future sctp).
+  set_reply_no_connect() - like set_forward_no_connect(),  but works for
+       replies to the current message.
+  set_forward_close()  - try to close the connection after forwarding the
+       current message (it applies only when the underlying protocol is
+       connection oriented).
+  set_reply_close() - like set_forward_close(), but it works for replies to
+       the current message.
 
 
 
index 352dd90..13a9817 100644 (file)
--- a/action.c
+++ b/action.c
@@ -50,6 +50,7 @@
  *  2008-12-03  use lvalues/rvalues for assignments (andrei)
  *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
+ *  2009-09-15  added SET_{FWD,RPL}_NO_CONNECT, SET_{FWD,RPL}_CLOSE (andrei)
  */
 
 
@@ -1214,6 +1215,22 @@ match_cleanup:
                        else
                                ret=v;
                        break;
+               case SET_FWD_NO_CONNECT_T:
+                       msg->fwd_send_flags|= SND_F_FORCE_CON_REUSE;
+                       ret=1; /* continue processing */
+                       break;
+               case SET_RPL_NO_CONNECT_T:
+                       msg->rpl_send_flags|= SND_F_FORCE_CON_REUSE;
+                       ret=1; /* continue processing */
+                       break;
+               case SET_FWD_CLOSE_T:
+                       msg->fwd_send_flags|= SND_F_CON_CLOSE;
+                       ret=1; /* continue processing */
+                       break;
+               case SET_RPL_CLOSE_T:
+                       msg->rpl_send_flags|= SND_F_CON_CLOSE;
+                       ret=1; /* continue processing */
+                       break;
 /*
                default:
                        LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
diff --git a/cfg.lex b/cfg.lex
index a5941e4..bedf79c 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
 
 /* start conditions */
 %x STRING1 STRING2 STR_BETWEEN COMMENT COMMENT_LN ATTR SELECT AVP_PVAR PVAR_P 
-%x PVARID INCL
+%x PVARID INCLF
 
 /* config script types : #!SER  or #!KAMAILIO or #!MAX_COMPAT */
 SER_CFG                        SER
@@ -215,12 +215,16 @@ ELSE                      "else"
 SET_ADV_ADDRESS        "set_advertised_address"
 SET_ADV_PORT   "set_advertised_port"
 FORCE_SEND_SOCKET      "force_send_socket"
+SET_FWD_NO_CONNECT             "set_forward_no_connect"
+SET_RPL_NO_CONNECT     "set_reply_no_connect"
+SET_FWD_CLOSE  "set_forward_close"
+SET_RPL_CLOSE  "set_reply_close"
 SWITCH                 "switch"
 CASE                   "case"
 DEFAULT                        "default"
 WHILE                  "while"
 
-INCLUDE         "include"
+INCLUDEFILE     "include_file"
 
 /*ACTION LVALUES*/
 URIHOST                        "uri:host"
@@ -572,12 +576,20 @@ EAT_ABLE  [\ \t\b\r]
                                                                                return SET_ADV_PORT; }
 <INITIAL>{FORCE_SEND_SOCKET}   {       count(); yylval.strval=yytext;
                                                                        return FORCE_SEND_SOCKET; }
+<INITIAL>{SET_FWD_NO_CONNECT}  { count(); yylval.strval=yytext;
+                                                                       return SET_FWD_NO_CONNECT; }
+<INITIAL>{SET_RPL_NO_CONNECT}  { count(); yylval.strval=yytext;
+                                                                       return SET_RPL_NO_CONNECT; }
+<INITIAL>{SET_FWD_CLOSE}               { count(); yylval.strval=yytext;
+                                                                       return SET_FWD_CLOSE; }
+<INITIAL>{SET_RPL_CLOSE}               { count(); yylval.strval=yytext;
+                                                                       return SET_RPL_CLOSE; }
 <INITIAL>{SWITCH}      { count(); yylval.strval=yytext; return SWITCH; }
 <INITIAL>{CASE}        { count(); yylval.strval=yytext; return CASE; }
 <INITIAL>{DEFAULT}     { count(); yylval.strval=yytext; return DEFAULT; }
 <INITIAL>{WHILE}       { count(); yylval.strval=yytext; return WHILE; }
 
-<INITIAL>{INCLUDE}  { count(); BEGIN(INCL); }
+<INITIAL>{INCLUDEFILE}  { count(); BEGIN(INCLF); }
 
 <INITIAL>{URIHOST}     { count(); yylval.strval=yytext; return URIHOST; }
 <INITIAL>{URIPORT}     { count(); yylval.strval=yytext; return URIPORT; }
@@ -1120,8 +1132,8 @@ EAT_ABLE  [\ \t\b\r]
 
 <SELECT>.               { unput(yytext[0]); state = INITIAL_S; BEGIN(INITIAL); } /* Rescan the token in INITIAL state */
 
-<INCL>[ \t]*      /* eat the whitespace */
-<INCL>[^ \t\n]+   { /* get the include file name */
+<INCLF>[ \t]*      /* eat the whitespace */
+<INCLF>[^ \t\n]+   { /* get the include file name */
                                if(sr_push_yy_state(yytext)<0)
                                        exit(-1);
                                BEGIN(INITIAL);
diff --git a/cfg.y b/cfg.y
index 3344b03..af5335c 100644 (file)
--- a/cfg.y
+++ b/cfg.y
@@ -319,6 +319,10 @@ extern char *finame;
 %token SET_ADV_ADDRESS
 %token SET_ADV_PORT
 %token FORCE_SEND_SOCKET
+%token SET_FWD_NO_CONNECT
+%token SET_RPL_NO_CONNECT
+%token SET_FWD_CLOSE
+%token SET_RPL_CLOSE
 %token SWITCH
 %token CASE
 %token DEFAULT
@@ -2990,6 +2994,30 @@ cmd:
                $$=0; yyerror("bad argument, [proto:]host[:port] expected");
        }
        | FORCE_SEND_SOCKET error {$$=0; yyerror("missing '(' or ')' ?"); }
+       | SET_FWD_NO_CONNECT LPAREN RPAREN      {
+               $$=mk_action(SET_FWD_NO_CONNECT_T, 0); set_cfg_pos($$);
+       }
+       | SET_FWD_NO_CONNECT    {
+               $$=mk_action(SET_FWD_NO_CONNECT_T, 0); set_cfg_pos($$);
+       }
+       | SET_RPL_NO_CONNECT LPAREN RPAREN      {
+               $$=mk_action(SET_RPL_NO_CONNECT_T, 0); set_cfg_pos($$);
+       }
+       | SET_RPL_NO_CONNECT    {
+               $$=mk_action(SET_RPL_NO_CONNECT_T, 0); set_cfg_pos($$);
+       }
+       | SET_FWD_CLOSE LPAREN RPAREN   {
+               $$=mk_action(SET_FWD_CLOSE_T, 0); set_cfg_pos($$);
+       }
+       | SET_FWD_CLOSE {
+               $$=mk_action(SET_FWD_CLOSE_T, 0); set_cfg_pos($$);
+       }
+       | SET_RPL_CLOSE LPAREN RPAREN   {
+               $$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
+       }
+       | SET_RPL_CLOSE {
+               $$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
+       }
        | ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST,
                        0); } LPAREN func_params RPAREN {
                mod_func_action->val[0].u.data = 
index ca511a5..4a77233 100644 (file)
--- a/cfg/cfg.h
+++ b/cfg/cfg.h
@@ -55,6 +55,8 @@
 #define CFG_ATOMIC             (1U<<(2*CFG_INPUT_SHIFT))
 /* variable is read-only */
 #define CFG_READONLY           (1U<<(2*CFG_INPUT_SHIFT+1))
+/* per-child process callback needs to be called only once */
+#define CFG_CB_ONLY_ONCE       (1U<<(2*CFG_INPUT_SHIFT+2))
 
 typedef int (*cfg_on_change)(void *, str *, str *, void **);
 typedef void (*cfg_on_set_child)(str *, str *);
index ef3661c..a504588 100644 (file)
@@ -317,7 +317,8 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                s2.s = var->def->name;
                s2.len = var->name_len;
                child_cb = cfg_child_cb_new(&s, &s2,
-                                       var->def->on_set_child_cb);
+                                       var->def->on_set_child_cb,
+                                       var->def->type);
                if (!child_cb) {
                        LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
                        goto error0;
@@ -749,7 +750,8 @@ int cfg_commit(cfg_ctx_t *ctx)
                        s2.s = changed->var->def->name;
                        s2.len = changed->var->name_len;
                        child_cb = cfg_child_cb_new(&s, &s2,
-                                       changed->var->def->on_set_child_cb);
+                                       changed->var->def->on_set_child_cb,
+                                       changed->var->def->type);
                        if (!child_cb) goto error0;
 
                        if (child_cb_last)
index c90b12e..ad613ee 100644 (file)
@@ -319,7 +319,7 @@ int sr_cfg_init(void)
        This stucture will be the entry point for the child processes, and
        will be freed later, when none of the processes refers to it */
        *cfg_child_cb_first = *cfg_child_cb_last =
-               cfg_child_cb_new(NULL, NULL, NULL);
+               cfg_child_cb_new(NULL, NULL, NULL, 0);
 
        if (!*cfg_child_cb_first) goto error;
 
@@ -591,7 +591,9 @@ void cfg_install_global(cfg_block_t *block, char **replaced,
 }
 
 /* creates a structure for a per-child process callback */
-cfg_child_cb_t *cfg_child_cb_new(str *gname, str *name, cfg_on_set_child cb)
+cfg_child_cb_t *cfg_child_cb_new(str *gname, str *name,
+                       cfg_on_set_child cb,
+                       unsigned int type)
 {
        cfg_child_cb_t  *cb_struct;
 
@@ -612,6 +614,22 @@ cfg_child_cb_t *cfg_child_cb_new(str *gname, str *name, cfg_on_set_child cb)
        cb_struct->cb = cb;
        atomic_set(&cb_struct->refcnt, 0);
 
+       if (type & CFG_CB_ONLY_ONCE) {
+               /* The callback needs to be executed only once.
+                * Set the cb_count value to 1, so the first child
+                * process that executes the callback will decrement
+                * it to 0, and no other children will execute the
+                * callback again.
+                */
+               atomic_set(&cb_struct->cb_count, 1);
+       } else {
+               /* Set the cb_count to a high value, i.e. max signed integer,
+                * so all the child processes will execute the callback,
+                * the counter will never reach 0.
+                */
+               atomic_set(&cb_struct->cb_count, (1U<<(sizeof(int)*8-1))-1);
+       }
+
        return cb_struct;
 }
 
index f0c2253..e7ac4e3 100644 (file)
@@ -98,6 +98,12 @@ typedef struct _cfg_block {
 typedef struct _cfg_child_cb {
        atomic_t                refcnt; /* number of child processes
                                        referring to the element */
+       atomic_t                cb_count;       /* This counter is used to track
+                                                * how many times the callback needs
+                                                * to be executed.
+                                                * >0 the cb needs to be executed
+                                                * <=0 the cb no longer needs to be executed
+                                                */
        str                     gname, name;    /* name of the variable that has changed */
        cfg_on_set_child        cb;     /* callback function that has to be called */
 
@@ -247,8 +253,11 @@ static inline void cfg_update_local(void)
                                CFG_UNLOCK();
                        }
                }
-               /* execute the callback */
-               cfg_child_cb->cb(&cfg_child_cb->gname, &cfg_child_cb->name);
+               if (atomic_add(&cfg_child_cb->cb_count, -1) >= 0) /* the new value is returned
+                                                               by atomic_add() */
+                       /* execute the callback */
+                       cfg_child_cb->cb(&cfg_child_cb->gname, &cfg_child_cb->name);
+               /* else the callback no longer needs to be executed */
        }
 }
 
@@ -296,7 +305,9 @@ void cfg_install_global(cfg_block_t *block, char **replaced,
                        cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last);
 
 /* creates a structure for a per-child process callback */
-cfg_child_cb_t *cfg_child_cb_new(str *gname, str *name, cfg_on_set_child cb);
+cfg_child_cb_t *cfg_child_cb_new(str *gname, str *name,
+                       cfg_on_set_child cb,
+                       unsigned int type);
 
 /* free the memory allocated for a child cb list */
 void cfg_child_cb_free(cfg_child_cb_t *child_cb_first);
index 9b306ea..e7be888 100644 (file)
@@ -120,6 +120,17 @@ Each row consists of the following items:
        - CFG_READONLY          The variable is read-only, its value cannot
                                be changed.
 
+       - CFG_CB_ONLY_ONCE      The per-child process callback is called only once
+                               after the changes to the global config have been
+                               committed. (The first child process that updates
+                               its local config calls the callback, and no other child
+                               process does so.)
+                               The per-child process cb is intended to be used to
+                               update the config variables that are stored outside
+                               of the cfg framework. By default this callback is
+                               called by all the child processes separately,
+                               this can be changed with this flag.
+
 - minimum value for integers (optional)
 - maximum value for integers (optional)
 - fixup function (optional) that is called when the variable is going to be
@@ -132,7 +143,8 @@ Each row consists of the following items:
   typedef int (*cfg_on_change)(void *temp_handle, str *group_name, str *var_name, void **value);
 
 - per-child process callback function (optional) that is called by each child
-  process separately, after the new values have been committed, and the
+  process separately (unless the CFG_CB_ONLY_ONCE flag is set, see above)
+  after the new values have been committed, and the
   child process is updating its local configuration. The old value will no
   longer be used by the process. (Useful for fix-ups that cannot be done
   in shm memory, for example regexp compilation.)
index 27b1f7d..55f6ffc 100644 (file)
@@ -146,7 +146,7 @@ rev_dns=no              # (cmd. line: -R)
 #user=sip-router
 #group=sip-router
 #disable_core=yes       # disables core dumping
-open_files_limit=20480  # sets the open file descriptors limit
+#open_files_limit=20480  # sets the open file descriptors limit
 #mhomed=yes             # usefull for multihomed hosts, small performance
                         # penalty
 disable_tcp=no          # be conservative about enabling TCP -- it can
@@ -195,12 +195,12 @@ dns_cache_min_ttl=60
 dns_cache_max_ttl=86400 # 1 day
 dns_cache_mem=2048 # 2 MB
 dns_cache_gc_interval=60  # garbage collection every minute
-# sip-router 2.1 specific options
-dns_try_naptr=yes
-dns_srv_lb=yes  # srv based load balancing
-dns_udp_pref=3  # prefer udp (when resolving naptr record)
-dns_tcp_pref=2  # if no udp availbale accept tcp (for naptr)
-dns_tls_pref=-1 # ignore / don't accept tls (for naptr)
+# ser 2.1 specific options
+dns_try_naptr=yes
+dns_srv_lb=yes  # srv based load balancing
+dns_udp_pref=3  # prefer udp (when resolving naptr record)
+dns_tcp_pref=2  # if no udp availbale accept tcp (for naptr)
+dns_tls_pref=-1 # ignore / don't accept tls (for naptr)
 # dns_cache_delete_nonexpired=no
 
 # ------------------- Blacklist Parameters ----------------------------------
@@ -223,6 +223,9 @@ tcp_connect_timeout=1
 
 # Enable TLS hooks so that the TLS module can be used
 tls_enable=yes
+# This option is required if you want to use TLS as the TLS
+# module does not support the new async TCP mode yet
+tcp_async=no
 
 # -------------------- Custom Parameters ------------------------------------
 # These parameters can be modified runtime via RPC interface,
@@ -289,6 +292,7 @@ loadmodule "db_ops"
 loadmodule "exec"
 loadmodule "cfg_rpc"
 loadmodule "eval"
+#loadmodule "enum"
 #loadmodule "tls"
 
 # ----------------- Declaration of Script Flags -----------------------------
@@ -403,7 +407,7 @@ modparam("auth", "secret", "aqwedrftredswqwddcft")
 modparam("rr", "enable_full_lr", 1)
 
 # Limit the length of the AVP cookie to necessary attributes only
-modparam("rr", "cookie_filter", "(account|uac_nat|stimer)")
+modparam("rr", "cookie_filter", "(account|rproxy|stimer)")
 
 # You probably do not want that someone can simply read and change
 # the AVP cookie in your Routes, thus should really change this
@@ -539,7 +543,7 @@ route
        route(CATCH_CANCEL);
 
        # Check if the request is routed via Route header.
-       route(LOOSE_ROUTE);
+       route(PROCESS_ROUTES);
 
        # Look up domain IDs
        route(DOMAIN);
@@ -609,20 +613,20 @@ route[FORWARD]
        # Always use the reply route to check for NATed UAS.
        t_on_reply("REPLY_ROUTE");
 
-       # Insert a Record-Route header into all requests.
-       # This has to be done as one of the last steps to include all the
-       # RR cookies which might have been created during the script run.
-       route(RR);
-
-       # Activate the RTP proxy as the last step because it modifies the
-       # body.
-       route(RTPPROXY);
-
        # Remove credentials to keep requests shorter
        if (isflagset(FLAG_AUTH_OK) && !isflagset(FLAG_DONT_RM_CRED) ) {
                consume_credentials();
        }
 
+       # Activate the RTP proxy as the second last step because it modifies the
+       # body but also sets an dialog AVP cookie.
+       route(RTPPROXY);
+
+       # Insert a Record-Route header into all requests.
+       # This has to be done as one of the last steps to include all the
+       # RR cookies which might have been created during the script run.
+       route(RECORD_ROUTE);
+
        # Send it out now.
        if (!t_relay()) {
                if (isflagset(FLAG_FAILUREROUTE)) {
@@ -652,13 +656,14 @@ route[INIT]
                drop;
        }
 
-       # Set flag for use in the onsend route
+       # Set flag for use in the onsend route (because it does not
+       # allow to use "select" statements)
        if (@to.tag != "") {
                setflag(FLAG_TOTAG);
        }
 
        # Check if the UAC is NATed and fix the message accordingly
-       route(NAT_DETECTION);
+       route(UAC_NAT_DETECTION);
 
        # Activate accounting for all initial INVITEs. In-dialog requests
        # are accounted by a RR cookie (see below).
@@ -690,14 +695,20 @@ route[OPTIONS_REPLY]
 
 
 # Check if the sender of the request is behind a NAT device. If so,
-# fix the request so that other devices can talk to the sender nonetheless.
+# fix the request so that other devices can talk to the sender none the less.
 #
-route[NAT_DETECTION]
+route[UAC_NAT_DETECTION]
 {
        # Lots of UAs do not include the rport parameter in there Via
        # header, so we put it there regardless.
        force_rport();
-       force_tcp_alias();
+
+       # If a reliable transport was used store the connection internally
+       # so that SERs core can re-use the connection later.
+       if (proto==TCP || proto == TLS)
+       {
+               force_tcp_alias();
+       }
 
        # Check if the request contains hints for a NATed UAC. Also, try to
        # rewrite contacts using maddr. Using maddr is a really dubious
@@ -712,12 +723,26 @@ route[NAT_DETECTION]
        # own.  Should you encounter such a case, a possible solution
        # would be to store the original information as a contact parameter
        # and restore it on its way back.
-       if (nat_uac_test("19")
-           || (@hf_value["contact"] != "" && @contact.uri.params.maddr != ""))
+
+       # In case of UDP we test for
+       #  - private IPs in Contact
+       #  - mismatch of transport IP and IP in Via
+       #  - mismatch of transport port and port in Via
+       # in all other cases we skip the port test, because lots of clients
+       # do not correctly advertise their emphemeral port number in their Via
+       # header in case of reliable transports (although they are not behind
+       # a NAT).
+
+       # Warning: if you are dealing with SIP implementations which are
+       # running on public IP and do as-symmertic signaling for whatever
+       # reason the following check will make their signaling symmetric.
+       # If you need to support as-symmertic signaling reduce the following
+       # nat_uac_test for UDP to "3" or even "1".
+       if ((proto == UDP && nat_uac_test("19")) ||
+               (nat_uac_test("3")) ||
+               (@hf_value["contact"] && @contact.uri.params.maddr))
        {
                setflag(FLAG_NAT);
-               $uac_nat = 1;
-               setavpflag($uac_nat, "dialog_cookie");
                if (method == "REGISTER") {
                        # Prepare the Contact so that the registrar module
                        # saves the source address and port as well.
@@ -732,6 +757,46 @@ route[NAT_DETECTION]
 }
 
 
+# Check if the receiver of the request is behind a NAT device. If so,
+# fix the Contact header to allow proper routing of in-dialog requests.
+route[UAS_NAT_DETECTION]
+{
+       # Fix the Contact in the reply if it contains a private IP to
+       # allow proper routing of in-dialog messages.
+       # Do the same if the contact is maddred.
+
+       # But skip 3XX responses, because we do not know the right IP for that,
+       # even if they contain private IPs.
+       if (status=~"(3[0-9][0-9])") {
+               break;
+       }
+
+       # Prevent that we over-write the Contact with the IP of our proxy when
+       # the reply loops through ourself.
+       if (src_ip == myself) {
+               break;
+       }
+
+       # In this case we check only if the Contact URI contains a private
+       # IP, because the Via header contains only informations from the UAC.
+       # Additionally we check if the port in the Contact URI differs from
+       # the port of the transport to catch UAS or ALG which put the public
+       # IP address into the Contact header, but "forget" about the port.
+
+       # Warning: if you are dealing with SIP implementations which are
+       # running on public IP and do as-symmertic signaling for whatever
+       # reason the following check will make their signaling symmetric.
+       # If you need to support as-symmertic signaling reduce the following
+       # nat_uac_test for UDP to just "1".
+       if ( (proto == UDP && nat_uac_test("33")) ||
+               (nat_uac_test("1") ||
+               (@hf_value["contact"] && @contact.uri.params.maddr)))
+       {
+               fix_nated_contact();
+       }
+}
+
+
 # Activates RTP proxy if necessary.
 #
 route[RTPPROXY]
@@ -750,29 +815,32 @@ route[RTPPROXY]
                break;
        } # else rtp proxy is permanently enabled
 
-       # If the message terminates a dialog turn RTP proxy off.
-       if (method == "BYE" || method == "CANCEL") {
+       # If the message terminates a dialog for which the RTP proxy 
+       # was turned on, turn it off again.
+       if ((method == "BYE" && isflagset(FLAG_RTP_PROXY)) ||
+               (method == "CANCEL")) {
                unforce_rtp_proxy();
-               append_hf("P-RTP-Proxy: UNFORCED\r\n");
+               append_hf("P-RTP-Proxy: Off\r\n");
                break;
        }
 
-       # Turn the RTP proxy on for INVITEs and UPDATEs.
+       # Turn the RTP proxy on for INVITEs and UPDATEs, if they 
+       # have a body
        if (((method=="INVITE" || method == "UPDATE") && @msg.body != "")
            && !isflagset(FLAG_RTP_PROXY))
        {
                force_rtp_proxy('r');
-               append_hf("P-RTP-Proxy: YES\r\n");
+               append_hf("P-RTP-Proxy: On\r\n");
                setflag(FLAG_RTP_PROXY);
+               $rproxy = 1;
+               setavpflag($rproxy, "dialog_cookie");
        }
 }
 
 
-# Handling of loose routed requests
+# Handling of Route headers
 #
-#    XXX Isn't the proper term "record routed"? This route also handles
-#        strict routed requests, doesn't it? -- martinh
-route[LOOSE_ROUTE]
+route[PROCESS_ROUTES]
 {
        # subsequent messages withing a dialog should take the
        # path determined by the Route headers.
@@ -791,9 +859,9 @@ route[LOOSE_ROUTE]
                        setflag(FLAG_ACC);
                }
 
-               # Restore the NAT flag if present
-               if ($uac_nat == 1) {
-                       setflag(FLAG_NAT);
+               # Restore the RTP proxy flag if present
+               if ($rproxy == "1") {
+                       setflag(FLAG_RTP_PROXY);
                }
 
                # Restore Session Timer flag and headers.
@@ -814,7 +882,7 @@ route[LOOSE_ROUTE]
                # out-of-dialog requests. Some in-dialog requests can't be
                # authenticated at all, see the call-forwarding example in
                # route[DOMAIN].
-               route(RR);
+               route(RECORD_ROUTE);
 
                route(FORWARD);
        }
@@ -823,7 +891,7 @@ route[LOOSE_ROUTE]
 
 # Add a Record-Route header
 #
-route[RR]
+route[RECORD_ROUTE]
 {
        if (!isflagset(FLAG_RR_DONE) && method != "REGISTER") {
                # We record-route all messages to make sure that
@@ -901,7 +969,6 @@ route[REGISTRAR]
                # Read marker from master
                if (search("^Repl-Marker: nated")) {
                        setflag(FLAG_NAT);
-                       $uac_nat = 1;
                }
 
                # If the replicating server added its own server id to the
@@ -1363,6 +1430,21 @@ route[SESSION_TIMER]
        }
 }
 
+# Route which checks and performs ENUM queries
+# #
+route[ENUM]
+{
+       # perform ENUM query only if the RURI contains an E.164
+       # number as uer part
+       if (uri =~ "sip:\+[0-9]?@") {
+               # if the ENUM query was successful send it right
+               # away of to the new target, otherwise do nothing
+               if (enum_query()) {
+                       route(FORWARD);
+               }
+       }
+}
+
 
 # Failure route for initial INVITEs.
 #
@@ -1406,15 +1488,9 @@ failure_route[FAILURE_ROUTE]
 #
 onreply_route[REPLY_ROUTE]
 {
-       # Fix the Contact in the reply if it contains a private IP to
+       # Check and fix the Contact in the reply to
        # allow proper routing of in-dialog messages.
-       # Do the same if the contact is maddred. See the notes in route
-       # [NAT_DETECTION] for more information.
-       if (nat_uac_test("1") ||
-           (@hf_value["contact"] != "" && @contact.uri.params.maddr != ""))
-       {
-               fix_nated_contact();
-       }
+       route(UAS_NAT_DETECTION);
 
        # If RTP proxy was activated and this is a 18x or 2xx reply with a
        # body, inform RTP proxy.
index dea7584..d7fb827 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -384,9 +384,12 @@ int check_self_port(unsigned short port, unsigned short proto)
  *               default port or non srv. lookup is desired, the port must
  *               be !=0 
  *   port      - used only if dst!=0 (else the port in send_info->to is used)
- *   send_info - filled dest_info structure:
- *               if the send_socket member is null, a send_socket will be 
- *               chosen automatically
+ *   send_info - value/result partially filled dest_info structure:
+ *                 - send_info->proto and comp are used
+ *                 - send_info->to will be filled (dns)
+ *                 - send_info->send_flags is filled from the message
+ *                 - if the send_socket member is null, a send_socket will be 
+ *                   chosen automatically
  * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
  * random id along with proto==PROTO_TCP can have bad consequences, same for
  *   a bogus send_socket value)
@@ -438,13 +441,14 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                        goto error;
                }
        }/* dst */
+       send_info->send_flags=msg->fwd_send_flags;
        /* calculate branch for outbound request;  if syn_branch is turned off,
           calculate is from transaction key, i.e., as an md5 of From/To/CallID/
           CSeq exactly the same way as TM does; good for reboot -- than messages
           belonging to transaction lost due to reboot will still be forwarded
           with the same branch parameter and will be match-able downstream
-
-       if it is turned on, we don't care about reboot; we simply put a simple
+       
+          if it is turned on, we don't care about reboot; we simply put a simple
           value in there; better for performance
        */
        if (syn_branch ) {
@@ -694,6 +698,7 @@ int forward_reply(struct sip_msg* msg)
        }
 
        dst.proto=msg->via2->proto;
+       dst.send_flags=msg->fwd_send_flags | msg->rpl_send_flags;
        if (update_sock_struct_from_via( &dst.to, msg, msg->via2 )==-1) goto error;
 #ifdef USE_COMP
        dst.comp=msg->via2->comp_no;
index 50bcbe4..355fc77 100644 (file)
--- a/ip_addr.h
+++ b/ip_addr.h
@@ -34,6 +34,7 @@
  *  2006-04-21  added init_dst_from_rcv (andrei)
  *  2007-06-26  added ip_addr_mk_any() (andrei)
  *  2008-05-21  added su2a(), ip_addr2sbuf(), ip4tosbuf(), ip62sbuf() (andrei)
+ *  2009-09-14  added send flags support to dest_info (andrei)
  */
 
 #ifndef ip_addr_h
@@ -136,11 +137,18 @@ struct receive_info{
 };
 
 
+/* send flags */
+#define SND_F_FORCE_CON_REUSE  1 /* reuse an existing connection or fail */
+#define SND_F_CON_CLOSE                        2 /* close the connection after sending */
+
+typedef unsigned char  snd_flags_t;
+
 struct dest_info{
        struct socket_info* send_sock;
        union sockaddr_union to;
        int id; /* tcp stores the connection id here */ 
        char proto;
+       snd_flags_t send_flags;
 #ifdef USE_COMP
        short comp;
 #endif
@@ -748,6 +756,7 @@ inline static void init_dst_from_rcv(struct dest_info* dst,
                dst->to=rcv->src_su;
                dst->id=rcv->proto_reserved1;
                dst->proto=rcv->proto;
+               dst->send_flags=0;
 #ifdef USE_COMP
                dst->comp=rcv->comp;
 #endif
index 7192926..f88c364 100644 (file)
@@ -14,97 +14,98 @@ Henning Westerholt
 
    Copyright © 2007 1&1 Internet AG
    Revision History
-   Revision $Revision: 4872 $ $Date: 2008-09-09 17:39:38 +0200
-                              (Di, 09 Sep 2008) $
-     __________________________________________________________
+   Revision $Revision$ $Date$
+     __________________________________________________________________
 
    Table of Contents
 
    1. Admin Guide
 
-        1.1. Overview
-        1.2. Dependencies
+        1. Overview
+        2. Dependencies
 
-              1.2.1. Kamailio Modules
-              1.2.2. External Libraries or Applications
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
 
-        1.3. Exported Parameters
+        3. Exported Parameters
 
-              1.3.1. subscriber_table (string)
-              1.3.2. subscriber_user_col (string)
-              1.3.3. subscriber_domain_col (string)
-              1.3.4. subscriber_carrier_col (string)
-              1.3.5. config_source (string)
-              1.3.6. config_file (string)
-              1.3.7. default_tree (string)
-              1.3.8. use_domain (int)
-              1.3.9. fallback_default (int)
-              1.3.10. fetch_rows (integer)
-              1.3.11. match_mode (integer)
+              3.1. subscriber_table (string)
+              3.2. subscriber_user_col (string)
+              3.3. subscriber_domain_col (string)
+              3.4. subscriber_carrier_col (string)
+              3.5. config_source (string)
+              3.6. config_file (string)
+              3.7. default_tree (string)
+              3.8. use_domain (int)
+              3.9. fallback_default (int)
+              3.10. fetch_rows (integer)
+              3.11. match_mode (integer)
 
-        1.4. Exported Functions
+        4. Exported Functions
 
-              1.4.1. cr_user_carrier(user, domain, dstavp)
-              1.4.2. cr_route(carrier, domain, prefix_matching,
+              4.1. cr_user_carrier(user, domain, dstavp)
+              4.2. cr_route(carrier, domain, prefix_matching,
                       rewrite_user, hash_source, descavp)
 
-              1.4.3. cr_prime_route(carrier, domain,
-                      prefix_matching, rewrite_user, hash_source,
-                      descavp)
+              4.3. cr_prime_route(carrier, domain, prefix_matching,
+                      rewrite_user, hash_source, descavp)
+
+              4.4. cr_nofallback_route(carrier, domain, prefix_matching,
+                      rewrite_user, hash_source, descavp)
 
-              1.4.4. cr_next_domain(carrier, domain,
-                      prefix_matching, host, reply_code, dstavp)
+              4.5. cr_next_domain(carrier, domain, prefix_matching, host,
+                      reply_code, dstavp)
 
-        1.5. MI Commands
+        5. MI Commands
 
-              1.5.1. cr_reload_routes
-              1.5.2. cr_dump_routes
-              1.5.3. cr_replace_host
-              1.5.4. cr_deactivate_host
-              1.5.5. cr_activate_host
-              1.5.6. cr_add_host
-              1.5.7. cr_delete_host
+              5.1. cr_reload_routes
+              5.2. cr_dump_routes
+              5.3. cr_replace_host
+              5.4. cr_deactivate_host
+              5.5. cr_activate_host
+              5.6. cr_add_host
+              5.7. cr_delete_host
 
-        1.6. Configuration examples
-        1.7. Installation and Running
+        6. Configuration examples
+        7. Installation and Running
 
-              1.7.1. Database setup
-              1.7.2. Database examples
-              1.7.3. User specific routing
+              7.1. Database setup
+              7.2. Database examples
+              7.3. User specific routing
 
    2. Module parameter for database access.
 
-        2.1. db_url (String)
-        2.2. carrierroute_table (String)
-        2.3. carrierroute_id_col (string)
-        2.4. carrierroute_carrier_col (string)
-        2.5. carrierroute_domain_col (string)
-        2.6. carrierroute_scan_prefix_col (string)
-        2.7. carrierroute_flags_col (string)
-        2.8. carrierroute_mask_col (string)
-        2.9. carrierroute_prob_col (string)
-        2.10. carrierroute_strip_col (string)
-        2.11. carrierroute_rewrite_host_col (string)
-        2.12. carrierroute_rewrite_prefix_col (string)
-        2.13. carrierroute_rewrite_suffix_col (string)
-        2.14. carrierroute_description_col (string)
-        2.15. carrierfailureroute_table (String)
-        2.16. carrierfailureroute_id_col (string)
-        2.17. carrierfailureroute_carrier_col (string)
-        2.18. carrierfailureroute_domain_col (string)
-        2.19. carrierfailureroute_scan_prefix_col (string)
-        2.20. carrierfailureroute_host_name_col (string)
-        2.21. carrierfailureroute_reply_code_col (string)
-        2.22. carrierfailureroute_flags_col (string)
-        2.23. carrierfailureroute_mask_col (string)
-        2.24. carrierfailureroute_next_domain_col (string)
-        2.25. carrierfailureroute_description_col (string)
-        2.26. carrier_name_table (String)
-        2.27. carrier_name_id_col (string)
-        2.28. carrier_name_carrier_col (string)
-        2.29. domain_name_table (String)
-        2.30. domain_name_id_col (string)
-        2.31. domain_name_domain_col (string)
+        1. db_url (String)
+        2. carrierroute_table (String)
+        3. carrierroute_id_col (string)
+        4. carrierroute_carrier_col (string)
+        5. carrierroute_domain_col (string)
+        6. carrierroute_scan_prefix_col (string)
+        7. carrierroute_flags_col (string)
+        8. carrierroute_mask_col (string)
+        9. carrierroute_prob_col (string)
+        10. carrierroute_strip_col (string)
+        11. carrierroute_rewrite_host_col (string)
+        12. carrierroute_rewrite_prefix_col (string)
+        13. carrierroute_rewrite_suffix_col (string)
+        14. carrierroute_description_col (string)
+        15. carrierfailureroute_table (String)
+        16. carrierfailureroute_id_col (string)
+        17. carrierfailureroute_carrier_col (string)
+        18. carrierfailureroute_domain_col (string)
+        19. carrierfailureroute_scan_prefix_col (string)
+        20. carrierfailureroute_host_name_col (string)
+        21. carrierfailureroute_reply_code_col (string)
+        22. carrierfailureroute_flags_col (string)
+        23. carrierfailureroute_mask_col (string)
+        24. carrierfailureroute_next_domain_col (string)
+        25. carrierfailureroute_description_col (string)
+        26. carrier_name_table (String)
+        27. carrier_name_id_col (string)
+        28. carrier_name_carrier_col (string)
+        29. domain_name_table (String)
+        30. domain_name_id_col (string)
+        31. domain_name_domain_col (string)
 
    List of Examples
 
@@ -128,12 +129,8 @@ Henning Westerholt
    1.18. Configuration example - Routing to user tree
    1.19. Configuration example - module configuration
    1.20. Example database content - carrierroute table
-   1.21. Example database content - simple carrierfailureroute
-          table
-
-   1.22. Example database content - more complex
-          carrierfailureroute table
-
+   1.21. Example database content - simple carrierfailureroute table
+   1.22. Example database content - more complex carrierfailureroute table
    1.23. Example database content - carrier_name table
    1.24. Example database content - domain_name table
    1.25. Necessary extensions for the user table
@@ -171,96 +168,158 @@ Henning Westerholt
 
 Chapter 1. Admin Guide
 
-1.1. Overview
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. subscriber_table (string)
+        3.2. subscriber_user_col (string)
+        3.3. subscriber_domain_col (string)
+        3.4. subscriber_carrier_col (string)
+        3.5. config_source (string)
+        3.6. config_file (string)
+        3.7. default_tree (string)
+        3.8. use_domain (int)
+        3.9. fallback_default (int)
+        3.10. fetch_rows (integer)
+        3.11. match_mode (integer)
+
+   4. Exported Functions
+
+        4.1. cr_user_carrier(user, domain, dstavp)
+        4.2. cr_route(carrier, domain, prefix_matching, rewrite_user,
+                hash_source, descavp)
+
+        4.3. cr_prime_route(carrier, domain, prefix_matching,
+                rewrite_user, hash_source, descavp)
+
+        4.4. cr_nofallback_route(carrier, domain, prefix_matching,
+                rewrite_user, hash_source, descavp)
+
+        4.5. cr_next_domain(carrier, domain, prefix_matching, host,
+                reply_code, dstavp)
+
+   5. MI Commands
+
+        5.1. cr_reload_routes
+        5.2. cr_dump_routes
+        5.3. cr_replace_host
+        5.4. cr_deactivate_host
+        5.5. cr_activate_host
+        5.6. cr_add_host
+        5.7. cr_delete_host
+
+   6. Configuration examples
+   7. Installation and Running
+
+        7.1. Database setup
+        7.2. Database examples
+        7.3. User specific routing
+
+1. Overview
 
    A module which provides routing, balancing and blacklisting
    capabilities.
 
-   The module provides routing, balancing and blacklisting
-   capabilities. It reads routing entries from a database source
-   or from a config file at Kamailio startup. It can uses one
-   routing tree (for one carrier), or if needed for every user a
-   different routing tree (unique for each carrier) for number
-   prefix based routing. It supports several route tree domains,
-   e.g. for failback routes or different routing rules for VoIP
-   and PSTN targets.
+   The module provides routing, balancing and blacklisting capabilities.
+   It reads routing entries from a database source or from a config file
+   at Kamailio startup. It can uses one routing tree (for one carrier), or
+   if needed for every user a different routing tree (unique for each
+   carrier) for number prefix based routing. It supports several route
+   tree domains, e.g. for failback routes or different routing rules for
+   VoIP and PSTN targets.
 
    Based on the tree, the module decides which number prefixes are
-   forwarded to which gateway. It can also distribute the traffic
-   by ratio parameters. Furthermore, the requests can be
-   distributed by a hash funcion to predictable destinations. The
-   hash source is configurable, two different hash functions are
-   available.
-
-   This modules scales up to more than a few million users, and is
-   able to handle more than several hundred thousand routing table
-   entries. We recieved reports of some setups that used more than
-   a million routing table entries. It also supports a large
-   number of carriers and domains which can be efficiently looked
-   up in most of the cases (see below for more informations). In
-   load balancing scenarios the usage of the config file mode is
-   recommended, to avoid the additional complexity that the
+   forwarded to which gateway. It can also distribute the traffic by ratio
+   parameters. Furthermore, the requests can be distributed by a hash
+   funcion to predictable destinations. The hash source is configurable,
+   two different hash functions are available.
+
+   This modules scales up to more than a few million users, and is able to
+   handle more than several hundred thousand routing table entries. We
+   recieved reports of some setups that used more than a million routing
+   table entries. It also supports a large number of carriers and domains
+   which can be efficiently looked up in most of the cases (see below for
+   more informations). In load balancing scenarios the usage of the config
+   file mode is recommended, to avoid the additional complexity that the
    database driven routing creates.
 
-   Routing tables can be reloaded and edited (in config file mode)
-   with the MI interface, the config file is updated according the
-   changes. This is not implemented for the db interface, because
-   its easier to do the changes directly on the db. But the reload
-   and dump functions works of course here too.
-
-   Some module functionality is not fully available in the config
-   file mode, as it is not possible to specify all information
-   that can be stored in the database tables in the config file.
-   Further information about these limitations is given in later
-   sections. For user based routing or LCR you should use the
-   database mode.
-
-   In database mode, this module supports names and IDs for the
-   carriers and domains. When using IDs for the routing functions,
-   efficient binary search is used to find the needed data
-   structures. If you are using constant strings as parameter,
-   these will be converted to IDs during the fixup procedure.
-   However, if you are using AVPs as parameter and they contain
-   strings, this cannot be converted to IDs during the fixup
-   procedure. In that case linear search is performed to find the
-   needed data structures. So from a performance point of view it
-   is better to pass only IDs in AVPs to the routing functions.
-
-   Basically this module could be used as an replacement for the
-   lcr and the dispatcher module, if you have certain flexibility,
-   integration and/or performance requirements that can't be
-   satisfied with these modules. But for smaller installations it
-   probably make more sense to use the lcr and dispatcher module.
-
-   If you want to use this module in failure routes, then you need
-   to call "append_branch()" after rewriting the request URI in
-   order to relay the message to the new target. Its also
-   supportes the usage of database derived failure routing
-   descisions with the carrierfailureroute table.
-
-1.2. Dependencies
-
-1.2.1. Kamailio Modules
+   Routing tables can be reloaded and edited (in config file mode) with
+   the MI interface, the config file is updated according the changes.
+   This is not implemented for the db interface, because its easier to do
+   the changes directly on the db. But the reload and dump functions works
+   of course here too.
+
+   Some module functionality is not fully available in the config file
+   mode, as it is not possible to specify all information that can be
+   stored in the database tables in the config file. Further information
+   about these limitations is given in later sections. For user based
+   routing or LCR you should use the database mode.
+
+   In database mode, this module supports names and IDs for the carriers
+   and domains. When using IDs for the routing functions, efficient binary
+   search is used to find the needed data structures. If you are using
+   constant strings as parameter, these will be converted to IDs during
+   the fixup procedure. However, if you are using AVPs as parameter and
+   they contain strings, this cannot be converted to IDs during the fixup
+   procedure. In that case linear search is performed to find the needed
+   data structures. So from a performance point of view it is better to
+   pass only IDs in AVPs to the routing functions.
+
+   Basically this module could be used as an replacement for the lcr and
+   the dispatcher module, if you have certain flexibility, integration
+   and/or performance requirements that can't be satisfied with these
+   modules. But for smaller installations it probably make more sense to
+   use the lcr and dispatcher module.
+
+   If you want to use this module in failure routes, then you need to call
+   "append_branch()" after rewriting the request URI in order to relay the
+   message to the new target. Its also supportes the usage of database
+   derived failure routing descisions with the carrierfailureroute table.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
 
    The following module must be loaded before this module:
-     * a database module, when a database is used as configuration
-       data source. Only SQL based databases are supported, as
-       this module needs the capability to issue raw queries. Its
-       not possible to use the dbtext or db_berkeley module at the
-       moment.
+     * a database module, when a database is used as configuration data
+       source. Only SQL based databases are supported, as this module
+       needs the capability to issue raw queries. Its not possible to use
+       the dbtext or db_berkeley module at the moment.
      * The tm module, when you want to use the $T_reply_code
        pseudo-variable in the "cr_next_domain" function.
 
-1.2.2. External Libraries or Applications
+2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed
-   before running Kamailio with this module loaded:
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
      * libconfuse, a configuration file parser library. (
        http://www.nongnu.org/confuse/ )
 
-1.3. Exported Parameters
+3. Exported Parameters
+
+   3.1. subscriber_table (string)
+   3.2. subscriber_user_col (string)
+   3.3. subscriber_domain_col (string)
+   3.4. subscriber_carrier_col (string)
+   3.5. config_source (string)
+   3.6. config_file (string)
+   3.7. default_tree (string)
+   3.8. use_domain (int)
+   3.9. fallback_default (int)
+   3.10. fetch_rows (integer)
+   3.11. match_mode (integer)
 
-1.3.1. subscriber_table (string)
+3.1. subscriber_table (string)
 
    The name of the table containing the subscribers
 
@@ -271,7 +330,7 @@ Chapter 1. Admin Guide
 modparam("carrierroute", "subscriber_table", "subscriber")
 ...
 
-1.3.2. subscriber_user_col (string)
+3.2. subscriber_user_col (string)
 
    The name of the column in the subscriber table containing the
    usernames.
@@ -283,10 +342,10 @@ modparam("carrierroute", "subscriber_table", "subscriber")
 modparam("carrierroute", "subscriber_user_col", "username")
 ...
 
-1.3.3. subscriber_domain_col (string)
+3.3. subscriber_domain_col (string)
 
-   The name of the column in the subscriber table containing the
-   domain of the subscriber.
+   The name of the column in the subscriber table containing the domain of
+   the subscriber.
 
    Default value is "domain".
 
@@ -295,23 +354,22 @@ modparam("carrierroute", "subscriber_user_col", "username")
 modparam("carrierroute", "subscriber_domain_col", "domain")
 ...
 
-1.3.4. subscriber_carrier_col (string)
+3.4. subscriber_carrier_col (string)
 
-   The name of the column in the subscriber table containing the
-   carrier id of the subscriber.
+   The name of the column in the subscriber table containing the carrier
+   id of the subscriber.
 
    Default value is "cr_preferred_carrier".
 
    Example 1.4. Set subscriber_carrier_col parameter
 ...
-modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier
-")
+modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier")
 ...
 
-1.3.5. config_source (string)
+3.5. config_source (string)
 
-   Specifies whether the module loads its config data from a file
-   or from a database. Possible values are file or db.
+   Specifies whether the module loads its config data from a file or from
+   a database. Possible values are file or db.
 
    Default value is "file".
 
@@ -320,7 +378,7 @@ modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier
 modparam("carrierroute", "config_source", "file")
 ...
 
-1.3.6. config_file (string)
+3.6. config_file (string)
 
    Specifies the path to the config file.
 
@@ -328,11 +386,10 @@ modparam("carrierroute", "config_source", "file")
 
    Example 1.6. Set config_file parameter
 ...
-modparam("carrierroute", "config_file", "/etc/kamailio/carrierroute.conf
-")
+modparam("carrierroute", "config_file", "/etc/kamailio/carrierroute.conf")
 ...
 
-1.3.7. default_tree (string)
+3.7. default_tree (string)
 
    The name of the carrier tree used per default (if the current
    subscriber has no preferred tree)
@@ -344,10 +401,10 @@ modparam("carrierroute", "config_file", "/etc/kamailio/carrierroute.conf
 modparam("carrierroute", "default_tree", "default")
 ...
 
-1.3.8. use_domain (int)
+3.8. use_domain (int)
 
-   When using tree lookup per user, this parameter specifies
-   whether to use the domain part for user matching or not.
+   When using tree lookup per user, this parameter specifies whether to
+   use the domain part for user matching or not.
 
    Default value is "0".
 
@@ -356,12 +413,12 @@ modparam("carrierroute", "default_tree", "default")
 modparam("carrierroute", "use_domain", 0)
 ...
 
-1.3.9. fallback_default (int)
+3.9. fallback_default (int)
 
-   This parameter defines the behaviour when using user-based tree
-   lookup. If the user has a non-existing tree set and
-   fallback_default is set to 1, the default tree is used.
-   Otherwise, cr_user_rewrite_uri returns an error.
+   This parameter defines the behaviour when using user-based tree lookup.
+   If the user has a non-existing tree set and fallback_default is set to
+   1, the default tree is used. Otherwise, cr_user_rewrite_uri returns an
+   error.
 
    Default value is "1".
 
@@ -370,13 +427,12 @@ modparam("carrierroute", "use_domain", 0)
 modparam("carrierroute", "fallback_default", 1)
 ...
 
-1.3.10. fetch_rows (integer)
+3.10. fetch_rows (integer)
 
-   The number of the rows to be fetched at once from database when
-   loading the routing data. This value can be used to tune the
-   load time at startup. For 1MB of private memory (default) it
-   should be below 3750. The database driver must support the
-   fetch_result() capability.
+   The number of the rows to be fetched at once from database when loading
+   the routing data. This value can be used to tune the load time at
+   startup. For 1MB of private memory (default) it should be below 3750.
+   The database driver must support the fetch_result() capability.
 
    Default value is "2000".
 
@@ -385,15 +441,14 @@ modparam("carrierroute", "fallback_default", 1)
 modparam("carrierroute", "fetch_rows", 3000)
 ...
 
-1.3.11. match_mode (integer)
+3.11. match_mode (integer)
 
-   The number of individual characters that are used for matching.
-   Valid values are 10 or 128. When you specifiy 10, only digits
-   will be used for matching, this operation mode is equivalent to
-   the old behaviour. When configured with 128, all standard ascii
-   chars are available for matching. Please be aware that memory
-   requirements for storing the routing tree in shared memory will
-   also increase by a factor of 12.8.
+   The number of individual characters that are used for matching. Valid
+   values are 10 or 128. When you specifiy 10, only digits will be used
+   for matching, this operation mode is equivalent to the old behaviour.
+   When configured with 128, all standard ascii chars are available for
+   matching. Please be aware that memory requirements for storing the
+   routing tree in shared memory will also increase by a factor of 12.8.
 
    Default value is "10".
 
@@ -402,11 +457,23 @@ modparam("carrierroute", "fetch_rows", 3000)
 modparam("carrierroute", "match_mode", 10)
 ...
 
-1.4. Exported Functions
+4. Exported Functions
+
+   4.1. cr_user_carrier(user, domain, dstavp)
+   4.2. cr_route(carrier, domain, prefix_matching, rewrite_user,
+          hash_source, descavp)
+
+   4.3. cr_prime_route(carrier, domain, prefix_matching, rewrite_user,
+          hash_source, descavp)
+
+   4.4. cr_nofallback_route(carrier, domain, prefix_matching,
+          rewrite_user, hash_source, descavp)
+
+   4.5. cr_next_domain(carrier, domain, prefix_matching, host, reply_code,
+          dstavp)
 
-   Previous versions of carrierroute had some more function. All
-   the old semantics can be achieved by using the few new
-   functions like this:
+   Previous versions of carrierroute had some more function. All the old
+   semantics can be achieved by using the few new functions like this:
 cr_rewrite_uri(domain, hash_source)
 -> cr_route("default", domain, "$rU", "$rU", hash_source)
 
@@ -432,178 +499,217 @@ cr_user_rewrite_uri(uri, domain)
 cr_tree_rewrite_uri(tree, domain)
 -> cr_route(tree, domain, "$rU", "$rU", "call_id")
 
-1.4.1.  cr_user_carrier(user, domain, dstavp)
+4.1.  cr_user_carrier(user, domain, dstavp)
 
-   This function loads the carrier and stores it in an AVP. It
-   cannot be used in the config file mode, as it needs a mapping
-   of the given user to a certain carrier. The is derived from a
-   database entry belonging to the user parameter. This mapping
-   must be available in the table that is specified in the
-   "subscriber_table" variable. This data is not cached in memory,
-   that means for every execution of this function a database
+   This function loads the carrier and stores it in an AVP. It cannot be
+   used in the config file mode, as it needs a mapping of the given user
+   to a certain carrier. The is derived from a database entry belonging to
+   the user parameter. This mapping must be available in the table that is
+   specified in the "subscriber_table" variable. This data is not cached
+   in memory, that means for every execution of this function a database
    query will be done.
 
    Meaning of the parameters is as follows:
-     * user - Name of the user for the carrier tree lookup.
-       Additional to a string any pseudo-variable could be used as
-       input.
-     * domain - Name of the routing domain to be used. Additional
-       to a string any pseudo-variable could be used as input.
+     * user - Name of the user for the carrier tree lookup. Additional to
+       a string any pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional to a
+       string any pseudo-variable could be used as input.
      * dstavp - Name of the AVP where to store the carrier id.
 
-1.4.2.  cr_route(carrier, domain, prefix_matching, rewrite_user,
-hash_source, descavp)
-
-   This function searches for the longest match for the user given
-   in prefix_matching at the given domain in the given carrier
-   tree. The Request URI is rewritten using rewrite_user and the
-   given hash source and algorithm. Returns -1 if there is no data
-   found or an empty rewrite host on the longest match is found.
-   On sucess also the description is stored in the given AVP (if
-   obmitted, nothing is stored in an AVP). This is useful if you
-   need some additional informations that belongs to each gw, like
-   the destination or the number of channels.
-
-   This function is only usable with rewrite_user and
-   prefix_matching containing a valid string. This string needs to
-   be numerical if the match_mode parameter is set to 10. It uses
-   the standard CRC32 algorithm to calculate the hash values.
-
-   If flags and masks values are specified in the routing rule,
-   they will be compared by this function to the message flags.
-   Specify a flag and mask value of "0" to match to all possible
-   message flags (this is the default value). If flags and mask
-   are not zero, and no match to the message flags is possible, no
-   routing will be done. The calculation of the hash and the
-   load-balancing is done after the flags matching.
+4.2.  cr_route(carrier, domain, prefix_matching, rewrite_user, hash_source,
+descavp)
+
+   This function searches for the longest match for the user given in
+   prefix_matching at the given domain in the given carrier tree. The
+   Request URI is rewritten using rewrite_user and the given hash source
+   and algorithm. Returns -1 if there is no data found or an empty rewrite
+   host on the longest match is found. On sucess also the description is
+   stored in the given AVP (if obmitted, nothing is stored in an AVP).
+   This is useful if you need some additional informations that belongs to
+   each gw, like the destination or the number of channels.
+
+   This function is only usable with rewrite_user and prefix_matching
+   containing a valid string. This string needs to be numerical if the
+   match_mode parameter is set to 10. It uses the standard CRC32 algorithm
+   to calculate the hash values.
+
+   If flags and masks values are specified in the routing rule, they will
+   be compared by this function to the message flags. Specify a flag and
+   mask value of "0" to match to all possible message flags (this is the
+   default value). If flags and mask are not zero, and no match to the
+   message flags is possible, no routing will be done. The calculation of
+   the hash and the load-balancing is done after the flags matching.
 
    Meaning of the parameters is as follows:
-     * carrier - The routing tree to be used. Additional to a
+     * carrier - The routing tree to be used. Additional to a string any
+       pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional to a
        string any pseudo-variable could be used as input.
-     * domain - Name of the routing domain to be used. Additional
+     * prefix_matching - User name to be used for prefix matching in the
+       routing tree. Additional to a string any pseudo-variable could be
+       used as input.
+     * rewrite_user - The user name to be used for applying the rewriting
+       rule. Usually this is the user part of the request URI. Additional
        to a string any pseudo-variable could be used as input.
-     * prefix_matching - User name to be used for prefix matching
-       in the routing tree. Additional to a string any
-       pseudo-variable could be used as input.
-     * rewrite_user - The user name to be used for applying the
-       rewriting rule. Usually this is the user part of the
-       request URI. Additional to a string any pseudo-variable
-       could be used as input.
-     * hash_source - The hash values of the destination set must
-       be a contiguous range starting at 1, limited by the
-       configuration parameter max_targets. Possible values for
-       hash_source are: call_id, from_uri, from_user, to_uri and
-       to_user.
-     * decsavp - Name of the AVP where to store the description.
-       This parameter is optional.
-
-1.4.3.  cr_prime_route(carrier, domain, prefix_matching,
-rewrite_user, hash_source, descavp)
-
-   This function searches for the longest match for the user given
-   in prefix_matching at the given domain in the given carrier
-   tree. The Request URI is rewritten using rewrite_user and the
-   given hash source and algorithm. Returns -1 if there is no data
-   found or an empty rewrite host on the longest match is found.
-   On success also the description is stored in the given AVP (if
-   obmitted, nothing is stored in an AVP). This is useful if you
-   need some additional informations that belongs to each gw, like
-   the destination or the number of channels. This function is
-   only usable with rewrite_user and prefix_matching containing a
-   valid string. This string needs to be numerical if the
-   match_mode parameter is set to 10.
-
-   It uses the prime hash algorithm to calculate the hash values.
-   This means that the fuction behaves different then the normal
-   routing function. If you don't know what this prime
-   functionality is, you should just use the cr_route function.
-   The prime routing algorithm not use the configured
-   probabilities in order to route requests, it just uses a fixed
-   hash distribution. If one of the hash targets is not available,
-   and no backup rule is configured, the function will return -1.
+     * hash_source - The hash values of the destination set must be a
+       contiguous range starting at 1, limited by the configuration
+       parameter max_targets. Possible values for hash_source are:
+       call_id, from_uri, from_user, to_uri, to_user and rand
+     * decsavp - Name of the AVP where to store the description. This
+       parameter is optional.
+
+4.3.  cr_prime_route(carrier, domain, prefix_matching, rewrite_user,
+hash_source, descavp)
+
+   This function searches for the longest match for the user given in
+   prefix_matching at the given domain in the given carrier tree. The
+   Request URI is rewritten using rewrite_user and the given hash source
+   and algorithm. Returns -1 if there is no data found or an empty rewrite
+   host on the longest match is found. On success also the description is
+   stored in the given AVP (if obmitted, nothing is stored in an AVP).
+   This is useful if you need some additional informations that belongs to
+   each gw, like the destination or the number of channels. This function
+   is only usable with rewrite_user and prefix_matching containing a valid
+   string. This string needs to be numerical if the match_mode parameter
+   is set to 10.
+
+   It uses the prime hash algorithm to calculate the hash values. This
+   means that the fuction behaves different then the normal routing
+   function. If you don't know what this prime functionality is, you
+   should just use the cr_route function. The prime routing algorithm not
+   use the configured probabilities in order to route requests, it just
+   uses a fixed hash distribution. If one of the hash targets is not
+   available, and no backup rule is configured, the function will return
+   -1.
+
+   Please not that this function is deprecated and will be removed in the
+   next stable release. Please consider using the cr_nofallback_route
+   function instead.
 
    Meaning of the parameters is as follows:
-     * carrier - The routing tree to be used. Additional to a
+     * carrier - The routing tree to be used. Additional to a string any
+       pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional to a
        string any pseudo-variable could be used as input.
-     * domain - Name of the routing domain to be used. Additional
+     * prefix_matching - User name to be used for prefix matching in the
+       routing tree. Additional to a string any pseudo-variable could be
+       used as input.
+     * rewrite_user - The user name to be used for applying the rewriting
+       rule. Usually this is the user part of the request URI. Additional
        to a string any pseudo-variable could be used as input.
-     * prefix_matching - User name to be used for prefix matching
-       in the routing tree. Additional to a string any
-       pseudo-variable could be used as input.
-     * rewrite_user - The user name to be used for applying the
-       rewriting rule. Usually this is the user part of the
-       request URI. Additional to a string any pseudo-variable
-       could be used as input.
-     * hash_source - The hash values of the destination set must
-       be a contiguous range starting at 1, limited by the
-       configuration parameter max_targets. Possible values for
-       hash_source are: call_id, from_uri, from_user, to_uri and
-       to_user.
-     * descavp - Name of the AVP where to store the description.
-       This parameter is optional.
-
-1.4.4.  cr_next_domain(carrier, domain, prefix_matching, host,
-reply_code, dstavp)
-
-   This function searches for the longest match for the user given
-   in prefix_matching at the given domain in the given carrier
-   failure tree. It tries to find a next domain matching the given
-   host, reply_code and the message flags. The matching is done in
-   this order: host, reply_code and then flags. The more wildcards
-   in reply_code and the more bits used in flags, the lower the
-   priority. Returns -1 if there is no data found or an empty
-   next_domain on the longest match is found. Otherwise the next
-   domain is stored in the given AVP. This function is only usable
-   with rewrite_user and prefix_matching containing a valid
-   string. This string needs to be numerical if the match_mode
-   parameter is set to 10.
+     * hash_source - The hash values of the destination set must be a
+       contiguous range starting at 1, limited by the configuration
+       parameter max_targets. Possible values for hash_source are:
+       call_id, from_uri, from_user, to_uri to_user and rand
+     * descavp - Name of the AVP where to store the description. This
+       parameter is optional.
+
+4.4.  cr_nofallback_route(carrier, domain, prefix_matching, rewrite_user,
+hash_source, descavp)
+
+   This function searches for the longest match for the user given in
+   prefix_matching at the given domain in the given carrier tree. The
+   Request URI is rewritten using rewrite_user and the given hash source
+   and algorithm. Returns -1 if there is no data found or an empty rewrite
+   host on the longest match is found. On success also the description is
+   stored in the given AVP (if obmitted, nothing is stored in an AVP).
+   This is useful if you need some additional informations that belongs to
+   each gw, like the destination or the number of channels. This function
+   is only usable with rewrite_user and prefix_matching containing a valid
+   string. This string needs to be numerical if the match_mode parameter
+   is set to 10.
+
+   It uses the standard CRC32 algorithm to calculate the hash values. In
+   contrast to the normal cr_route function the backup rules of
+   cr_prime_route is used. This means not the configured probabilities
+   will be used, only a fixed hash distribution. This makes sense to
+   distribute incoming register requests e.g. to a bunch of registrar
+   servers. If one of the hash targets is not available and backup rule is
+   configured, the function will return -1.
 
    Meaning of the parameters is as follows:
-     * carrier - The routing tree to be used. Additional to a
+     * carrier - The routing tree to be used. Additional to a string any
+       pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional to a
        string any pseudo-variable could be used as input.
-     * domain - Name of the routing domain to be used. Additional
+     * prefix_matching - User name to be used for prefix matching in the
+       routing tree. Additional to a string any pseudo-variable could be
+       used as input.
+     * rewrite_user - The user name to be used for applying the rewriting
+       rule. Usually this is the user part of the request URI. Additional
        to a string any pseudo-variable could be used as input.
-     * prefix_matching - User name to be used for prefix matching
-       in the routing tree. Additional to a string any
+     * hash_source - The hash values of the destination set must be a
+       contiguous range starting at 1, limited by the configuration
+       parameter max_targets. Possible values for hash_source are:
+       call_id, from_uri, from_user, to_uri and to_user.
+     * descavp - Name of the AVP where to store the description. This
+       parameter is optional.
+
+4.5.  cr_next_domain(carrier, domain, prefix_matching, host, reply_code,
+dstavp)
+
+   This function searches for the longest match for the user given in
+   prefix_matching at the given domain in the given carrier failure tree.
+   It tries to find a next domain matching the given host, reply_code and
+   the message flags. The matching is done in this order: host, reply_code
+   and then flags. The more wildcards in reply_code and the more bits used
+   in flags, the lower the priority. Returns -1 if there is no data found
+   or an empty next_domain on the longest match is found. Otherwise the
+   next domain is stored in the given AVP. This function is only usable
+   with rewrite_user and prefix_matching containing a valid string. This
+   string needs to be numerical if the match_mode parameter is set to 10.
+
+   Meaning of the parameters is as follows:
+     * carrier - The routing tree to be used. Additional to a string any
        pseudo-variable could be used as input.
-     * host - The host name to be used for failure route rule
-       matching. Usually this is the last tried routing
-       destination stored in an avp by cr_route. Additional to a
+     * domain - Name of the routing domain to be used. Additional to a
        string any pseudo-variable could be used as input.
-     * reply_code - The reply code to be used for failure route
-       rule matching. Additional to a string any pseudo-variable
-       could be used as input.
-     * dstavp - Name of the AVP where to store the next routing
-       domain.
-
-1.5. MI Commands
-
-   All commands understand the "-?" parameter to print a short
-   help message. The options have to be quoted as one string to be
-   passed to MI interface. Each option except host and new host
-   can be wildcarded by * (but only * and not things like "-d
-   prox*").
-
-1.5.1. cr_reload_routes
+     * prefix_matching - User name to be used for prefix matching in the
+       routing tree. Additional to a string any pseudo-variable could be
+       used as input.
+     * host - The host name to be used for failure route rule matching.
+       Usually this is the last tried routing destination stored in an avp
+       by cr_route. Additional to a string any pseudo-variable could be
+       used as input.
+     * reply_code - The reply code to be used for failure route rule
+       matching. Additional to a string any pseudo-variable could be used
+       as input.
+     * dstavp - Name of the AVP where to store the next routing domain.
+
+5. MI Commands
+
+   5.1. cr_reload_routes
+   5.2. cr_dump_routes
+   5.3. cr_replace_host
+   5.4. cr_deactivate_host
+   5.5. cr_activate_host
+   5.6. cr_add_host
+   5.7. cr_delete_host
+
+   All commands understand the "-?" parameter to print a short help
+   message. The options have to be quoted as one string to be passed to MI
+   interface. Each option except host and new host can be wildcarded by *
+   (but only * and not things like "-d prox*").
+
+5.1. cr_reload_routes
 
    This command reloads the routing data from the data source.
 
-   Important: When new domains have been added, a restart of the
-   server must be done, because the mapping of the ids used in the
-   config script cannot be updated at runtime at the moment. So a
-   reload could result in a wrong routing behaviour, because the
-   ids used in the script could differ from the one used
-   internally from the server. Modifying of already existing
-   domains is no problem.
+   Important: When new domains have been added, a restart of the server
+   must be done, because the mapping of the ids used in the config script
+   cannot be updated at runtime at the moment. So a reload could result in
+   a wrong routing behaviour, because the ids used in the script could
+   differ from the one used internally from the server. Modifying of
+   already existing domains is no problem.
 
-1.5.2. cr_dump_routes
+5.2. cr_dump_routes
 
    This command prints the route rules on the command line.
 
-1.5.3. cr_replace_host
+5.3. cr_replace_host
 
-   This command can replace the rewrite_host of a route rule, it
-   is only usable in file mode. Following options are possible:
+   This command can replace the rewrite_host of a route rule, it is only
+   usable in file mode. Following options are possible:
      * -d - the domain containing the host
      * -p - the prefix containing the host
      * -h - the host to be replaced
@@ -616,20 +722,19 @@ reply_code, dstavp)
 kamctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2"
 ...
 
-1.5.4. cr_deactivate_host
+5.4. cr_deactivate_host
 
-   This command deactivates the specified host, i.e. it sets its
-   status to 0. It is only usable in file mode. Following options
-   are possible:
+   This command deactivates the specified host, i.e. it sets its status to
+   0. It is only usable in file mode. Following options are possible:
      * -d - the domain containing the host
      * -p - the prefix containing the host
      * -h - the host to be deactivated
      * -t - the new host used as backup
 
    When -t (new_host) is specified, the portion of traffic for the
-   deactivated host is routed to the host given by -t. This is
-   indicated in the output of dump_routes. The backup route is
-   deactivated if the host is activated again.
+   deactivated host is routed to the host given by -t. This is indicated
+   in the output of dump_routes. The backup route is deactivated if the
+   host is activated again.
 
    Use the "null" prefix to specify an empty prefix.
 
@@ -638,11 +743,10 @@ kamctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2"
 kamctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1"
 ...
 
-1.5.5. cr_activate_host
+5.5. cr_activate_host
 
-   This command activates the specified host, i.e. it sets its
-   status to 1. It is only usable in file mode. Following options
-   are possible:
+   This command activates the specified host, i.e. it sets its status to
+   1. It is only usable in file mode. Following options are possible:
      * -d - the domain containing the host
      * -p - the prefix containing the host
      * -h - the host to be activated
@@ -654,7 +758,7 @@ kamctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1"
 kamctl fifo cr_activate_host "-d proxy -p 49 -h proxy1"
 ...
 
-1.5.6. cr_add_host
+5.6. cr_add_host
 
    This command adds a route rule, it is only usable in file mode.
    Following options are possible:
@@ -674,11 +778,11 @@ kamctl fifo cr_activate_host "-d proxy -p 49 -h proxy1"
 kamctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25"
 ...
 
-1.5.7. cr_delete_host
+5.7. cr_delete_host
 
-   This command delete the specified hosts or rules, i.e. remove
-   them from the route tree. It is only usable in file mode.
-   Following options are possible:
+   This command delete the specified hosts or rules, i.e. remove them from
+   the route tree. It is only usable in file mode. Following options are
+   possible:
      * -d - the domain containing the host
      * -p - the prefix containing the host
      * -h - the host to be added
@@ -695,7 +799,7 @@ kamctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25"
 kamctl fifo cr_delete_host "-d proxy -p 49 -h proxy1 -w 0.25"
 ...
 
-1.6. Configuration examples
+6. Configuration examples
 
    Example 1.17. Configuration example - Routing to default tree
 ...
@@ -730,7 +834,6 @@ failure_route[2] {
         # further processing
 }
 
-
    Example 1.18. Configuration example - Routing to user tree
 ...
 route[1] {
@@ -755,8 +858,7 @@ route[1] {
 failure_route[1] {
         revert_uri();
         if (!cr_next_domain("$avp(s:carrier)", "$avp(s:domain)", "$rU",
-                        "$avp(s:host)", "$T_reply_code", "$avp(s:domain)
-")) {
+                        "$avp(s:host)", "$T_reply_code", "$avp(s:domain)")) {
                 xlog("L_ERR", "cr_next_domain failed\n");
                 exit;
         }
@@ -777,24 +879,22 @@ failure_route[1] {
 
    Example 1.19. Configuration example - module configuration
 
-   The following config file specifies within the default carrier
-   two domains, each with an prefix that contains two hosts. It is
-   not possible to specify another carrier if you use the config
-   file as data source.
-
-   All traffic will be equally distributed between the hosts, both
-   are active. The hash algorithm will working over the [1,2] set,
-   messages hashed to one will go to the first host, the other to
-   the second one. Don't use a hash index value of zero. If you
-   ommit the hash completly, the module gives them a autogenerated
-   value, starting from one.
-
-   Use the "NULL" prefix to specify an empty prefix in the config
-   file. Please note that the prefix is matched against the
-   request URI (or to URI), if they did not contain a valid
-   (numerical) URI, no match is possible. So for loadbalancing
-   purposes e.g. for your registrars, you should use an empty
-   prefix.
+   The following config file specifies within the default carrier two
+   domains, each with an prefix that contains two hosts. It is not
+   possible to specify another carrier if you use the config file as data
+   source.
+
+   All traffic will be equally distributed between the hosts, both are
+   active. The hash algorithm will working over the [1,2] set, messages
+   hashed to one will go to the first host, the other to the second one.
+   Don't use a hash index value of zero. If you ommit the hash completly,
+   the module gives them a autogenerated value, starting from one.
+
+   Use the "NULL" prefix to specify an empty prefix in the config file.
+   Please note that the prefix is matched against the request URI (or to
+   URI), if they did not contain a valid (numerical) URI, no match is
+   possible. So for loadbalancing purposes e.g. for your registrars, you
+   should use an empty prefix.
 ...
 domain proxy {
    prefix 49 {
@@ -833,29 +933,32 @@ domain register {
 }
 ...
 
-1.7. Installation and Running
+7. Installation and Running
+
+   7.1. Database setup
+   7.2. Database examples
+   7.3. User specific routing
 
-1.7.1. Database setup
+7.1. Database setup
 
-   Before running Kamailio with carrierroute, you have to setup
-   the database table where the module will store the routing
-   data. For that, if the table was not created by the
-   installation script or you choose to install everything by
-   yourself you can use the carrierroute-create.sql SQL script in
-   the database directories in the kamailio/scripts folder as
-   template. Database and table name can be set with module
-   parameters so they can be changed, but the name of the columns
-   must be as they are in the SQL script. You can also find the
+   Before running Kamailio with carrierroute, you have to setup the
+   database table where the module will store the routing data. For that,
+   if the table was not created by the installation script or you choose
+   to install everything by yourself you can use the
+   carrierroute-create.sql SQL script in the database directories in the
+   kamailio/scripts folder as template. Database and table name can be set
+   with module parameters so they can be changed, but the name of the
+   columns must be as they are in the SQL script. You can also find the
    complete database documentation on the project webpage,
-   http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
-   The flags and mask columns have the same function as in the
-   carrierfailureroute table. A zero value in the flags and mask
-   column means that any message flags will match this rule.
+   http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html. The
+   flags and mask columns have the same function as in the
+   carrierfailureroute table. A zero value in the flags and mask column
+   means that any message flags will match this rule.
 
-   For a minimal configuration either use the config file given
-   above, or insert some data into the tables of the module.
+   For a minimal configuration either use the config file given above, or
+   insert some data into the tables of the module.
 
-1.7.2. Database examples
+7.2. Database examples
 
    Example 1.20. Example database content - carrierroute table
 ...
@@ -878,26 +981,24 @@ domain register {
 +----+---------+--------+-------------+-------+------+---------------+
 ...
 
-   This table contains three routes to two gateways for the "49"
-   prefix, and a default route for other prefixes over carrier 2
-   and carrier 1. The gateways for the default carrier will be
-   used for functions that don't support the user specific carrier
-   lookup. The routing rules for carrier 1 and carrier 2 for the
-   "49" prefix contains a additional rule with the domain 2, that
-   can be used for example as fallback if the gateways in domain 1
-   are not reachable. Two more fallback rules (domain 3 and 4) for
-   carrier 1 are also supplied to support the functionality of the
-   carrierfailureroute table example that is provided in the next
+   This table contains three routes to two gateways for the "49" prefix,
+   and a default route for other prefixes over carrier 2 and carrier 1.
+   The gateways for the default carrier will be used for functions that
+   don't support the user specific carrier lookup. The routing rules for
+   carrier 1 and carrier 2 for the "49" prefix contains a additional rule
+   with the domain 2, that can be used for example as fallback if the
+   gateways in domain 1 are not reachable. Two more fallback rules (domain
+   3 and 4) for carrier 1 are also supplied to support the functionality
+   of the carrierfailureroute table example that is provided in the next
    section.
 
-   This table provides also a "carrier 1" routing rule for the
-   "49" prefix, that is only choosen if some message flags are
-   set. If this flags are not set, the other two rules are used.
-   The "strip", "mask" and "comment" colums are omitted for
-   brevity.
+   This table provides also a "carrier 1" routing rule for the "49"
+   prefix, that is only choosen if some message flags are set. If this
+   flags are not set, the other two rules are used. The "strip", "mask"
+   and "comment" colums are omitted for brevity.
 
-   Example 1.21. Example database content - simple
-   carrierfailureroute table
+   Example 1.21. Example database content - simple carrierfailureroute
+   table
 ...
 +----+---------+--------+---------------+------------+-------------+
 | id | carrier | domain | host_name     | reply_code | next_domain |
@@ -907,16 +1008,16 @@ domain register {
 +----+---------+--------+---------------+------------+-------------+
 ...
 
-   This table contains two failure routes for the "gw.carrier1-1"
-   and "-2" gateways. For any (failure) reply code the respective
-   next domain is choosen. After that no more failure routes are
-   available, an error will be returned from the "cr_next_domain"
-   function. Not all table colums are show here for brevity.
+   This table contains two failure routes for the "gw.carrier1-1" and "-2"
+   gateways. For any (failure) reply code the respective next domain is
+   choosen. After that no more failure routes are available, an error will
+   be returned from the "cr_next_domain" function. Not all table colums
+   are show here for brevity.
 
    For each failure route domain and carrier that is added to the
-   carrierfailureroute table there must be at least one
-   corresponding entry in the carrierroute table, otherwise the
-   module will not load the routing data.
+   carrierfailureroute table there must be at least one corresponding
+   entry in the carrierroute table, otherwise the module will not load the
+   routing data.
 
    Example 1.22. Example database content - more complex
    carrierfailureroute table
@@ -931,19 +1032,17 @@ domain register {
 +----+---------+-----------+------------+-------+------+-------------+
 ...
 
-   This table contains four failure routes that shows the usage of
-   more advanced features. The first route matches to a 408, and
-   to some flag for example that indicates that ringing has
-   happened. If this flag is set, there will be no further
-   forwarding, because next_domain is empty. In the second and
-   third routes are certain gateway errors matched, if this errors
-   have occured, then the next domain will be choosen. The last
-   route does forwarding according some flags, e.g. the customer
-   came from a certain carrier, and has call-forwarding
-   deactivated. In order to use the routing that is specified
-   above, a matching carrierroute table must be provided, that
-   holds domain entries for this routing rules. Not all table
-   colums are show here for brevity.
+   This table contains four failure routes that shows the usage of more
+   advanced features. The first route matches to a 408, and to some flag
+   for example that indicates that ringing has happened. If this flag is
+   set, there will be no further forwarding, because next_domain is empty.
+   In the second and third routes are certain gateway errors matched, if
+   this errors have occured, then the next domain will be choosen. The
+   last route does forwarding according some flags, e.g. the customer came
+   from a certain carrier, and has call-forwarding deactivated. In order
+   to use the routing that is specified above, a matching carrierroute
+   table must be provided, that holds domain entries for this routing
+   rules. Not all table colums are show here for brevity.
 
    Example 1.23. Example database content - carrier_name table
 ...
@@ -956,8 +1055,7 @@ domain register {
 +----+----------+
 ...
 
-   This table contains the mapping of the carrier id to actual
-   names.
+   This table contains the mapping of the carrier id to actual names.
 
    Example 1.24. Example database content - domain_name table
 ...
@@ -970,15 +1068,14 @@ domain register {
 +----+----------+
 ...
 
-   This table contains the mapping of the domain id to actual
-   names.
+   This table contains the mapping of the domain id to actual names.
 
-1.7.3. User specific routing
+7.3. User specific routing
 
-   For a functional routing the "cr_preferred_carrier" column must
-   be added to the subscriber table (or to the table and column
-   that you specified as modul parameter) to choose the actual
-   carrier for the users.
+   For a functional routing the "cr_preferred_carrier" column must be
+   added to the subscriber table (or to the table and column that you
+   specified as modul parameter) to choose the actual carrier for the
+   users.
 
    Example 1.25. Necessary extensions for the user table
 
@@ -989,20 +1086,52 @@ ALTER TABLE subscriber ADD cr_preferred_carrier int(10) default NULL;
 
 Chapter 2. Module parameter for database access.
 
-2.1. db_url (String)
+   Table of Contents
+
+   1. db_url (String)
+   2. carrierroute_table (String)
+   3. carrierroute_id_col (string)
+   4. carrierroute_carrier_col (string)
+   5. carrierroute_domain_col (string)
+   6. carrierroute_scan_prefix_col (string)
+   7. carrierroute_flags_col (string)
+   8. carrierroute_mask_col (string)
+   9. carrierroute_prob_col (string)
+   10. carrierroute_strip_col (string)
+   11. carrierroute_rewrite_host_col (string)
+   12. carrierroute_rewrite_prefix_col (string)
+   13. carrierroute_rewrite_suffix_col (string)
+   14. carrierroute_description_col (string)
+   15. carrierfailureroute_table (String)
+   16. carrierfailureroute_id_col (string)
+   17. carrierfailureroute_carrier_col (string)
+   18. carrierfailureroute_domain_col (string)
+   19. carrierfailureroute_scan_prefix_col (string)
+   20. carrierfailureroute_host_name_col (string)
+   21. carrierfailureroute_reply_code_col (string)
+   22. carrierfailureroute_flags_col (string)
+   23. carrierfailureroute_mask_col (string)
+   24. carrierfailureroute_next_domain_col (string)
+   25. carrierfailureroute_description_col (string)
+   26. carrier_name_table (String)
+   27. carrier_name_id_col (string)
+   28. carrier_name_carrier_col (string)
+   29. domain_name_table (String)
+   30. domain_name_id_col (string)
+   31. domain_name_domain_col (string)
+
+1. db_url (String)
 
    URL to the database containing the data.
 
-   Default value is
-   "mysql://openserro:openserro@localhost/openser".
+   Default value is "mysql://openserro:openserro@localhost/openser".
 
    Example 2.1. Set db_url parameter
 ...
-modparam("carrierroute", "db_url", "dbdriver://username:password@dbhost/
-dbname")
+modparam("carrierroute", "db_url", "dbdriver://username:password@dbhost/dbname")
 ...
 
-2.2. carrierroute_table (String)
+2. carrierroute_table (String)
 
    Name of the carrierroute table for the carrierroute module.
 
@@ -1013,7 +1142,7 @@ dbname")
 modparam("carrierroute", "carrierroute_table", "carrierroute")
 ...
 
-2.3. carrierroute_id_col (string)
+3. carrierroute_id_col (string)
 
    Name of the column contains the unique identifier of a route.
 
@@ -1022,7 +1151,7 @@ modparam("carrierroute", "carrierroute_table", "carrierroute")
 modparam("carrierroute", "carrierroute_id_col", "id")
 ...
 
-2.4. carrierroute_carrier_col (string)
+4. carrierroute_carrier_col (string)
 
    This column contains the carrier id.
 
@@ -1031,33 +1160,31 @@ modparam("carrierroute", "carrierroute_id_col", "id")
 modparam("carrierroute", "carrierroute_carrier_col", "carrier")
 ...
 
-2.5. carrierroute_domain_col (string)
+5. carrierroute_domain_col (string)
 
-   This column contains the routing domain id. You can define
-   several routing domains to have different routing rules. Maybe
-   you use domain 0 for normal routing and domain 1 if domain 0
-   failed.
+   This column contains the routing domain id. You can define several
+   routing domains to have different routing rules. Maybe you use domain 0
+   for normal routing and domain 1 if domain 0 failed.
 
    Example 2.5. Set carrierroute_domain_col parameter
 ...
 modparam("carrierroute", "carrierroute_domain_col", "domain")
 ...
 
-2.6. carrierroute_scan_prefix_col (string)
+6. carrierroute_scan_prefix_col (string)
 
-   Name of column contains the scan prefixes. Scan prefixes define
-   the matching portion of a phone number, e.g. when we have the
-   scan prefixes 49721 and 49, the called number is 49721913740,
-   it matches 49721, because the longest match is taken. If no
-   prefix matches, the number is not routed. To prevent this, an
-   empty prefix value of could be added.
+   Name of column contains the scan prefixes. Scan prefixes define the
+   matching portion of a phone number, e.g. when we have the scan prefixes
+   49721 and 49, the called number is 49721913740, it matches 49721,
+   because the longest match is taken. If no prefix matches, the number is
+   not routed. To prevent this, an empty prefix value of could be added.
 
    Example 2.6. Set carrierroute_scan_prefix_col parameter
 ...
 modparam("carrierroute", "carrierroute_scan_prefix_col", "scan_prefix")
 ...
 
-2.7. carrierroute_flags_col (string)
+7. carrierroute_flags_col (string)
 
    This column contains the flags used for rule matching.
 
@@ -1066,106 +1193,99 @@ modparam("carrierroute", "carrierroute_scan_prefix_col", "scan_prefix")
 modparam("carrierroute", "carrierroute_flags_col", "flags")
 ...
 
-2.8. carrierroute_mask_col (string)
+8. carrierroute_mask_col (string)
 
-   This column contains the mask that is applied to the message
-   flags before rule matching.
+   This column contains the mask that is applied to the message flags
+   before rule matching.
 
    Example 2.8. Set carrierroute_mask_col parameter
 ...
 modparam("carrierroute", "carrierroute_mask_col", "mask")
 ...
 
-2.9. carrierroute_prob_col (string)
+9. carrierroute_prob_col (string)
 
-   Name of column contains the probability. The probability value
-   is used to distribute the traffic between several gateways.
-   Let's say 70 % of the traffic shall be routed to gateway A, the
-   other 30 % shall be routed to gateway B, we define a rule for
-   gateway A with a prob value of 0.7 and a rule for gateway B
-   with a prob value of 0.3. If all probabilities for a given
-   prefix, tree and domain don't add to 100%, the prefix values
-   will be adjusted according the given prob values. E.g. if three
-   hosts with prob values of 0.5, 0.5 and 0.4 are defined, the
-   resulting probabilities are 35.714, 35.714 and 28.571%. But its
-   better to choose meaningful values in the first place because
-   of clarity.
+   Name of column contains the probability. The probability value is used
+   to distribute the traffic between several gateways. Let's say 70 % of
+   the traffic shall be routed to gateway A, the other 30 % shall be
+   routed to gateway B, we define a rule for gateway A with a prob value
+   of 0.7 and a rule for gateway B with a prob value of 0.3. If all
+   probabilities for a given prefix, tree and domain don't add to 100%,
+   the prefix values will be adjusted according the given prob values.
+   E.g. if three hosts with prob values of 0.5, 0.5 and 0.4 are defined,
+   the resulting probabilities are 35.714, 35.714 and 28.571%. But its
+   better to choose meaningful values in the first place because of
+   clarity.
 
    Example 2.9. Set carrierroute_prob_col parameter
 ...
 modparam("carrierroute", "carrierroute_prob_col", "prob")
 ...
 
-2.10. carrierroute_strip_col (string)
+10. carrierroute_strip_col (string)
 
-   Name of the column contains the number of digits to be stripped
-   of the userpart of an URI before prepending rewrite_prefix.
+   Name of the column contains the number of digits to be stripped of the
+   userpart of an URI before prepending rewrite_prefix.
 
    Example 2.10. Set carrierroute_strip_col parameter
 ...
 modparam("carrierroute", "carrierroute_strip_col", "strip")
 ...
 
-2.11. carrierroute_rewrite_host_col (string)
+11. carrierroute_rewrite_host_col (string)
 
-   Name of column contains the rewrite prefixes. Here you can
-   define a rewrite prefix for the localpart of the SIP URI. An
-   empty field represents a blacklist entry, anything else is put
-   as domain part into the Request URI of the SIP message.
+   Name of column contains the rewrite prefixes. Here you can define a
+   rewrite prefix for the localpart of the SIP URI. An empty field
+   represents a blacklist entry, anything else is put as domain part into
+   the Request URI of the SIP message.
 
    Example 2.11. Set carrierroute_rewrite_host_col parameter
 ...
-modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host"
-)
+modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host")
 ...
 
-2.12. carrierroute_rewrite_prefix_col (string)
+12. carrierroute_rewrite_prefix_col (string)
 
-   Name of column contains the rewrite prefixes. Here you can
-   define a rewrite prefix for the localpart of the SIP URI.
+   Name of column contains the rewrite prefixes. Here you can define a
+   rewrite prefix for the localpart of the SIP URI.
 
    Example 2.12. Set carrierroute_rewrite_prefix_col parameter
 ...
-modparam("carrierroute", "carrierroute_rewrite_prefix_col", "rewrite_pre
-fix")
+modparam("carrierroute", "carrierroute_rewrite_prefix_col", "rewrite_prefix")
 ...
 
-2.13. carrierroute_rewrite_suffix_col (string)
+13. carrierroute_rewrite_suffix_col (string)
 
-   Name of column contains the rewrite suffixes. Here you can
-   define a rewrite suffix for the localpart of the SIP URI.
+   Name of column contains the rewrite suffixes. Here you can define a
+   rewrite suffix for the localpart of the SIP URI.
 
    Example 2.13. Set carrierroute_rewrite_suffix_col parameter
 ...
-modparam("carrierroute", "carrierroute_rewrite_suffix_col", "rewrite_suf
-fix")
+modparam("carrierroute", "carrierroute_rewrite_suffix_col", "rewrite_suffix")
 ...
 
-2.14. carrierroute_description_col (string)
+14. carrierroute_description_col (string)
 
-   A comment for the route entry, useful for larger routing
-   tables. The comment is also displayed by the fifo cmd
-   "cr_dump_routes".
+   A comment for the route entry, useful for larger routing tables. The
+   comment is also displayed by the fifo cmd "cr_dump_routes".
 
    Example 2.14. Set carrierroute_description_col parameter
 ...
 modparam("carrierroute", "carrierroute_description_col", "description")
 ...
 
-2.15. carrierfailureroute_table (String)
+15. carrierfailureroute_table (String)
 
-   Name of the carrierfailureroute table for the carrierroute
-   module.
+   Name of the carrierfailureroute table for the carrierroute module.
 
    Default value is "carrierfailureroute".
 
    Example 2.15. Set carrierfailureroute_table parameter
 ...
-modparam("carrierroute", "carrierfailureroute_table", "carrierfailurerou
-te")
+modparam("carrierroute", "carrierfailureroute_table", "carrierfailureroute")
 ...
 
-2.16. carrierfailureroute_id_col (string)
+16. carrierfailureroute_id_col (string)
 
    This column contains the unique identifier of a failure route.
 
@@ -1174,7 +1294,7 @@ te")
 modparam("carrierroute", "carrierfailureroute_id_col", "id")
 ...
 
-2.17. carrierfailureroute_carrier_col (string)
+17. carrierfailureroute_carrier_col (string)
 
    This column contains the carrier id.
 
@@ -1183,55 +1303,51 @@ modparam("carrierroute", "carrierfailureroute_id_col", "id")
 modparam("carrierroute", "carrierfailureroute_carrier_col", "carrier")
 ...
 
-2.18. carrierfailureroute_domain_col (string)
+18. carrierfailureroute_domain_col (string)
 
-   This column contains the routing domain id. You can define
-   several routing domains to have different routing rules. Maybe
-   you use domain 0 for normal routing and domain 1 if domain 0
-   failed.
+   This column contains the routing domain id. You can define several
+   routing domains to have different routing rules. Maybe you use domain 0
+   for normal routing and domain 1 if domain 0 failed.
 
    Example 2.18. Set carrierfailureroute_domain_col parameter
 ...
 modparam("carrierroute", "carrierfailureroute_domain_col", "domain")
 ...
 
-2.19. carrierfailureroute_scan_prefix_col (string)
+19. carrierfailureroute_scan_prefix_col (string)
 
-   Name of column contains the the scan prefixes. Scan prexies
-   define the matching portion of a phone number, e.g. we have the
-   scan prefixes 49721 and 49, the called number is 49721913740,
-   it matches 49721, because the longest match is taken. If no
-   prefix matches, the number is not failure routed. To prevent
-   this, an empty prefix value of could be added.
+   Name of column contains the the scan prefixes. Scan prexies define the
+   matching portion of a phone number, e.g. we have the scan prefixes
+   49721 and 49, the called number is 49721913740, it matches 49721,
+   because the longest match is taken. If no prefix matches, the number is
+   not failure routed. To prevent this, an empty prefix value of could be
+   added.
 
    Example 2.19. Set carrierfailureroute_scan_prefix_col parameter
 ...
-modparam("carrierroute", "carrierfailureroute_scan_prefix_col", "scan_pr
-efix")
+modparam("carrierroute", "carrierfailureroute_scan_prefix_col", "scan_prefix")
 ...
 
-2.20. carrierfailureroute_host_name_col (string)
+20. carrierfailureroute_host_name_col (string)
 
    Name of the column containing the host name of the last routing
    destination, using for rules matching.
 
    Example 2.20. Set carrierfailureroute_host_name_col parameter
 ...
-modparam("carrierroute", "carrierfailureroute_host_name_col", "host_name
-")
+modparam("carrierroute", "carrierfailureroute_host_name_col", "host_name")
 ...
 
-2.21. carrierfailureroute_reply_code_col (string)
+21. carrierfailureroute_reply_code_col (string)
 
    This column contains the reply code used for rule matching.
 
    Example 2.21. Set carrierfailureroute_reply_code_col parameter
 ...
-modparam("carrierroute", "carrierfailureroute_reply_code_col", "reply_co
-de")
+modparam("carrierroute", "carrierfailureroute_reply_code_col", "reply_code")
 ...
 
-2.22. carrierfailureroute_flags_col (string)
+22. carrierfailureroute_flags_col (string)
 
    This column contains the flags used for rule matching.
 
@@ -1240,39 +1356,36 @@ de")
 modparam("carrierroute", "carrierfailureroute_flags_col", "flags")
 ...
 
-2.23. carrierfailureroute_mask_col (string)
+23. carrierfailureroute_mask_col (string)
 
-   This column contains the mask that is applied to the message
-   flags before rule matching.
+   This column contains the mask that is applied to the message flags
+   before rule matching.
 
    Example 2.23. Set carrierfailureroute_mask_col parameter
 ...
 modparam("carrierroute", "carrierfailureroute_mask_col", "mask")
 ...
 
-2.24. carrierfailureroute_next_domain_col (string)
+24. carrierfailureroute_next_domain_col (string)
 
-   This column contains the route domain id that should be used
-   for the next routing attempt.
+   This column contains the route domain id that should be used for the
+   next routing attempt.
 
    Example 2.24. Set carrierfailureroute_next_domain_col parameter
 ...
-modparam("carrierroute", "carrierfailureroute_next_domain_col", "next_do
-main")
+modparam("carrierroute", "carrierfailureroute_next_domain_col", "next_domain")
 ...
 
-2.25. carrierfailureroute_description_col (string)
+25. carrierfailureroute_description_col (string)
 
-   A comment for the route entry, useful for larger routing
-   tables.
+   A comment for the route entry, useful for larger routing tables.
 
    Example 2.25. Set carrierfailureroute_description_col parameter
 ...
-modparam("carrierroute", "carrierfailureroute_description_col", "descrip
-tion")
+modparam("carrierroute", "carrierfailureroute_description_col", "description")
 ...
 
-2.26. carrier_name_table (String)
+26. carrier_name_table (String)
 
    Name of the carrier_name table for the carrierroute module.
 
@@ -1283,17 +1396,16 @@ tion")
 modparam("carrierroute", "carrier_name_table", "carrier_name")
 ...
 
-2.27. carrier_name_id_col (string)
+27. carrier_name_id_col (string)
 
-   Name of the column containing the unique identifier of a
-   carrier.
+   Name of the column containing the unique identifier of a carrier.
 
    Example 2.27. Set carrier_name_id_col parameter
 ...
 modparam("carrierroute", "carrier_name_id_col", "id")
 ...
 
-2.28. carrier_name_carrier_col (string)
+28. carrier_name_carrier_col (string)
 
    This column contains the carrier name.
 
@@ -1302,7 +1414,7 @@ modparam("carrierroute", "carrier_name_id_col", "id")
 modparam("carrierroute", "carrier_name_carrier_col", "carrier")
 ...
 
-2.29. domain_name_table (String)
+29. domain_name_table (String)
 
    Name of the domain_name table for the carrierroute module.
 
@@ -1313,17 +1425,16 @@ modparam("carrierroute", "carrier_name_carrier_col", "carrier")
 modparam("carrierroute", "domain_name_table", "domain_name")
 ...
 
-2.30. domain_name_id_col (string)
+30. domain_name_id_col (string)
 
-   Name of the column containing the unique identifier of a
-   domain.
+   Name of the column containing the unique identifier of a domain.
 
    Example 2.30. Set domain_name_id_col parameter
 ...
 modparam("carrierroute", "domain_name_id_col", "id")
 ...
 
-2.31. domain_name_domain_col (string)
+31. domain_name_domain_col (string)
 
    This column contains the domain name.
 
index 9984b72..ab91609 100644 (file)
@@ -86,12 +86,14 @@ static void mod_destroy(void);
 
 /************* Module Exports **********************************************/
 static cmd_export_t cmds[]={
-       {"cr_user_carrier", (cmd_function)cr_load_user_carrier,  3, cr_load_user_carrier_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
-       {"cr_route",        (cmd_function)cr_route,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-       {"cr_route",        (cmd_function)cr_route,              6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-       {"cr_prime_route",  (cmd_function)cr_prime_route,        5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-       {"cr_prime_route",  (cmd_function)cr_prime_route,        6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-       {"cr_next_domain",  (cmd_function)cr_load_next_domain,   6, cr_load_next_domain_fixup,  0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_user_carrier",  (cmd_function)cr_load_user_carrier,  3, cr_load_user_carrier_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_route",         (cmd_function)cr_route,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_route",         (cmd_function)cr_route,              6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_prime_route",   (cmd_function)cr_prime_route,        5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_prime_route",   (cmd_function)cr_prime_route,        6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_nofallback_route",(cmd_function)cr_nofallback_route,     5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_nofallback_route",(cmd_function)cr_nofallback_route,     6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {"cr_next_domain",   (cmd_function)cr_load_next_domain,   6, cr_load_next_domain_fixup,  0, REQUEST_ROUTE | FAILURE_ROUTE },
        {0, 0, 0, 0, 0, 0}
 };
 
index 6d1c271..073579d 100644 (file)
@@ -781,6 +781,11 @@ static int update_route_data_recursor(struct dtrie_node_t *node, str * act_domai
                                                }
                                                if (opts->new_host.len > 0) {
                                                        LM_INFO("deactivating host %.*s\n", rr->host.len, rr->host.s);
+                                                       if ( opts->new_host.s && (strcmp(opts->new_host.s, rr->host.s) == 0)){
+                                                               LM_ERR("Backup host the same as initial host %.*s",rr->host.len, rr->host.s);
+                                                               FIFO_ERR(E_WRONGOPT);
+                                                               return -1;
+                                                       }
                                                        if (opts->new_host.len == 1 && opts->new_host.s[0] == 'a') {
                                                                if ((backup = find_auto_backup(rf, rr)) == NULL) {
                                                                        LM_ERR("didn't find auto backup route\n");
index f832af4..94632f6 100644 (file)
@@ -103,6 +103,8 @@ static enum hash_source hash_fixup(const char * my_hash_source) {
                return shs_to_uri;
        } else if (strcasecmp("to_user", my_hash_source) == 0) {
                return shs_to_user;
+       } else if (strcasecmp("rand", my_hash_source) == 0) {
+               return shs_rand;
        } else {
                return shs_error;
        }
index 74e3c8e..607d7a2 100644 (file)
@@ -52,6 +52,8 @@
 enum hash_algorithm {
        alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
        alg_prime, /*!< hashing algorithm is (right 18 digits of hash_source % prime_number) % max_targets + 1 */
+       alg_crc32_nofallback, /*!< same algorithm as alg_crc32, with only a backup rule, but no fallback tree is chosen
+                           if there is something wrong. */
        alg_error
 };
 
@@ -422,7 +424,25 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
                                }
                        }
                        break;
-               default: 
+               case alg_crc32_nofallback:
+                       if(rf->max_targets == 0) {
+                               LM_ERR("invalid dice_max value\n");
+                               return -1;
+                       }
+                       if ((prob = (hash_func(msg, hash_source, rf->dice_max) + 1)) < 0) {
+                               LM_ERR("could not hash message with CRC32");
+                               return -1;
+                       }
+                       /* Instead of search the whole rule_list if there is something broken
+                        * this function just tries only a backup rule and otherwise
+                        * returns -1. This way we get an error
+                        */
+                       if ((rr = get_rule_by_hash(rf, prob)) == NULL) {
+                               LM_CRIT("no route found\n");
+                               return -1;
+                       }
+                       break;
+               default:
                        LM_ERR("invalid hash algorithm\n");
                        return -1;
        }
@@ -670,6 +690,31 @@ int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
                _rewrite_user, _hsrc, alg_prime, _descavp);
 }
 
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI with the crc32 hash algorithm. The difference
+ * to cr_route is that no fallback rule is chosen if there is something
+ * wrong (like cr_prime_route)
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_nofallback_route(struct sip_msg * _msg, gparam_t *_carrier,
+               gparam_t *_domain, gparam_t *_prefix_matching,
+               gparam_t *_rewrite_user, enum hash_source _hsrc,
+               gparam_t *_dstavp)
+{
+       return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+               _rewrite_user, _hsrc, alg_crc32_nofallback, _dstavp);
+}
+
 
 /**
  * Loads next domain from failure routing table and stores it in an AVP.
index 226adfc..9db6131 100644 (file)
@@ -90,6 +90,28 @@ int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
                gparam_t *_rewrite_user, enum hash_source _hsrc,
                gparam_t *_descavp);
 
+/**
+ *
+ * rewrites the request URI of msg after determining the
+ * new destination URI with the crc32 hash algorithm. The difference
+ * to cr_route is that no fallback rule is chosen if there is something
+ * wrong (behaves like cr_prime_route)
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_nofallback_route(struct sip_msg * _msg, gparam_t *_carrier,
+               gparam_t *_domain, gparam_t *_prefix_matching,
+               gparam_t *_rewrite_user, enum hash_source _hsrc,
+               gparam_t *_dstavp);
+
 
 /**
  * Loads next domain from failure routing table and stores it in an AVP.
index 7814d83..47c5fb1 100644 (file)
@@ -480,8 +480,8 @@ cr_tree_rewrite_uri(tree, domain)
                      <para><emphasis>hash_source</emphasis> - The hash values of the destination set must
             be a contiguous range starting at 1, limited by the
             configuration parameter max_targets. Possible values for
-            hash_source are: call_id, from_uri, from_user, to_uri
-            and to_user.
+            hash_source are: call_id, from_uri, from_user, to_uri,
+            to_user and rand
                    </para>
         </listitem>
         <listitem>
@@ -518,6 +518,11 @@ cr_tree_rewrite_uri(tree, domain)
         order to route requests, it just uses a fixed hash distribution. If
         one of the hash targets is not available, and no backup rule is
         configured, the function will return -1.
+           </para>
+           <para>
+       Please not that this function is deprecated and will be removed in
+       the next stable release. Please consider using the
+       <emphasis>cr_nofallback_route</emphasis> function instead.
            </para>
            <para>Meaning of the parameters is as follows:</para>
            <itemizedlist>
@@ -551,7 +556,7 @@ cr_tree_rewrite_uri(tree, domain)
             be a contiguous range starting at 1, limited by the
             configuration parameter max_targets. Possible values for
             hash_source are: call_id, from_uri, from_user, to_uri
-            and to_user.
+            to_user and rand
                    </para>
         </listitem>
         <listitem>
@@ -562,6 +567,76 @@ cr_tree_rewrite_uri(tree, domain)
            </itemizedlist>
        </section>
 
+               <section>
+           <title>
+               <function moreinfo="none">cr_nofallback_route(carrier, domain, prefix_matching, rewrite_user, hash_source, descavp)</function>
+           </title>
+           <para>
+        This function searches for the longest match for the user given
+        in prefix_matching at the given domain in the given carrier tree.
+        The Request URI is rewritten using rewrite_user and the given
+        hash source and algorithm. Returns -1 if there is no data found
+        or an empty rewrite host on the longest match is found. On success
+        also the description is stored in the given AVP (if obmitted, nothing
+        is stored in an AVP). This is useful if you need some additional
+        informations that belongs to each gw, like the destination or the
+        number of channels.
+        This function is only usable with rewrite_user and prefix_matching
+        containing a valid string. This string needs to be numerical if the match_mode
+        parameter is set to 10.
+           </para>
+           <para>
+               It uses the standard CRC32 algorithm to calculate the hash values. In contrast
+               to the normal <emphasis>cr_route</emphasis> function the backup
+               rules of cr_prime_route is used. This means not the configured
+               probabilities will be used, only a fixed hash distribution. This
+               makes sense to distribute incoming register requests e.g. to a bunch of
+               registrar servers. If one of the hash targets is not available and
+               backup rule is configured, the function will return -1.
+           </para>
+           <para>Meaning of the parameters is as follows:</para>
+           <itemizedlist>
+        <listitem>
+                     <para><emphasis>carrier</emphasis> - The routing tree to be used. Additional to a string
+            any pseudo-variable could be used as input.
+                     </para>
+        </listitem>
+        <listitem>
+                     <para><emphasis>domain</emphasis> - Name of the routing domain to be used.
+            Additional to a string any pseudo-variable could
+            be used as input.
+                     </para>
+        </listitem>
+        <listitem>
+                     <para><emphasis>prefix_matching</emphasis> - User name to be used for prefix matching
+            in the routing tree.
+            Additional to a string any pseudo-variable could
+            be used as input.
+                     </para>
+        </listitem>
+        <listitem>
+                     <para><emphasis>rewrite_user</emphasis> - The user name to be used for applying the
+            rewriting rule. Usually this is the user part of the request
+            URI. Additional to a string any pseudo-variable could
+            be used as input.
+                     </para>
+        </listitem>
+        <listitem>
+                     <para><emphasis>hash_source</emphasis> - The hash values of the destination set must
+            be a contiguous range starting at 1, limited by the
+            configuration parameter max_targets. Possible values for
+            hash_source are: call_id, from_uri, from_user, to_uri
+            and to_user.
+                     </para>
+        </listitem>
+        <listitem>
+                     <para><emphasis>descavp</emphasis> - Name of the AVP where to store the description.
+                       This parameter is optional.
+                     </para>
+        </listitem>
+           </itemizedlist>
+       </section>
+
        <section>
            <title>
                <function moreinfo="none">cr_next_domain(carrier, domain, prefix_matching, host, reply_code, dstavp)</function>
index 8bb9579..25819c5 100644 (file)
 #include "../../lib/kcore/km_crc.h"
 
 #include <ctype.h>
+#include <stdio.h> /* for snprintf */
+#include <stdlib.h> /* for rand */
 
 #include "prime_hash.h"
 
+#define CR_RANDBUF_S 20
+static char cr_randbuf[CR_RANDBUF_S];
 
 static int determine_source(struct sip_msg *msg, enum hash_source source,
                             str *source_string);
@@ -44,6 +48,7 @@ static int validate_msg(struct sip_msg * msg);
 static int determine_call_id (struct sip_msg *msg, str *source_string);
 static int determine_fromto_uri (struct to_body *fromto, str *source_string);
 static int determine_fromto_user (struct to_body *fromto, str *source_string);
+static int determine_fromrand(str* source_string);
 static int first_token (str *source_string);
 
 
@@ -56,6 +61,7 @@ int hash_func (struct sip_msg * msg,
        if(determine_source (msg, source, &source_string) == -1) {
                return -1;
        }
+
        crc32_uint(&source_string, &hash);
 
        ret = hash % denominator;
@@ -128,6 +134,8 @@ static int determine_source (struct sip_msg *msg, enum hash_source source,
                        return determine_fromto_uri (get_to(msg), source_string);
                        case shs_to_user:
                        return determine_fromto_user (get_to(msg), source_string);
+                       case shs_rand:
+                       return determine_fromrand(source_string); /* msg is not needed */
                        default:
                        LM_ERR("unknown hash source %i.\n",
                             (int) source);
@@ -190,6 +198,17 @@ static int determine_fromto_user (struct to_body *fromto, str *source_string) {
        return 0;
 }
 
+static int determine_fromrand(str* source_string){
+
+       snprintf(&cr_randbuf[0], CR_RANDBUF_S , "%d", rand());
+
+       LM_NOTICE("randbuf is %s\n", cr_randbuf);
+       source_string->s = cr_randbuf;
+       source_string->len = strlen(source_string->s);
+
+       return 0;
+}
+
 static int first_token (str *source_string) {
        size_t len;
 
index b059742..b383046 100644 (file)
@@ -45,6 +45,7 @@
  * - \b shs_from_user   the username part of the URI in the From header field
  * - \b shs_to_uri      the entire URI in the To header field
  * - \b shs_to_user     the username part of the URI in the To header field
+ * - \b shs_rand       some random data which is not related to any header field
  * - \b shs_error       no hash specified
 */
 enum hash_source {
@@ -53,6 +54,7 @@ enum hash_source {
        shs_from_user,
        shs_to_uri,
        shs_to_user,
+       shs_rand,
        shs_error
 };
 
diff --git a/modules/pdb/Makefile b/modules/pdb/Makefile
new file mode 100644 (file)
index 0000000..00111fc
--- /dev/null
@@ -0,0 +1,14 @@
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=pdb.so
+
+LIBS=
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi $(SERLIBPATH)/kcore/kcore
+
+include ../../Makefile.modules
diff --git a/modules/pdb/README b/modules/pdb/README
new file mode 100644 (file)
index 0000000..47e7f8a
--- /dev/null
@@ -0,0 +1,154 @@
+pdb Module
+
+Hardy Kahl
+
+   1&1 Internet AG
+
+Edited by
+
+Henning Westerholt
+
+   1&1 Internet AG
+   <henning.westerholt@1und1.de>
+
+   Copyright 2009 1&1 Internet AG
+   Revision History
+   Revision $Revision: 4863 $ $Date: 2008-09-05 13:11:33 +0200
+                              (Fri, 05 Sep 2008) $
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. timeout (integer)
+              1.3.2. server (string)
+
+        1.4. Exported Functions
+
+              1.4.1. pdb_query (string query, string dstavp)
+
+        1.5. MI Commands
+
+              1.5.1. pdb_status
+              1.5.2. pdb_activate
+              1.5.3. pdb_deactivate
+
+   List of Examples
+
+   1.1. Set timeout parameter
+   1.2. Set server parameter
+   1.3. pdb_query usage
+   1.4. pdb_status usage
+   1.5. pdb_activate usage
+   1.6. pdb_deactivate usage
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   The pdb module allows Kamailio to send queries to a list of
+   servers and store the answer in an AVP. The idea is to ask all
+   servers in parallel and use the first answer, that comes back.
+   A timeout for the query can be defined in milliseconds. The
+   queying can be activated and deactivated using FIFO commands.
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The module depends on the following modules (in the other words
+   the listed modules must be loaded before this module):
+     * none
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running Kamailio with this module loaded:
+     * none
+
+1.3. Exported Parameters
+
+1.3.1. timeout (integer)
+
+   This is the timeout in milliseconds for the pdb_query function.
+
+   Default value is "50".
+
+   Example 1.1. Set timeout parameter
+...
+modparam("pdb", "timeout", 10)
+...
+
+1.3.2. server (string)
+
+   This is the list of servers to be used by the pdb_query
+   function. Queries will be sent in parallel to all servers
+   configured in this list. This parameter is mandatory.
+
+   Example 1.2. Set server parameter
+...
+modparam("pdb", "server", "localhost:10001,host.name:10001,192.168.1.
+7:10002")
+...
+
+1.4. Exported Functions
+
+1.4.1.  pdb_query (string query, string dstavp)
+
+   Sends the query string to all configured servers and stores the
+   answer in dstavp. If it takes more than the configured timeout,
+   false is returned. Pseudo-variables or AVPs can be used for the
+   query string. The answer must consist of the null terminated
+   query string followed by a two byte integer value in network
+   byte order. The integer value will be stored in the given AVP.
+
+   Example 1.3. pdb_query usage
+...
+# query external service for routing information
+if (!pdb_query("$rU", "$avp(i:82)"))
+  $avp(i:82) = 0; # default routing
+}
+cr_route("$avp(i:82)", "$rd", "$rU", "$rU", "call_id");
+...
+
+1.5. MI Commands
+
+1.5.1.  pdb_status
+
+   Prints the status of the module. This can either be "active" or
+   "deactivated".
+
+   Example 1.4. pdb_status usage
+...
+kamctl fifo pdb_status
+...
+
+1.5.2.  pdb_activate
+
+   Activates the module. This is the default after loading the
+   module.
+
+   Example 1.5. pdb_activate usage
+...
+kamctl fifo pdb_activate
+...
+
+1.5.3.  pdb_deactivate
+
+   Deactivates the module. No more queries are performed until it
+   is activated again. As long as the module is deactivated, the
+   pdb_query function will return -1.
+
+   Example 1.6. pdb_deactivate usage
+...
+kamctl fifo pdb_deactivate
+...
diff --git a/modules/pdb/doc/pdb.xml b/modules/pdb/doc/pdb.xml
new file mode 100644 (file)
index 0000000..082fa91
--- /dev/null
@@ -0,0 +1,41 @@
+<?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" [
+
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+       <bookinfo>
+       <title>pdb Module</title>
+       <productname class="trade">&kamailioname;</productname>        
+       <authorgroup>
+               <author>
+               <firstname>Hardy</firstname>
+               <surname>Kahl</surname>
+               <affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+               </author>
+               <editor>
+               <firstname>Henning</firstname>
+               <surname>Westerholt</surname>
+               <affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+               <email>henning.westerholt@1und1.de</email>
+               </editor>
+       </authorgroup>
+       <copyright>
+               <year>2009</year>
+               <holder>1&amp;1 Internet AG</holder>
+       </copyright>
+       <revhistory>
+               <revision>
+               <revnumber>$Revision: 4863 $</revnumber>
+               <date>$Date: 2008-09-05 13:11:33 +0200 (Fri, 05 Sep 2008) $</date>
+               </revision>
+       </revhistory>
+       </bookinfo>
+       <toc></toc>
+
+       <xi:include href="pdb_admin.xml"/>
+</book>
diff --git a/modules/pdb/doc/pdb_admin.xml b/modules/pdb/doc/pdb_admin.xml
new file mode 100644 (file)
index 0000000..468fda4
--- /dev/null
@@ -0,0 +1,166 @@
+<chapter>
+       <title>&adminguide;</title>
+       
+       <section>
+       <title>Overview</title>
+       <para>
+       The pdb module allows Kamailio to send queries to a list of servers
+  and store the answer in an AVP. The idea is to ask all servers in
+  parallel and use the first answer, that comes back. A timeout for the
+       query can be defined in milliseconds. The queying can be activated and
+  deactivated using FIFO commands.
+       </para>
+       </section>
+
+       <section>
+               <title>Dependencies</title>
+               <section>
+                       <title>&kamailio; Modules</title>
+                       <para>
+                       The module depends on the following modules (in the other words 
+                       the listed modules must be loaded before this module):
+                       </para>
+                       <itemizedlist>
+                       <listitem>
+                               <para><emphasis>none</emphasis></para>
+                       </listitem>
+                       </itemizedlist>
+               </section>
+               <section>
+                       <title>External Libraries or Applications</title>
+                       <para>
+                       The following libraries or applications must be installed 
+                       before running &kamailio; with this module loaded:
+                       </para>
+                       <itemizedlist>
+                               <listitem>
+                               <para><emphasis>none</emphasis></para>
+                               </listitem>
+                       </itemizedlist>
+               </section>
+       </section>
+
+
+       <section>
+       <title>Exported Parameters</title>
+    <section>
+           <title><varname>timeout</varname> (integer)</title>
+           <para>
+                       This is the timeout in milliseconds for the pdb_query function.
+           </para>
+           <para>
+                   <emphasis>
+                           Default value is <quote>50</quote>.
+                   </emphasis>
+           </para>
+           <example>
+                   <title>Set <varname>timeout</varname> parameter</title>
+                   <programlisting format="linespecific">
+...
+modparam("pdb", "timeout", 10)
+...
+                   </programlisting>
+           </example>
+    </section>
+    <section>
+           <title><varname>server</varname> (string)</title>
+           <para>
+                       This is the list of servers to be used by the pdb_query function.
+      Queries will be sent in parallel to all servers configured in this list.
+                       This parameter is mandatory.
+           </para>
+           <example>
+                   <title>Set <varname>server</varname> parameter</title>
+                   <programlisting format="linespecific">
+...
+modparam("pdb", "server", "localhost:10001,host.name:10001,192.168.1.7:10002")
+...
+                   </programlisting>
+           </example>
+    </section>
+       </section>
+       <section>
+               <title>Exported Functions</title>
+               <section>
+           <title>
+                               <function moreinfo="none">pdb_query (string query, string dstavp)</function>
+           </title>
+           <para>
+                               Sends the query string to all configured servers and stores the answer in
+                               dstavp. If it takes more than the configured timeout, false is returned.
+                               
+                               Pseudo-variables or AVPs can be used for the query string.
+                               
+                               The answer must consist of the null terminated query string followed by
+                               a two byte integer value in network byte order. The integer value will
+                               be stored in the given AVP.
+           </para>
+                       <example>
+                               <title><function>pdb_query</function> usage</title>
+                               <programlisting format="linespecific">
+...
+# query external service for routing information
+if (!pdb_query("$rU", "$avp(i:82)"))
+  $avp(i:82) = 0; # default routing
+}
+cr_route("$avp(i:82)", "$rd", "$rU", "$rU", "call_id");
+...
+                               </programlisting>
+                       </example>
+               </section>
+       </section>
+       <section>
+               <title><acronym>MI</acronym> Commands</title>
+               <section>
+           <title>
+                               <function moreinfo="none">pdb_status</function>
+           </title>
+           <para>
+                               Prints the status of the module.
+                               This can either be "active" or "deactivated".
+           </para>
+                       <example>
+                               <title><function>pdb_status</function> usage</title>
+                               <programlisting format="linespecific">
+...
+kamctl fifo pdb_status
+...
+                               </programlisting>
+           </example>
+               </section>
+               <section>
+           <title>
+                               <function moreinfo="none">pdb_activate</function>
+           </title>
+           <para>
+                               Activates the module. This is the default after loading the module.
+           </para>
+                       <example>
+                               <title><function>pdb_activate</function> usage</title>
+                               <programlisting format="linespecific">
+...
+kamctl fifo pdb_activate
+...
+                               </programlisting>
+           </example>
+               </section>
+               <section>
+           <title>
+                               <function moreinfo="none">pdb_deactivate</function>
+           </title>
+           <para>
+                               Deactivates the module. No more queries are performed until it is
+                               activated again. As long as the module is deactivated, the
+                               pdb_query function will return -1.
+           </para>
+                       <example>
+                               <title><function>pdb_deactivate</function> usage</title>
+                               <programlisting format="linespecific">
+...
+kamctl fifo pdb_deactivate
+...
+                               </programlisting>
+           </example>
+               </section>
+       </section>
+</chapter>
diff --git a/modules/pdb/pdb.c b/modules/pdb/pdb.c
new file mode 100644 (file)
index 0000000..9720a13
--- /dev/null
@@ -0,0 +1,690 @@
+/*
+ * $Id: carrierroute.c 4712 2008-08-22 17:05:16Z henningw $
+ *
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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
+ *
+ * sip-router 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
+ */
+
+/*!
+ * @file pdb.c
+ * @brief Contains the functions exported by the module.
+ */
+
+
+#include "../../sr_module.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include <sys/time.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+MODULE_VERSION
+
+
+#define NETBUFSIZE 200
+
+
+static char* modp_server = NULL;  /*!< format: <host>:<port>,... */
+static int timeout = 50;  /*!< timeout for queries in milliseconds */
+static int timeoutlogs = -10;  /*!< for aggregating timeout logs */
+static int *active = NULL;
+
+
+/*!
+ * Generic parameter that holds a string, an int or an pseudo-variable
+ * @todo replace this with gparam_t
+ */
+struct multiparam_t {
+       enum {
+               MP_INT,
+               MP_STR,
+               MP_AVP,
+               MP_PVE,
+       } type;
+       union {
+               int n;
+               str s;
+               struct {
+                       unsigned short flags;
+                       int_str name;
+               } a;
+               pv_elem_t *p;
+       } u;
+};
+
+
+/* ---- exported commands: */
+static int pdb_query(struct sip_msg *_msg, struct multiparam_t *_number, struct multiparam_t *_dstavp);
+
+/* ---- fixup functions: */
+static int pdb_query_fixup(void **arg, int arg_no);
+
+/* ---- module init functions: */
+static int mod_init(void);
+static int child_init(int rank);
+static int mi_child_init(void);
+static void mod_destroy();
+
+/* --- fifo functions */
+struct mi_root * mi_pdb_status(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_status */
+struct mi_root * mi_pdb_activate(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_activate */
+struct mi_root * mi_pdb_deactivate(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_deactivate */
+
+
+static cmd_export_t cmds[]={
+       { "pdb_query", (cmd_function)pdb_query, 2, pdb_query_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+       {0,0,0,0,0,0}
+};
+
+
+static param_export_t params[] = {
+       {"server",      STR_PARAM, &modp_server },
+       {"timeout",     INT_PARAM, &timeout },
+       {0, 0, 0 }
+};
+
+
+/* Exported MI functions */
+static mi_export_t mi_cmds[] = {
+       { "pdb_status", mi_pdb_status, MI_NO_INPUT_FLAG, 0, mi_child_init },
+       { "pdb_activate", mi_pdb_activate, MI_NO_INPUT_FLAG, 0, mi_child_init },
+       { "pdb_deactivate", mi_pdb_deactivate, MI_NO_INPUT_FLAG, 0, mi_child_init },
+       { 0, 0, 0, 0, 0}
+};
+
+
+struct module_exports exports = {
+       "pdb",
+       DEFAULT_DLFLAGS, /* dlopen flags */
+       cmds,       /* Exported functions */
+       params,     /* Export parameters */
+       0,          /* exported statistics */
+       mi_cmds,    /* exported MI functions */
+       0,          /* exported pseudo-variables */
+       0,          /* extra processes */
+       mod_init,   /* Module initialization function */
+       0,          /* Response function */
+       mod_destroy,/* Destroy function */
+       child_init  /* Child initialization function */
+};
+
+
+struct server_item_t {
+       struct server_item_t *next;
+       char *host;
+       unsigned short int port;
+       struct sockaddr_in dstaddr;
+       socklen_t dstaddrlen;
+       int sock;
+};
+
+
+struct server_list_t {
+       struct server_item_t *head;
+       int nserver;
+       struct pollfd *fds;
+};
+
+
+/*! global server list */
+static struct server_list_t *server_list;
+
+
+/*!
+ * \return 1 if query for the number succeded and the avp with the corresponding carrier id was set,
+ * -1 otherwise
+ */
+static int pdb_query(struct sip_msg *_msg, struct multiparam_t *_number, struct multiparam_t *_dstavp)
+{
+       struct timeval tstart, tnow;
+       struct server_item_t *server;
+       short int carrierid;
+       char buf[NETBUFSIZE+1+sizeof(carrierid)];
+       size_t reqlen;
+       int_str avp_val;
+       struct usr_avp *avp;
+       int i, ret, nflush;
+       long int td;
+       str number = { .len = 0, .s = NULL};
+
+       if ((active == NULL) || (*active == 0)) return -1;
+
+       switch (_number->type) {
+       case MP_STR:
+               number = _number->u.s;
+               break;
+       case MP_AVP:
+               avp = search_first_avp(_number->u.a.flags, _number->u.a.name, &avp_val, 0);
+               if (!avp) {
+                       LM_ERR("cannot find AVP '%.*s'\n", _number->u.a.name.s.len, _number->u.a.name.s.s);
+                       return -1;
+               }
+               if ((avp->flags&AVP_VAL_STR)==0) {
+                       LM_ERR("cannot process integer value in AVP '%.*s'\n", _number->u.a.name.s.len, _number->u.a.name.s.s);
+                       return -1;
+               }
+               else number = avp_val.s;
+               break;
+       case MP_PVE:
+               if (pv_printf_s(_msg, _number->u.p, &number)<0) {
+                       LM_ERR("cannot print the number\n");
+                       return -1;
+               }
+               break;
+       default:
+               LM_ERR("invalid number type\n");
+               return -1;
+       }
+
+       LM_DBG("querying '%.*s'...\n", number.len, number.s);
+       if (server_list == NULL) return -1;
+       if (server_list->fds == NULL) return -1;
+
+       if (gettimeofday(&tstart, NULL) != 0) {
+               LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+               return -1;
+       }
+
+       /* clear recv buffer */
+       server = server_list->head;
+       while (server) {
+               nflush = 0;
+               while (recv(server->sock, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) {
+                       nflush++;
+                       if (gettimeofday(&tnow, NULL) != 0) {
+                               LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+                               return -1;
+                       }
+                       td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
+                       if (td > timeout) {
+                               LM_WARN("exceeded timeout while flushing recv buffer.\n");
+                               return -1;
+                       }
+               }
+               LM_DBG("flushed %d packets for '%s:%d'\n", nflush, server->host, server->port);
+               server = server ->next;
+       }
+
+       /* prepare request */
+       reqlen = number.len + 1; /* include null termination */
+       if (reqlen > NETBUFSIZE) {
+               LM_ERR("number too long '%.*s'.\n", number.len, number.s);
+               return -1;
+       }
+       strncpy(buf, number.s, number.len);
+       buf[number.len] = '\0';
+
+       /* send request to all servers */
+       server = server_list->head;
+       while (server) {
+               LM_DBG("sending request to '%s:%d'\n", server->host, server->port);
+               ret=sendto(server->sock, buf, reqlen, MSG_DONTWAIT, (struct sockaddr *)&(server->dstaddr), server->dstaddrlen);
+               if (ret < 0) {
+                       LM_ERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
+               }
+               server = server->next;
+       }
+               
+       /* wait for response */
+       for (;;) {
+               if (gettimeofday(&tnow, NULL) != 0) {
+                       LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+                       return -1;
+               }
+               td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
+               if (td > timeout) {
+                       timeoutlogs++;
+                       if (timeoutlogs<0) {
+                               LM_WARN("exceeded timeout while waiting for response.\n");
+                       }
+                       else if (timeoutlogs>1000) {
+                               LM_WARN("exceeded timeout %d times while waiting for response.\n", timeoutlogs);
+                               timeoutlogs=0;
+                       }
+                       return -1;
+               }
+               
+               ret=poll(server_list->fds, server_list->nserver, timeout-td);
+               for (i=0; i<server_list->nserver; i++) {
+                       if (server_list->fds[i].revents & POLLIN) {
+                               if (recv(server_list->fds[i].fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) { /* do not block - just in case select/poll was wrong */
+                                       buf[NETBUFSIZE] = '\0';
+                                       if (strncmp(buf, number.s, number.len) == 0) {
+                                               carrierid=ntohs(*((short int *)&(buf[reqlen]))); /* convert to host byte order */
+                                               goto found;
+                                       }
+                               }
+                       }
+                       server_list->fds[i].revents = 0;
+               }
+       }
+
+       found:
+       if (timeoutlogs>0) {
+               LM_WARN("exceeded timeout while waiting for response (buffered %d lines).\n", timeoutlogs);
+               timeoutlogs=-10;
+       }
+       if (gettimeofday(&tnow, NULL) == 0) {
+               LM_INFO("got an answer in %f ms\n", ((double)(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000))/1000);
+       }
+       avp_val.n=carrierid;
+       /* set avp ! */
+       if (add_avp(_dstavp->u.a.flags, _dstavp->u.a.name, avp_val)<0) {
+               LM_ERR("add AVP failed\n");
+               return -1;
+       }
+
+       return 1;
+}
+
+
+/*!
+ * fixes the module functions' parameters if it is a phone number.
+ * supports string, pseudo-variables and AVPs.
+ *
+ * @param param the parameter
+ * @return 0 on success, -1 on failure
+ */
+static int mp_fixup(void ** param) {
+       pv_spec_t avp_spec;
+       struct multiparam_t *mp;
+       str s;
+
+       mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
+       if (mp == NULL) {
+               PKG_MEM_ERROR;
+               return -1;
+       }
+       memset(mp, 0, sizeof(struct multiparam_t));
+       
+       s.s = (char *)(*param);
+       s.len = strlen(s.s);
+
+       if (s.s[0]!='$') {
+               /* This is string */
+               mp->type=MP_STR;
+               mp->u.s=s;
+       }
+       else {
+               /* This is a pseudo-variable */
+               if (pv_parse_spec(&s, &avp_spec)==0) {
+                       LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param));
+                       pkg_free(mp);
+                       return -1;
+               }
+               if (avp_spec.type==PVT_AVP) {
+                       /* This is an AVP - could be an id or name */
+                       mp->type=MP_AVP;
+                       if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
+                               LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
+                               pkg_free(mp);
+                               return -1;
+                       }
+               } else {
+                       mp->type=MP_PVE;
+                       if(pv_parse_format(&s, &(mp->u.p))<0) {
+                               LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param));
+                               pkg_free(mp);
+                               return -1;
+                       }
+               }
+       }
+       *param = (void*)mp;
+
+       return 0;
+}
+
+
+/*!
+ * fixes the module functions' parameters in case of AVP names.
+ *
+ * @param param the parameter
+ * @return 0 on success, -1 on failure
+ */
+static int avp_name_fixup(void ** param) {
+       pv_spec_t avp_spec;
+       struct multiparam_t *mp;
+       str s;
+
+       s.s = (char *)(*param);
+       s.len = strlen(s.s);
+       if (s.len <= 0) return -1;
+       if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
+               LM_ERR("Malformed or non AVP definition <%s>\n", (char *)(*param));
+               return -1;
+       }
+       
+       mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
+       if (mp == NULL) {
+               PKG_MEM_ERROR;
+               return -1;
+       }
+       memset(mp, 0, sizeof(struct multiparam_t));
+       
+       mp->type=MP_AVP;
+       if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
+               LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
+               pkg_free(mp);
+               return -1;
+       }
+
+       *param = (void*)mp;
+       
+       return 0;
+}
+
+
+static int pdb_query_fixup(void **arg, int arg_no)
+{
+       if (arg_no == 1) {
+               /* phone number */
+               if (mp_fixup(arg) < 0) {
+                       LM_ERR("cannot fixup parameter %d\n", arg_no);
+                       return -1;
+               }
+       }
+       else if (arg_no == 2) {
+               /* destination avp name */
+               if (avp_name_fixup(arg) < 0) {
+                       LM_ERR("cannot fixup parameter %d\n", arg_no);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/*!
+ * Adds new server structure to server list.
+ * \return 0 on success -1 otherwise
+ */
+static int add_server(char *host, char *port)
+{
+       long int ret;
+       struct server_item_t *server;
+
+       LM_DBG("adding server '%s:%s'\n", host, port);
+       server= pkg_malloc(sizeof(struct server_item_t));
+       if (server == NULL) {
+               PKG_MEM_ERROR;
+               return -1;
+       }
+       memset(server, 0, sizeof(struct server_item_t));
+
+       server->next = server_list->head;
+       server_list->head = server;
+
+       server->host = pkg_malloc(strlen(host)+1);
+       if (server->host == NULL) {
+               PKG_MEM_ERROR;
+               return -1;
+       }
+       strcpy(server->host, host);
+
+       ret=strtol(port, NULL, 10);
+       if ((ret<0) || (ret>65535)) {
+               LM_ERR("invalid port '%s'\n", port);
+               return -1;
+       }
+       server->port=ret;
+
+       return 0;
+}
+
+
+/*!
+ * Prepares data structures for all configured servers.
+ *  \return 0 on success, -1 otherwise
+ */
+static int prepare_server(void)
+{
+       char *p, *dst, *end, *sep, *host, *port;
+
+       if (modp_server == NULL) {
+               LM_ERR("server parameter missing.\n");
+               return -1;
+       }
+
+       /* Remove white space from db_sources */
+       for (p = modp_server, dst = modp_server; *p != '\0'; ++p, ++dst) {
+               while (isspace(*p)) ++p;
+               *dst = *p;
+       }
+       *dst = '\0';
+
+       p = modp_server;
+       end = p + strlen(p);
+
+       while (p < end) {
+               sep = strchr(p, ':');
+               if (sep == NULL) {
+                       LM_ERR("syntax error in sources parameter.\n");
+                       return -1;
+               }
+               host = p;
+               *sep = '\0';
+               p = sep + 1;
+
+               sep = strchr(p, ',');
+               if (sep == NULL) sep = end;
+               port = p;
+               *sep = '\0';
+               p = sep + 1;
+
+               if (add_server(host, port) != 0)  return -1;
+       }
+
+       return 0;
+}
+
+
+static void destroy_server_list(void)
+{
+       if (server_list) {
+               while (server_list->head) {
+                       struct server_item_t *server = server_list->head;
+                       server_list->head = server->next;
+                       if (server->host) pkg_free(server->host);
+                       pkg_free(server);
+               }
+               pkg_free(server_list);
+               server_list = NULL;
+       }
+}
+
+
+/*!
+ * Allocates memory and builds a list of all servers defined in module parameter.
+ * \return 0 on success, -1 otherwise
+ */
+static int init_server_list(void)
+{
+       server_list = pkg_malloc(sizeof(struct server_list_t));
+       if (server_list == NULL) {
+               PKG_MEM_ERROR;
+               return -1;
+       }
+       memset(server_list, 0, sizeof(struct server_list_t));
+
+       if (prepare_server() != 0) {
+               destroy_server_list();
+               return -1;
+       }
+
+  return 0;
+}
+
+
+/*!
+ * Initializes sockets for all servers in server list.
+ * \return 0 on success, -1 otherwise
+ */
+static int init_server_socket(void)
+{
+       struct server_item_t *server;
+       struct hostent *hp;
+       int i;
+
+       if (server_list) {
+               server_list->nserver=0;
+               server = server_list->head;
+               while (server) {
+                       LM_DBG("initializing socket for '%s:%d'\n", server->host, server->port);
+                       server->sock = socket(AF_INET, SOCK_DGRAM, 0);
+                       if (server->sock<0) {
+                               LM_ERR("socket() failed with errno=%d (%s).\n", errno, strerror(errno));
+                               return -1;
+                       }
+
+                       memset(&(server->dstaddr), 0, sizeof(server->dstaddr));
+                       server->dstaddr.sin_family = AF_INET;
+                       server->dstaddr.sin_port = htons(server->port);
+                       hp = gethostbyname(server->host);
+                       if (hp == NULL) {
+                               LM_ERR("gethostbyname(%s) failed with h_errno=%d.\n", server->host, h_errno);
+                               close(server->sock);
+                               server->sock=0;
+                               return -1;
+                       }
+                       memcpy(&(server->dstaddr.sin_addr.s_addr), hp->h_addr, hp->h_length);
+                       server->dstaddrlen=sizeof(server->dstaddr);
+
+                       server = server->next;
+                       server_list->nserver++;
+               }
+
+               LM_DBG("got %d server in list\n", server_list->nserver);
+               server_list->fds = pkg_malloc(sizeof(struct pollfd)*server_list->nserver);
+               if (server_list->fds == NULL) {
+                       PKG_MEM_ERROR;
+                       return -1;
+               }
+               memset(server_list->fds, 0, sizeof(struct pollfd)*server_list->nserver);
+
+               i=0;
+               server = server_list->head;
+               while (server) {
+                       server_list->fds[i].fd=server->sock;
+                       server_list->fds[i].events=POLLIN;
+                       server = server->next;
+                       i++;
+               }
+       }
+       return 0;
+}
+
+
+/*!
+ * Destroys sockets for all servers in server list.
+ */
+static void destroy_server_socket(void)
+{
+       if (server_list) {
+               struct server_item_t *server = server_list->head;
+               while (server) {
+                       if (server->sock>0) close(server->sock);
+                       server = server->next;
+               }
+               if (server_list->fds) pkg_free(server_list->fds);
+       }
+}
+
+
+struct mi_root * mi_pdb_status(struct mi_root* cmd, void* param)
+{
+       struct mi_root * root = NULL;
+       struct mi_node * node = NULL;
+
+       if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
+
+       root = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+       if (root == NULL) return NULL;
+
+       if (*active) node = addf_mi_node_child(&root->node, 0, 0, 0, "pdb is active");
+       else node = addf_mi_node_child(&root->node, 0, 0, 0, "pdb is deactivated");
+       if (node == NULL) {
+               free_mi_tree(root);
+               return NULL;
+       }
+
+       return root;
+}
+
+
+struct mi_root * mi_pdb_deactivate(struct mi_root* cmd, void* param)
+{
+       if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
+
+       *active=0;
+       return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+struct mi_root * mi_pdb_activate(struct mi_root* cmd, void* param)
+{
+       if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
+
+       *active=1;
+       return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+
+
+static int mod_init(void)
+{
+       active = shm_malloc(sizeof(*active));
+       if (active == NULL) {
+               SHM_MEM_ERROR;
+               return -1;
+       }
+       *active=1;
+
+       if (init_server_list() != 0) {
+               shm_free(active);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int child_init (int rank)
+{
+       if (init_server_socket() != 0) return -1;
+       return 0;
+}
+
+
+static int mi_child_init(void)
+{
+       if (init_server_socket() != 0) return -1;
+       return 0;
+}
+
+
+static void mod_destroy(void)
+{
+       destroy_server_socket();
+       destroy_server_list();
+       if (active) shm_free(active);
+}
index 909a1bc..51f9005 100644 (file)
@@ -142,7 +142,13 @@ void free_cell( struct cell* dead_cell )
                cbs_tmp = cbs;
                cbs = cbs->next;
                if (cbs_tmp->release) {
+                       /* It is safer to release the shm memory lock
+                        * otherwise the release function must to be aware of
+                        * the lock state (Miklos)
+                        */
+                       shm_unlock();
                        cbs_tmp->release(cbs_tmp->param);
+                       shm_lock();
                }
                shm_free_unsafe( cbs_tmp );
        }
@@ -266,6 +272,7 @@ struct cell*  build_cell( struct sip_msg* p_msg )
        struct cell* new_cell;
        int          sip_msg_len;
        avp_list_t* old;
+       struct tm_callback *cbs, *cbs_tmp;
 
        /* allocs a new cell */
        /* if syn_branch==0 add space for md5 (MD5_LEN -sizeof(struct cell.md5)) */
@@ -343,6 +350,24 @@ struct cell*  build_cell( struct sip_msg* p_msg )
        return new_cell;
 
 error:
+       /* Other modules may have already registered some
+        * transaction callbacks and may also allocated
+        * additional memory for their parameters,
+        * hence TMCB_DESTROY needs to be called. (Miklos)
+        */
+       if (unlikely(has_tran_tmcbs(new_cell, TMCB_DESTROY)))
+               run_trans_callbacks(TMCB_DESTROY, new_cell, 0, 0, 0);
+
+       /* free the callback list */
+       for( cbs=(struct tm_callback*)new_cell->tmcb_hl.first ; cbs ; ) {
+               cbs_tmp = cbs;
+               cbs = cbs->next;
+               if (cbs_tmp->release) {
+                       cbs_tmp->release(cbs_tmp->param);
+               }
+               shm_free( cbs_tmp );
+       }
+       
        destroy_avp_list(&new_cell->user_avps_from);
        destroy_avp_list(&new_cell->user_avps_to);
        destroy_avp_list(&new_cell->uri_avps_from);
index 66ada90..5933ad6 100644 (file)
@@ -413,6 +413,8 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
                t->uac[branch].request.dst.send_sock =
                get_send_socket( request, &t->uac[branch].request.dst.to,
                                                                t->uac[branch].request.dst.proto);
+               t->uac[branch].request.dst.send_flags=request?
+                                                                                               request->fwd_send_flags:0;
        }else {
 #ifdef USE_DNS_FAILOVER
                if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
@@ -1083,15 +1085,16 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
        } else try_new=0;
 
        init_branch_iterator();
-       while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri.s, &dst_uri.len, &si))) {
+       while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri.s,
+                                                                               &dst_uri.len, &si))) {
                try_new++;
                p_msg->force_send_socket = si;
                getbflagsval(get_branch_iterator(), &bflags);
                setbflagsval(0, bflags);
 
                branch_ret=add_uac( t, p_msg, &current_uri, 
-                                   (dst_uri.len) ? (&dst_uri) : &current_uri, 
-                                   proxy, proto);
+                                                       (dst_uri.len) ? (&dst_uri) : &current_uri, 
+                                                       proxy, proto);
                /* 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 72c14c7..1fe5755 100644 (file)
@@ -1218,6 +1218,7 @@ int init_rb( struct retr_buf *rb, struct sip_msg *msg)
 #ifdef USE_COMP
        rb->dst.comp=via->comp_no;
 #endif
+       rb->dst.send_flags=msg->rpl_send_flags;
        /* turn off mhomed for generating replies -- they are ideally sent to where
           request came from to make life with NATs and other beasts easier
        */
index e070cd5..cd3e1e9 100644 (file)
@@ -238,7 +238,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 #ifdef USE_DNS_FAILOVER
        if (cfg_get(core, core_cfg, use_dns_failover)){
                dns_srv_handle_init(&dns_h);
-               if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock,
+               if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, 0,
                                                        uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
                                || (dst.send_sock==0)){
                        dns_srv_handle_put(&dns_h);
@@ -249,7 +249,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
                }
                dns_srv_handle_put(&dns_h); /* not needed anymore */
        }else{
-               if ((uri2dst2(0, &dst, uac_r->dialog->send_sock,
+               if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, 0,
                                                uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
                                (dst.send_sock==0)){
                        ser_error = E_NO_SOCKET;
@@ -259,7 +259,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
                }
        }
 #else /* USE_DNS_FAILOVER */
-       if ((uri2dst2(&dst, uac_r->dialog->send_sock,
+       if ((uri2dst2(&dst, uac_r->dialog->send_sock, 0,
                                        uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
                        (dst.send_sock==0)){
                ser_error = E_NO_SOCKET;
index 0c7afaf..e0cabcf 100644 (file)
@@ -221,6 +221,7 @@ inline static int get_uri_send_info(str* uri, str* host, unsigned short* port,
  *         dst   - will be filled
  *         force_send_sock - if 0 dst->send_sock will be set to the default 
  *                 (see get_send_socket2()) 
+ *         sflags - send flags
  *         uri   - uri in str form
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *                 uri_proto, otherwise the uri proto will be used if set or
@@ -231,10 +232,12 @@ inline static int get_uri_send_info(str* uri, str* host, unsigned short* port,
 inline static struct dest_info *uri2dst2(struct dns_srv_handle* dns_h,
                                                                                struct dest_info* dst,
                                                                                struct socket_info *force_send_socket,
+                                                                               snd_flags_t sflags,
                                                                                str *uri, int proto )
 #else
 inline static struct dest_info *uri2dst2(struct dest_info* dst,
                                                                                struct socket_info *force_send_socket,
+                                                                               snd_flags_t sflags,
                                                                                str *uri, int proto )
 #endif
 {
@@ -268,6 +271,7 @@ inline static struct dest_info *uri2dst2(struct dest_info* dst,
 #ifdef USE_COMP
        dst->comp=parsed_uri.comp;
 #endif
+       dst->send_flags=sflags;
 #ifdef HONOR_MADDR
        if (parsed_uri.maddr_val.s && parsed_uri.maddr_val.len) {
                host=&parsed_uri.maddr_val;
@@ -336,9 +340,10 @@ inline static struct dest_info *uri2dst2(struct dest_info* dst,
  *                 null. If null or use_dns_failover==0 normal dns lookup will
  *                 be performed (no failover).
  *         dst   - will be filled
- *         msg   -  sip message used to set dst->send_sock, if 0 dst->send_sock
- *                 will be set to the default w/o using msg->force_send_socket 
- *                 (see get_send_socket()) 
+ *         msg   -  sip message used to set dst->send_sock and dst->send_flags,
+ *                 if 0 dst->send_sock will be set to the default w/o using 
+ *                  msg->force_send_socket (see get_send_socket()) and the 
+ *                  send_flags will be set to 0.
  *         uri   - uri in str form
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *                 uri_proto, otherwise the uri proto will be used if set or
@@ -351,14 +356,16 @@ inline static struct dest_info *uri2dst(struct dns_srv_handle* dns_h,
                                                                                struct sip_msg *msg, str *uri, 
                                                                                        int proto )
 {
-       return uri2dst2(dns_h, dst, msg?msg->force_send_socket:0, uri, proto);
+       return uri2dst2(dns_h, dst, msg?msg->force_send_socket:0,
+                                               msg?msg->fwd_send_flags:0, uri, proto);
 }
 #else
 inline static struct dest_info *uri2dst(struct dest_info* dst,
                                                                                struct sip_msg *msg, str *uri, 
                                                                                        int proto )
 {
-       return uri2dst2(dst, msg?msg->force_send_socket:0, uri, proto);
+       return uri2dst2(dst, msg?msg->force_send_socket:0,
+                                               msg?msg->fwd_send_flags:0, uri, proto);
 }
 #endif /* USE_DNS_FAILOVER */
 
index 2d05503..1ba35a6 100644 (file)
@@ -12,41 +12,41 @@ Juha Heinanen
 
    Copyright © 2002-2008 Juha Heinanen
    Revision History
-   Revision $Revision$ $Date: 2008-03-08 00:03:56 +0100
-                              (Sa, 08 Mär 2008) $
-     __________________________________________________________
+   Revision $Revision$ $Date$
+     __________________________________________________________________
 
    Table of Contents
 
    1. Admin Guide
 
-        1.1. Overview
-        1.2. Dependencies
-        1.3. Exported Parameters
+        1. Overview
+        2. Dependencies
+        3. Exported Parameters
 
-              1.3.1. db_url (string)
-              1.3.2. db_mode (integer)
-              1.3.3. domain_table (string)
-              1.3.4. domain_col (string)
+              3.1. db_url (string)
+              3.2. db_mode (integer)
+              3.3. domain_table (string)
+              3.4. domain_col (string)
+              3.5. register_myself (integer)
 
-        1.4. Exported Functions
+        4. Exported Functions
 
-              1.4.1. is_from_local()
-              1.4.2. is_uri_host_local()
-              1.4.3. is_domain_local(pseudo_variable)
+              4.1. is_from_local()
+              4.2. is_uri_host_local()
+              4.3. is_domain_local(pseudo_variable)
 
-        1.5. Exported MI Functions
+        5. Exported MI Functions
 
-              1.5.1. domain_reload
-              1.5.2. domain_dump
+              5.1. domain_reload
+              5.2. domain_dump
 
-        1.6. Known Limitations
+        6. Known Limitations
 
    2. Developer Guide
 
-        2.1. Available Functions
+        1. Available Functions
 
-              2.1.1. is_domain_local(domain)
+              1.1. is_domain_local(domain)
 
    List of Examples
 
@@ -54,51 +54,81 @@ Juha Heinanen
    1.2. db_mode example
    1.3. Setting domain_table parameter
    1.4. Setting domain_col parameter
-   1.5. is_from_local usage
-   1.6. is_uri_host_local usage
-   1.7. is_domain_local usage
+   1.5. register_myself example
+   1.6. is_from_local usage
+   1.7. is_uri_host_local usage
+   1.8. is_domain_local usage
 
 Chapter 1. Admin Guide
 
-1.1. Overview
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+   3. Exported Parameters
+
+        3.1. db_url (string)
+        3.2. db_mode (integer)
+        3.3. domain_table (string)
+        3.4. domain_col (string)
+        3.5. register_myself (integer)
+
+   4. Exported Functions
+
+        4.1. is_from_local()
+        4.2. is_uri_host_local()
+        4.3. is_domain_local(pseudo_variable)
+
+   5. Exported MI Functions
 
-   Domain module implements checks that based on domain table
-   determine if a host part of an URI is "local" or not. A "local"
-   domain is one that the proxy is responsible for.
+        5.1. domain_reload
+        5.2. domain_dump
 
-   Domain module operates in caching or non-caching mode depending
-   on value of module parameter db_mode. In caching mode domain
-   module reads the contents of domain table into cache memory
-   when the module is loaded. After that domain table is re-read
-   only when module is given domain_reload fifo command. Any
-   changes in domain table must thus be followed by
-   "domain_reload" command in order to reflect them in module
-   behavior. In non-caching mode domain module always queries
-   domain table in the database.
+   6. Known Limitations
 
-   Caching is implemented using a hash table. The size of the hash
-   table is given by HASH_SIZE constant defined in domain_mod.h.
-   Its "factory default" value is 128.
+1. Overview
 
-1.2. Dependencies
+   Domain module implements checks that based on domain table determine if
+   a host part of an URI is "local" or not. A "local" domain is one that
+   the proxy is responsible for.
 
-   The module depends on the following modules (in the other words
-   the listed modules must be loaded before this module):
+   Domain module operates in caching or non-caching mode depending on
+   value of module parameter db_mode. In caching mode domain module reads
+   the contents of domain table into cache memory when the module is
+   loaded. After that domain table is re-read only when module is given
+   domain_reload fifo command. Any changes in domain table must thus be
+   followed by "domain_reload" command in order to reflect them in module
+   behavior. In non-caching mode domain module always queries domain table
+   in the database.
+
+   Caching is implemented using a hash table. The size of the hash table
+   is given by HASH_SIZE constant defined in domain_mod.h. Its "factory
+   default" value is 128.
+
+2. Dependencies
+
+   The module depends on the following modules (in the other words the
+   listed modules must be loaded before this module):
      * database -- Any database module
 
-1.3. Exported Parameters
+3. Exported Parameters
 
-1.3.1. db_url (string)
+   3.1. db_url (string)
+   3.2. db_mode (integer)
+   3.3. domain_table (string)
+   3.4. domain_col (string)
+   3.5. register_myself (integer)
+
+3.1. db_url (string)
 
    This is URL of the database to be used.
 
-   Default value is
-   "mysql://openserro:openserro@localhost/openser"
+   Default value is "mysql://openserro:openserro@localhost/openser"
 
    Example 1.1. Setting db_url parameter
 modparam("domain", "db_url", "mysql://ser:pass@db_host/ser")
 
-1.3.2. db_mode (integer)
+3.2. db_mode (integer)
 
    Database mode: 0 means non-caching, 1 means caching.
 
@@ -107,18 +137,18 @@ modparam("domain", "db_url", "mysql://ser:pass@db_host/ser")
    Example 1.2. db_mode example
 modparam("domain", "db_mode", 1)   # Use caching
 
-1.3.3. domain_table (string)
+3.3. domain_table (string)
 
-   Name of table containing names of local domains that the proxy
-   is responsible for. Local users must have in their sip uri a
-   host part that is equal to one of these domains.
+   Name of table containing names of local domains that the proxy is
+   responsible for. Local users must have in their sip uri a host part
+   that is equal to one of these domains.
 
    Default value is "domain".
 
    Example 1.3. Setting domain_table parameter
 modparam("domain", "domain_table", "new_name")
 
-1.3.4. domain_col (string)
+3.4. domain_col (string)
 
    Name of column containing domains in domain table.
 
@@ -127,58 +157,70 @@ modparam("domain", "domain_table", "new_name")
    Example 1.4. Setting domain_col parameter
 modparam("domain", "domain_col", "domain_name")
 
-1.4. Exported Functions
+3.5. register_myself (integer)
+
+   Register the list of domains to match 'myself' check: 0 means no myself
+   registration, 1 means enable myself registration.
 
-1.4.1. is_from_local()
+   Default value is 0 (disable).
 
-   Checks based on domain table if host part of From header uri is
-   one of the local domains that the proxy is responsible for
+   Example 1.5. register_myself example
+modparam("domain", "register_myself", 1)
+
+4. Exported Functions
+
+   4.1. is_from_local()
+   4.2. is_uri_host_local()
+   4.3. is_domain_local(pseudo_variable)
+
+4.1. is_from_local()
+
+   Checks based on domain table if host part of From header uri is one of
+   the local domains that the proxy is responsible for
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 1.5. is_from_local usage
+   Example 1.6. is_from_local usage
 ...
 if (is_from_local()) {
         ...
 };
 ...
 
-1.4.2. is_uri_host_local()
+4.2. is_uri_host_local()
 
-   If called from route or failure route block, checks based on
-   domain table if host part of Request-URI is one of the local
-   domains that the proxy is responsible for. If called from
-   branch route, the test is made on host part of URI of first
-   branch, which thus must have been appended to the transaction
-   before is_uri_host_local() is called.
+   If called from route or failure route block, checks based on domain
+   table if host part of Request-URI is one of the local domains that the
+   proxy is responsible for. If called from branch route, the test is made
+   on host part of URI of first branch, which thus must have been appended
+   to the transaction before is_uri_host_local() is called.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE.
 
-   Example 1.6. is_uri_host_local usage
+   Example 1.7. is_uri_host_local usage
 ...
 if (is_uri_host_local()) {
         ...
 };
 ...
 
-1.4.3. is_domain_local(pseudo_variable)
+4.3. is_domain_local(pseudo_variable)
 
-   This function checks if the domain contained in the
-   pseudo_variable is local.
+   This function checks if the domain contained in the pseudo_variable is
+   local.
 
    This function is a generalized form of the is_from_local() and
-   is_uri_host_local() functions, being able to completely replace
-   them and also extends them by allowing the domain to be taken
-   from any of the above mentioned sources. The following
-   equivalences exist:
+   is_uri_host_local() functions, being able to completely replace them
+   and also extends them by allowing the domain to be taken from any of
+   the above mentioned sources. The following equivalences exist:
      * is_domain_local("$rd") is same as is_uri_host_local()
      * is_domain_local("$fd") is same as is_from_local()
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE.
 
-   Example 1.7. is_domain_local usage
+   Example 1.8. is_domain_local usage
 ...
 if (is_domain_local("$rd")) {
         ...
@@ -197,12 +239,15 @@ if (is_domain_local("$avp(s:some_avp)")) {
 };
 ...
 
-1.5. Exported MI Functions
+5. Exported MI Functions
+
+   5.1. domain_reload
+   5.2. domain_dump
 
-1.5.1. domain_reload
+5.1. domain_reload
 
-   Causes domain module to re-read the contents of domain table
-   into cache memory.
+   Causes domain module to re-read the contents of domain table into cache
+   memory.
 
    Name: domain_reload
 
@@ -212,10 +257,10 @@ if (is_domain_local("$avp(s:some_avp)")) {
                 :domain_reload:_reply_fifo_file_
                 _empty_line_
 
-1.5.2. domain_dump
+5.2. domain_dump
 
-   Causes domain module to dump hash indexes and domain names in
-   its cache memory.
+   Causes domain module to dump hash indexes and domain names in its cache
+   memory.
 
    Name: domain_dump
 
@@ -225,23 +270,31 @@ if (is_domain_local("$avp(s:some_avp)")) {
                 :domain_dump:_reply_fifo_file_
                 _empty_line_
 
-1.6. Known Limitations
+6. Known Limitations
 
-   There is an unlikely race condition on domain list update. If a
-   process uses a table, which is reloaded at the same time twice
-   through FIFO, the second reload will delete the original table
-   still in use by the process.
+   There is an unlikely race condition on domain list update. If a process
+   uses a table, which is reloaded at the same time twice through FIFO,
+   the second reload will delete the original table still in use by the
+   process.
 
 Chapter 2. Developer Guide
 
-   The module provides is_domain_local API function for use by
-   other Kamailio modules.
+   Table of Contents
+
+   1. Available Functions
+
+        1.1. is_domain_local(domain)
+
+   The module provides is_domain_local API function for use by other
+   Kamailio modules.
+
+1. Available Functions
 
-2.1. Available Functions
+   1.1. is_domain_local(domain)
 
-2.1.1.  is_domain_local(domain)
+1.1.  is_domain_local(domain)
 
    Checks if domain given in str* parameter is local.
 
-   The function returns 1 if domain is local and -1 if domain is
-   not local or if an error occurred.
+   The function returns 1 if domain is local and -1 if domain is not local
+   or if an error occurred.
index d563ce9..9961dea 100644 (file)
@@ -117,6 +117,24 @@ modparam("domain", "domain_col", "domain_name")
 </programlisting>
                </example>
        </section>
+       <section>
+               <title><varname>register_myself</varname> (integer)</title>
+               <para>
+                       Register the list of domains to match 'myself' check:
+                       0 means no myself registration, 1 means enable myself
+                       registration.
+               </para>
+               <para>
+               Default value is 0 (disable).
+               </para>
+               <example>
+               <title>register_myself example</title>
+               <programlisting format="linespecific">
+modparam("domain", "register_myself", 1)
+</programlisting>
+               </example>
+       </section>
+
        
        </section>
        <section>
index afefee5..fb70d06 100644 (file)
@@ -77,6 +77,7 @@ static str db_url = {DEFAULT_RODB_URL, DEFAULT_RODB_URL_LEN};
 int db_mode = 0;                       /* Database usage mode: 0 = no cache, 1 = cache */
 str domain_table = {DOMAIN_TABLE, DOMAIN_TABLE_LEN}; /* Name of domain table */
 str domain_col = {DOMAIN_COL, DOMAIN_COL_LEN};       /* Name of domain column */
+int domain_reg_myself = 0;
 
 /*
  * Other module variables
@@ -110,6 +111,7 @@ static param_export_t params[] = {
        {"db_mode",        INT_PARAM, &db_mode       },
        {"domain_table",   STR_PARAM, &domain_table.s},
        {"domain_col",     STR_PARAM, &domain_col.s  },
+       {"register_myself",INT_PARAM, &domain_reg_myself},
        {0, 0, 0}
 };
 
@@ -154,10 +156,13 @@ static int mod_init(void)
                LM_ERR("failed to register MI commands\n");
                return -1;
        }
-       if(register_check_self_func(domain_check_self)<0)
+       if(domain_reg_myself!=0)
        {
-               LM_ERR("failed to register check self function\n");
-               return -1;
+               if(register_check_self_func(domain_check_self)<0)
+               {
+                       LM_ERR("failed to register check self function\n");
+                       return -1;
+               }
        }
 
        db_url.len = strlen(db_url.s);
index f04419a..69db19d 100644 (file)
@@ -16,44 +16,44 @@ Anca-Maria Vamanu
 
    Copyright © 2006 voice-system.ro
    Revision History
-   Revision $Revision$ $Date: 2008-08-06 12:08:33 +0200
-                              (Mi, 06 Aug 2008) $
-     __________________________________________________________
+   Revision $Revision$ $Date$
+     __________________________________________________________________
 
    Table of Contents
 
    1. Admin Guide
 
-        1.1. Overview
-        1.2. Dependencies
+        1. Overview
+        2. Dependencies
 
-              1.2.1. Kamailio Modules
-              1.2.2. External Libraries or Applications
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
 
-        1.3. Exported Parameters
+        3. Exported Parameters
 
-              1.3.1. db_url (str)
-              1.3.2. rooms_table (str)
-              1.3.3. members_table (str)
-              1.3.4. hash_size (integer)
-              1.3.5. imc_cmd_start_char (str)
-              1.3.6. outbound_proxy (str)
+              3.1. db_url (str)
+              3.2. rooms_table (str)
+              3.3. members_table (str)
+              3.4. hash_size (integer)
+              3.5. imc_cmd_start_char (str)
+              3.6. outbound_proxy (str)
+              3.7. extra_hdrs (str)
 
-        1.4. Exported Functions
+        4. Exported Functions
 
-              1.4.1. imc_manager()
+              4.1. imc_manager()
 
-        1.5. Exported MI Functions
+        5. Exported MI Functions
 
-              1.5.1. imc_list_rooms
-              1.5.2. imc_list_members
+              5.1. imc_list_rooms
+              5.2. imc_list_members
 
-        1.6. Exported Statistics
+        6. Exported Statistics
 
-              1.6.1. active_rooms
+              6.1. active_rooms
 
-        1.7. IMC Commands
-        1.8. Installation
+        7. IMC Commands
+        8. Installation
 
    List of Examples
 
@@ -63,57 +63,102 @@ Anca-Maria Vamanu
    1.4. Set hash_size parameter
    1.5. Set imc_cmd_start_char parameter
    1.6. Set outbound_proxy parameter
-   1.7. Usage of imc_manager() function
-   1.8. List of commands
+   1.7. Set extra_hdrs parameter
+   1.8. Usage of imc_manager() function
+   1.9. List of commands
 
 Chapter 1. Admin Guide
 
-1.1. Overview
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. db_url (str)
+        3.2. rooms_table (str)
+        3.3. members_table (str)
+        3.4. hash_size (integer)
+        3.5. imc_cmd_start_char (str)
+        3.6. outbound_proxy (str)
+        3.7. extra_hdrs (str)
+
+   4. Exported Functions
+
+        4.1. imc_manager()
+
+   5. Exported MI Functions
+
+        5.1. imc_list_rooms
+        5.2. imc_list_members
+
+   6. Exported Statistics
+
+        6.1. active_rooms
+
+   7. IMC Commands
+   8. Installation
+
+1. Overview
 
-   This module offers support for instant message conference. It
-   follows the architecture of IRC channels, you can send commands
-   embedded in MESSAGE body, because there are no SIP UA clients
-   which have GUI for IM conferencing.
+   This module offers support for instant message conference. It follows
+   the architecture of IRC channels, you can send commands embedded in
+   MESSAGE body, because there are no SIP UA clients which have GUI for IM
+   conferencing.
 
-   You have to define an URI corresponding to im conferencing
-   manager, where user can send commands to create a new
-   conference room. Once the conference room is created, users can
-   send commands directly to conferece's URI.
+   You have to define an URI corresponding to im conferencing manager,
+   where user can send commands to create a new conference room. Once the
+   conference room is created, users can send commands directly to
+   conferece's URI.
 
-   To ease the integration in the configuration file, the
-   interpreter of the IMC commands are embeded in the module, from
-   configuration poin of view, there is only one function which
-   has to be executed for both messages and commands.
+   To ease the integration in the configuration file, the interpreter of
+   the IMC commands are embeded in the module, from configuration poin of
+   view, there is only one function which has to be executed for both
+   messages and commands.
 
-1.2. Dependencies
+2. Dependencies
 
-1.2.1. Kamailio Modules
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
 
    The following modules must be loaded before this module:
      * mysql.
      * tm.
 
-1.2.2. External Libraries or Applications
+2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed
-   before running Kamailio with this module loaded:
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
      * None.
 
-1.3. Exported Parameters
+3. Exported Parameters
+
+   3.1. db_url (str)
+   3.2. rooms_table (str)
+   3.3. members_table (str)
+   3.4. hash_size (integer)
+   3.5. imc_cmd_start_char (str)
+   3.6. outbound_proxy (str)
+   3.7. extra_hdrs (str)
 
-1.3.1. db_url (str)
+3.1. db_url (str)
 
    The database url.
 
-   The default value is
-   "mysql://openser:openserrw@localhost/openser".
+   The default value is "mysql://openser:openserrw@localhost/openser".
 
    Example 1.1. Set db_url parameter
 ...
 modparam("imc", "db_url", "dbdriver://username:password@dbhost/dbname")
 ...
 
-1.3.2. rooms_table (str)
+3.2. rooms_table (str)
 
    The name of the table storing IMC rooms.
 
@@ -124,7 +169,7 @@ modparam("imc", "db_url", "dbdriver://username:password@dbhost/dbname")
 modparam("imc", "rooms_table", "rooms")
 ...
 
-1.3.3. members_table (str)
+3.3. members_table (str)
 
    The name of the table storing IMC members.
 
@@ -132,13 +177,13 @@ modparam("imc", "rooms_table", "rooms")
 
    Example 1.3. Set members_table parameter
 ...
-modparam("imc", "rooms_table", "members")
+modparam("imc", "members_table", "members")
 ...
 
-1.3.4. hash_size (integer)
+3.4. hash_size (integer)
 
-   The power of 2 to get the size of the hash table used for
-   storing members and rooms.
+   The power of 2 to get the size of the hash table used for storing
+   members and rooms.
 
    The default value is 4 (resultimg in hash size 16).
 
@@ -147,7 +192,7 @@ modparam("imc", "rooms_table", "members")
 modparam("imc", "hash_size", 8)
 ...
 
-1.3.5. imc_cmd_start_char (str)
+3.5. imc_cmd_start_char (str)
 
    The character which indicates that the body of the message is a
    command.
@@ -159,13 +204,12 @@ modparam("imc", "hash_size", 8)
 modparam("imc", "imc_cmd_start_char", "#")
 ...
 
-1.3.6. outbound_proxy (str)
+3.6. outbound_proxy (str)
 
-   The SIP address used as next hop when sending the message. Very
-   useful when using Kamailio with a domain name not in DNS, or
-   when using a separate Kamailio instance for imc processing. If
-   not set, the message will be sent to the address in destination
-   URI.
+   The SIP address used as next hop when sending the message. Very useful
+   when using Kamailio with a domain name not in DNS, or when using a
+   separate Kamailio instance for imc processing. If not set, the message
+   will be sent to the address in destination URI.
 
    Default value is NULL.
 
@@ -174,28 +218,52 @@ modparam("imc", "imc_cmd_start_char", "#")
 modparam("imc", "outbound_proxy", "sip:kamailio.org;transport=tcp")
 ...
 
-1.4. Exported Functions
+3.7. extra_hdrs (str)
+
+   Extra headers (each ending with \r\n) to be added in messages sent out
+   from imc server.
+
+   Default value is NULL.
+
+   Example 1.7. Set extra_hdrs parameter
+...
+modparam("imc", "extra_hdrs", "P-Flags: 3\r\n")
+...
+
+4. Exported Functions
 
-1.4.1.  imc_manager()
+   4.1. imc_manager()
 
-   Handles Message method.It detects if the body of the message is
-   a conference command.If so it executes it, otherwise it sends
-   the message to all the members in the room.
+4.1. imc_manager()
 
-   This function can be used from REQUEST_ROUTE.
+   Handles Message method.It detects if the body of the message is a
+   conference command.If so it executes it, otherwise it sends the message
+   to all the members in the room.
 
-   Example 1.7. Usage of imc_manager() function
+   This function can be used from REQUEST_ROUTE. See command description
+   for error codes returned by this function.
+
+   Example 1.8. Usage of imc_manager() function
 ...
 # the rooms will be named chat-xyz to avoid overlapping
 # with usernames
 if(is_method("MESSAGE)
-        && (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@")
-    imc_manager();
+    && (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@"))
+{
+    if(imc_manager())
+        sl_send_reply("200", "ok");
+    else
+        sl_send_reply("500", "command error");
+    exit;
+}
 ...
 
-1.5. Exported MI Functions
+5. Exported MI Functions
+
+   5.1. imc_list_rooms
+   5.2. imc_list_members
 
-1.5.1.  imc_list_rooms
+5.1. imc_list_rooms
 
    Lists of the IM Conferencing rooms.
 
@@ -207,7 +275,7 @@ if(is_method("MESSAGE)
                 :imc_list_rooms:_reply_fifo_file_
                 _empty_line_
 
-1.5.2.  imc_list_members
+5.2. imc_list_members
 
    Listing of the members in IM Conferencing rooms.
 
@@ -221,22 +289,23 @@ if(is_method("MESSAGE)
                 _room_
                 _empty_line_
 
-1.6. Exported Statistics
+6. Exported Statistics
+
+   6.1. active_rooms
 
-1.6.1.  active_rooms
+6.1. active_rooms
 
    Number of active IM Conferencing rooms.
 
-1.7. IMC Commands
+7. IMC Commands
 
-   A command is identified by the starting character. A command
-   must be written in one line. By default, the starting character
-   is '#'. You can change it via "imc_cmd_start_char" parameter.
+   A command is identified by the starting character. A command must be
+   written in one line. By default, the starting character is '#'. You can
+   change it via "imc_cmd_start_char" parameter.
 
-   Next picture presents the list of commands and their
-   parameters.
+   Next picture presents the list of commands and their parameters.
 
-   Example 1.8. List of commands
+   Example 1.9. List of commands
 ...
 
 1.create
@@ -247,6 +316,7 @@ if(is_method("MESSAGE)
            and new members can be added only though invitations
   -the user is added as the first member and owner of the room
   -eg:  #create chat-000 private
+  -error case: return codes: -30 -- -39
 
 2.join
   -makes the user member of a room
@@ -256,6 +326,7 @@ if(is_method("MESSAGE)
   -if the room does not exist the command is treated as create
   -eg:join sip:chat-000@kamailio.org,
       or just, #join, sent to sip:chat-000@kamailio.org
+  -error case: return codes: -40 -- -49
 
 3.invite
   -invites a user to become a member of a room
@@ -267,6 +338,7 @@ if(is_method("MESSAGE)
     and the administrators
   -eg: #invite sip:john@kamailio.org sip:chat-000@kamailio.org
     or  #invite john@kamailio.org sent to sip:chat-000@kamailio.org
+  -error case: return codes: -50 -- -59
 
 4.accept
   -accepting an invitation
@@ -274,10 +346,12 @@ if(is_method("MESSAGE)
     present it will be considered to be the address in the To header
     of the message
   -eg: #accept sip:john@kamailio.org
+  -error case: return codes: -60 -- -69
 
 5.deny
   -rejects an invitation
   -the parameter is the same as for accept
+  -error case: return codes: -70 -- -79
 
 6.remove
   -deletes a member from a room
@@ -287,6 +361,7 @@ if(is_method("MESSAGE)
           to be the address in the To header of the message
   -only certain members have the right to remove other members
   -eg: #remove sip:john@kamailio.org, sent to sip:chat-000@kamailio.org
+  -error case: return codes: -80 -- -89
 
 7.exit
   -leaving a room
@@ -294,24 +369,26 @@ if(is_method("MESSAGE)
     present it will be considered to be the address in the To header
     of the message
   -if the user is the owner of the room, the room will be destroyed
+  -error case: return codes: -90 -- -99
 
 8.destroy
   -removing a room
   -the parameter is the same as for exit
   -only the owner of a room has the right to destroy it
+  -error case: return codes: -110 -- -119
 
 9.list
   -list members in a room
+  -error case: return codes: -100 -- -109
 
 ...
 
-1.8. Installation
+8. Installation
 
-   Before running Kamailio with IMC, you have to setup the
-   database tables where the module will store the data. For that,
-   if the tables were not created by the installation script or
-   you choose to install everything by yourself you can use the
-   imc-create.sql SQL script in the database directories in the
-   kamailio/scripts folder as template. You can also find the
-   complete database documentation on the project webpage,
+   Before running Kamailio with IMC, you have to setup the database tables
+   where the module will store the data. For that, if the tables were not
+   created by the installation script or you choose to install everything
+   by yourself you can use the imc-create.sql SQL script in the database
+   directories in the kamailio/scripts folder as template. You can also
+   find the complete database documentation on the project webpage,
    http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
index 6046581..4ed17e5 100644 (file)
@@ -123,7 +123,7 @@ modparam("imc", "rooms_table", "rooms")
                <title>Set <varname>members_table</varname> parameter</title>
                <programlisting format="linespecific">
 ...
-modparam("imc", "rooms_table", "members")
+modparam("imc", "members_table", "members")
 ...
 </programlisting>
                </example>
@@ -191,6 +191,27 @@ modparam("imc", "outbound_proxy", "sip:kamailio.org;transport=tcp")
                </example>
        </section>
 
+       <section>
+               <title><varname>extra_hdrs</varname> (str)</title>
+               <para>
+               Extra headers (each ending with \r\n) to be added in
+               messages sent out from imc server.
+               </para>
+               <para>
+               <emphasis>
+               Default value is NULL.
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>extra_hdrs</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("imc", "extra_hdrs", "P-Flags: 3\r\n")
+...
+</programlisting>
+               </example>
+       </section>
+
        </section>
        <section>
        <title>Exported Functions</title>
@@ -204,7 +225,8 @@ modparam("imc", "outbound_proxy", "sip:kamailio.org;transport=tcp")
                message to all the members in the room. 
                </para>
                <para>
-               This function can be used from REQUEST_ROUTE.
+               This function can be used from REQUEST_ROUTE. See command description
+               for error codes returned by this function.
                </para>
                <example>
                <title>Usage of <varname>imc_manager()</varname> function</title>
@@ -213,8 +235,14 @@ modparam("imc", "outbound_proxy", "sip:kamailio.org;transport=tcp")
 # the rooms will be named chat-xyz to avoid overlapping
 # with usernames
 if(is_method("MESSAGE)
-        &amp;&amp; (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@")
-    imc_manager();
+    &amp;&amp; (uri=~ "sip:chat-[0-9]+@" || (uri=~ "sip:chat-manager@"))
+{
+    if(imc_manager())
+        sl_send_reply("200", "ok");
+    else
+        sl_send_reply("500", "command error");
+    exit;
+}
 ...
 </programlisting>
                </example>
@@ -309,6 +337,7 @@ if(is_method("MESSAGE)
           and new members can be added only though invitations
   -the user is added as the first member and owner of the room
   -eg:  #create chat-000 private
+  -error case: return codes: -30 -- -39
 
 2.join
   -makes the user member of a room
@@ -318,6 +347,7 @@ if(is_method("MESSAGE)
   -if the room does not exist the command is treated as create
   -eg:join sip:chat-000@kamailio.org,
       or just, #join, sent to sip:chat-000@kamailio.org
+  -error case: return codes: -40 -- -49
 
 3.invite
   -invites a user to become a member of a room
@@ -329,6 +359,7 @@ if(is_method("MESSAGE)
     and the administrators
   -eg: #invite sip:john@kamailio.org sip:chat-000@kamailio.org
     or  #invite john@kamailio.org sent to sip:chat-000@kamailio.org
+  -error case: return codes: -50 -- -59
 
 4.accept
   -accepting an invitation
@@ -336,10 +367,12 @@ if(is_method("MESSAGE)
     present it will be considered to be the address in the To header
     of the message
   -eg: #accept sip:john@kamailio.org
+  -error case: return codes: -60 -- -69
 
 5.deny
   -rejects an invitation
   -the parameter is the same as for accept
+  -error case: return codes: -70 -- -79
 
 6.remove
   -deletes a member from a room
@@ -349,6 +382,7 @@ if(is_method("MESSAGE)
          to be the address in the To header of the message
   -only certain members have the right to remove other members
   -eg: #remove sip:john@kamailio.org, sent to sip:chat-000@kamailio.org
+  -error case: return codes: -80 -- -89
 
 7.exit
   -leaving a room
@@ -356,14 +390,17 @@ if(is_method("MESSAGE)
     present it will be considered to be the address in the To header
     of the message
   -if the user is the owner of the room, the room will be destroyed
+  -error case: return codes: -90 -- -99
 
 8.destroy
   -removing a room
   -the parameter is the same as for exit
   -only the owner of a room has the right to destroy it
+  -error case: return codes: -110 -- -119
 
 9.list
   -list members in a room
+  -error case: return codes: -100 -- -109
 
 ...
 </programlisting>
index 3b3b5b5..ccbea01 100644 (file)
 
 MODULE_VERSION
 
+/** header variables */
+str imc_hdr_ctype = { "Content-Type: text/plain\r\n",  26};
+char hdr_buf[1024];
+str all_hdrs;
+
 /** parameters */
 
 db1_con_t *imc_db = NULL;
@@ -78,6 +83,7 @@ imc_hentry_p _imc_htable = NULL;
 int imc_hash_size = 4;
 str imc_cmd_start_str = str_init(IMC_CMD_START_STR);
 char imc_cmd_start_char;
+str extra_hdrs = {NULL, 0};
 
 /** module functions */
 static int mod_init(void);
@@ -109,6 +115,7 @@ static param_export_t params[]={
        {"rooms_table",                 STR_PARAM, &rooms_table.s},
        {"members_table",               STR_PARAM, &members_table.s},
        {"outbound_proxy",              STR_PARAM, &outbound_proxy.s},
+       {"extra_hdrs",                  STR_PARAM, &extra_hdrs.s},
        {0,0,0}
 };
 
@@ -372,6 +379,21 @@ static int mod_init(void)
        rooms_table.len = strlen(rooms_table.s);
        members_table.len = strlen(members_table.s);
 
+       if (extra_hdrs.s) {
+           extra_hdrs.len = strlen(extra_hdrs.s);
+           if (extra_hdrs.len + imc_hdr_ctype.len > 1024) {
+               LM_ERR("extra_hdrs too long\n");
+               return -1;
+           }
+           all_hdrs.s = &(hdr_buf[0]);
+           memcpy(all_hdrs.s, imc_hdr_ctype.s, imc_hdr_ctype.len);
+           memcpy(all_hdrs.s + imc_hdr_ctype.len, extra_hdrs.s,
+                  extra_hdrs.len);
+           all_hdrs.len = extra_hdrs.len + imc_hdr_ctype.len;
+       } else {
+           all_hdrs = imc_hdr_ctype;
+       }
+
        /*  binding to mysql module */
        db_url.len = strlen(db_url.s);
        LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len, db_url.s);
@@ -455,6 +477,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
        str body;
        struct sip_uri from_uri, *pto_uri=NULL, *pfrom_uri=NULL;
        struct to_body *pfrom;
+       int ret = -1;
 
        body.s = get_body( msg );
        if (body.s==0) 
@@ -503,6 +526,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                if(imc_parse_cmd(body.s, body.len, &cmd)<0)
                {
                        LM_ERR("failed to parse imc cmd!\n");
+                       ret = -20;
                        goto error;
                }
 
@@ -512,6 +536,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_create(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'create'\n");
+                               ret = -30;
                                goto error;
                        }
                break;
@@ -519,6 +544,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_join(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'join'\n");
+                               ret = -40;
                                goto error;
                        }
                break;
@@ -526,6 +552,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_invite(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'invite'\n");
+                               ret = -50;
                                goto error;
                        }
                break;
@@ -533,6 +560,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_accept(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'accept'\n");
+                               ret = -60;
                                goto error;
                        }
                break;
@@ -540,6 +568,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_deny(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'deny'\n");
+                               ret = -70;
                                goto error;
                        }
                break;
@@ -547,6 +576,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_remove(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'remove'\n");
+                               ret = -80;
                                goto error;
                        }
                break;
@@ -554,6 +584,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_exit(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'exit'\n");
+                               ret = -90;
                                goto error;
                        }
                break;
@@ -561,6 +592,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_list(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'list'\n");
+                               ret = -100;
                                goto error;
                        }
                break;
@@ -568,6 +600,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        if(imc_handle_destroy(msg, &cmd, pfrom_uri, pto_uri)<0)
                        {
                                LM_ERR("failed to handle 'destroy'\n");
+                               ret = -110;
                                goto error;
                        }
                break;
@@ -576,6 +609,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0)
                        {
                                LM_ERR("failed to handle 'help'\n");
+                               ret = -120;
                                goto error;
                        }
                break;
@@ -584,6 +618,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
                        (msg->new_uri.s)?&msg->new_uri:&msg->first_line.u.request.uri)<0)
                        {
                                LM_ERR("failed to handle 'unknown'\n");
+                               ret = -130;
                                goto error;
                        }
                }
@@ -594,6 +629,7 @@ static int imc_manager(struct sip_msg* msg, char *str1, char *str2)
        if(imc_handle_message(msg, &body, pfrom_uri, pto_uri)<0)
        {
                LM_ERR("failed to handle 'message'\n");
+               ret = -200;
                goto error;
        }
 
@@ -601,8 +637,7 @@ done:
        return 1;
 
 error:
-
-       return -1;      
+       return ret;
 }
 
 /**
index 93c8a4c..fb3d7dc 100644 (file)
@@ -34,5 +34,7 @@ extern str imc_cmd_start_str;
 extern char imc_cmd_start_char;
 extern struct tm_binds tmb;
 extern str outbound_proxy;
+extern str all_hdrs;
+extern str extra_hdrs;
 
 #endif
index f947451..163f5f8 100644 (file)
@@ -42,7 +42,6 @@
 static char imc_body_buf[IMC_BUF_SIZE];
 
 static str imc_msg_type = { "MESSAGE", 7 };
-static str imc_hdr_ctype = { "Content-Type: text/plain\r\n",  26};
 
 int imc_send_message(str *src, str *dst, str *headers, str *body);
 int imc_room_broadcast(imc_room_p room, str *ctype, str *body);
@@ -144,7 +143,7 @@ int imc_parse_cmd(char *buf, int len, imc_cmd_p cmd)
        } while(1);
        
 done:
-       LM_ERR("command: [%.*s]\n", cmd->name.len, cmd->name.s);
+       LM_DBG("command: [%.*s]\n", cmd->name.len, cmd->name.s);
        for(i=0; i<IMC_CMD_MAX_PARAM; i++)
        {
                if(cmd->param[i].len<=0)
@@ -199,7 +198,7 @@ int imc_handle_create(struct sip_msg* msg, imc_cmd_t *cmd,
                /* send info message */
                body.s = "*** room was created";
                body.len = sizeof("*** room was created")-1;
-               imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body);
+               imc_send_message(&room->uri, &member->uri, &all_hdrs, &body);
                goto done;
        }
        
@@ -227,7 +226,7 @@ int imc_handle_create(struct sip_msg* msg, imc_cmd_t *cmd,
                                "*** <%.*s> has joined the room",
                                member->uri.len, member->uri.s);
                        if(body.len>0)
-                               imc_room_broadcast(room, &imc_hdr_ctype, &body);
+                               imc_room_broadcast(room, &all_hdrs, &body);
 
                        if(body.len>=IMC_BUF_SIZE)
                                LM_ERR("member name %.*s truncated\n", member->uri.len, member->uri.s);
@@ -285,7 +284,7 @@ int imc_handle_join(struct sip_msg* msg, imc_cmd_t *cmd,
                /* send info message */
                body.s = "*** room was created";
                body.len = sizeof("*** room was created")-1;
-               imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body);
+               imc_send_message(&room->uri, &member->uri, &all_hdrs, &body);
                goto done;
        }
 
@@ -330,7 +329,7 @@ build_inform:
        body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room",
                                        member->uri.len, member->uri.s);
        if(body.len>0)
-               imc_room_broadcast(room, &imc_hdr_ctype, &body);
+               imc_room_broadcast(room, &all_hdrs, &body);
 
        if(body.len>=IMC_BUF_SIZE)
                LM_ERR("member name %.*s truncated\n", member->uri.len, member->uri.s);
@@ -492,7 +491,7 @@ int imc_handle_invite(struct sip_msg* msg, imc_cmd_t *cmd,
        cback_param->inv_uri = member->uri;
        /*?!?! possible race with 'remove user' */
 
-       set_uac_req(&uac_r, &imc_msg_type, &imc_hdr_ctype, &body, 0, 0,
+       set_uac_req(&uac_r, &imc_msg_type, &all_hdrs, &body, 0, 0,
                                imc_inv_callback, (void*)(cback_param));
        result= tmb.t_request(&uac_r,
                                &member->uri,                                                   /* Request-URI */
@@ -556,7 +555,7 @@ int imc_handle_accept(struct sip_msg* msg, imc_cmd_t *cmd,
        body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room",
                                        member->uri.len, member->uri.s);
        if(body.len>0)
-               imc_room_broadcast(room, &imc_hdr_ctype, &body);
+               imc_room_broadcast(room, &all_hdrs, &body);
 
        if(body.len>=IMC_BUF_SIZE)
                LM_ERR("member name %.*s truncated\n", member->uri.len, member->uri.s);
@@ -690,7 +689,7 @@ int imc_handle_remove(struct sip_msg* msg, imc_cmd_t *cmd,
        LM_DBG("to: [%.*s]\nfrom: [%.*s]\nbody: [%.*s]\n",
                        member->uri.len, member->uri.s , room->uri.len, room->uri.s,
                        body.len, body.s);
-       imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body);
+       imc_send_message(&room->uri, &member->uri, &all_hdrs, &body);
 
        member->flags |= IMC_MEMBER_DELETED;
        imc_del_member(room, &inv_uri.user, &inv_uri.host);
@@ -699,7 +698,7 @@ int imc_handle_remove(struct sip_msg* msg, imc_cmd_t *cmd,
        body.len = snprintf(body.s, IMC_BUF_SIZE, "*** <%.*s> has joined the room",
                                        member->uri.len, member->uri.s);
        if(body.len>0)
-               imc_room_broadcast(room, &imc_hdr_ctype, &body);
+               imc_room_broadcast(room, &all_hdrs, &body);
 
        if(body.len>=IMC_BUF_SIZE)
                LM_ERR("member name %.*s truncated\n", member->uri.len, member->uri.s);
@@ -753,7 +752,7 @@ int imc_handle_deny(struct sip_msg* msg, imc_cmd_t *cmd,
                        "The user [%.*s] has denied the invitation",
                        src->user.len, src->user.s);
        if(body.len>0)
-               imc_send_message(&room->uri, &memeber->uri, &imc_hdr_ctype, &body);
+           imc_send_message(&room->uri, &memeber->uri, &all_hdrs, &body);
 #endif
        LM_ERR("user [%.*s] declined invitation in room [%.*s]!\n", 
                        src->user.len, src->user.s,     room_name.len, room_name.s);
@@ -831,7 +830,7 @@ int imc_handle_list(struct sip_msg* msg, imc_cmd_t *cmd,
        body.s   = imc_body_buf;
        body.len = p-body.s;
        LM_DBG("members = [%.*s]\n", body.len, body.s);
-       imc_send_message(&room->uri, &member->uri, &imc_hdr_ctype, &body);
+       imc_send_message(&room->uri, &member->uri, &all_hdrs, &body);
 
 
        return 0;
@@ -880,7 +879,7 @@ int imc_handle_exit(struct sip_msg* msg, imc_cmd_t *cmd,
                body.s = imc_body_buf;
                strcpy(body.s, "The room has been destroyed");
                body.len = strlen(body.s);
-               imc_room_broadcast(room, &imc_hdr_ctype, &body);
+               imc_room_broadcast(room, &all_hdrs, &body);
 
                imc_release_room(room);
                
@@ -896,7 +895,7 @@ int imc_handle_exit(struct sip_msg* msg, imc_cmd_t *cmd,
                                "The user [%.*s] has left the room",
                                src->user.len, src->user.s);
                if(body.len>0)
-                       imc_room_broadcast(room, &imc_hdr_ctype, &body);
+                       imc_room_broadcast(room, &all_hdrs, &body);
 
                if(body.len>=IMC_BUF_SIZE)
                        LM_ERR("user name %.*s truncated\n", src->user.len, src->user.s);
@@ -957,7 +956,7 @@ int imc_handle_destroy(struct sip_msg* msg, imc_cmd_t *cmd,
        body.len = strlen(body.s);
 
        /* braodcast message */
-       imc_room_broadcast(room, &imc_hdr_ctype, &body);
+       imc_room_broadcast(room, &all_hdrs, &body);
 
        imc_release_room(room);
 
@@ -984,7 +983,7 @@ int imc_handle_help(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst)
        body.len = IMC_HELP_MSG_LEN;
 
        LM_DBG("to: [%.*s] from: [%.*s]\n", src->len, src->s, dst->len, dst->s);
-       set_uac_req(&uac_r, &imc_msg_type, &imc_hdr_ctype, &body, 0, 0, 0, 0);
+       set_uac_req(&uac_r, &imc_msg_type, &all_hdrs, &body, 0, 0, 0, 0);
        tmb.t_request(&uac_r,
                                NULL,                                                                   /* Request-URI */
                                src,                                                                    /* To */
@@ -1014,7 +1013,7 @@ int imc_handle_unknown(struct sip_msg* msg, imc_cmd_t *cmd, str *src, str *dst)
        }
 
        LM_DBG("to: [%.*s] from: [%.*s]\n", src->len, src->s, dst->len, dst->s);
-       set_uac_req(&uac_r, &imc_msg_type, &imc_hdr_ctype, &body, 0, 0, 0, 0);
+       set_uac_req(&uac_r, &imc_msg_type, &all_hdrs, &body, 0, 0, 0, 0);
        tmb.t_request(&uac_r,
                                NULL,                                                                   /* Request-URI */
                                src,                                                                    /* To */
@@ -1065,7 +1064,7 @@ int imc_handle_message(struct sip_msg* msg, str *msgbody,
        body.s[body.len] = '\0';
 
        member->flags |= IMC_MEMBER_SKIP;
-       imc_room_broadcast(room, &imc_hdr_ctype, &body);
+       imc_room_broadcast(room, &all_hdrs, &body);
        member->flags &= ~IMC_MEMBER_SKIP;
 
        imc_release_room(room);
@@ -1209,7 +1208,7 @@ send_message:
 
        LM_DBG("to: %.*s\nfrom: %.*s\nbody: %.*s\n", to_uri_s.len, to_uri_s.s,
                        from_uri_s.len, from_uri_s.s, body_final.len, body_final.s);
-       set_uac_req(&uac_r, &imc_msg_type, 0, &body_final, 0, 0, 0, 0);
+       set_uac_req(&uac_r, &imc_msg_type, &extra_hdrs, &body_final, 0, 0, 0, 0);
        tmb.t_request(&uac_r,
                                        NULL,                                                                   /* Request-URI */
                                        &to_uri_s,                                                              /* To */
index f112632..5e4735c 100644 (file)
@@ -64,7 +64,7 @@
 #define IMC_ROOM_PRIVATE_LEN   (sizeof(IMC_ROOM_PRIVATE)-1)
 
 #define IMC_HELP_MSG   "\r\n"IMC_CMD_START_STR IMC_CMD_CREATE" <room_name> - \
-create new connference room\r\n\
+create new conference room\r\n\
 "IMC_CMD_START_STR IMC_CMD_JOIN" [<room_name>] - \
 join the conference room\r\n\
 "IMC_CMD_START_STR IMC_CMD_INVITE" <user_name> [<room_name>] - \
index 148543f..470fbe4 100644 (file)
@@ -197,6 +197,8 @@ static inline int no_contacts(udomain_t* _d, str* _a)
        if (res == 0) {  /* Contacts found */
                build_contact(r->contacts);
                ul.release_urecord(r);
+       } else {  /* No contacts found */
+               build_contact(NULL);
        }
        ul.unlock_udomain(_d, _a);
        return 0;
index f93e059..78e3f8a 100644 (file)
@@ -245,10 +245,11 @@ int sl_send_reply_helper(struct sip_msg *msg ,int code, str *text, str *tag)
        /* use for sending the received interface -bogdan*/
        dst.proto=msg->rcv.proto;
        dst.send_sock=msg->rcv.bind_address;
-       dst.id=msg->rcv.proto_reserved1;        
+       dst.id=msg->rcv.proto_reserved1;
 #ifdef USE_COMP
        dst.comp=msg->via1->comp_no;
 #endif
+       dst.send_flags=msg->rpl_send_flags;
        ret = msg_send(&dst, buf.s, buf.len);
        mhomed=backup_mhomed;
        pkg_free(buf.s);
index f60c92b..562d565 100644 (file)
@@ -19,65 +19,65 @@ Daniel-Constantin Mierla
 
    Copyright © 2003 FhG FOKUS
    Revision History
-   Revision $Revision$ $Date: 2008-08-11 08:46:44 +0200
-                              (Mo, 11 Aug 2008) $
-     __________________________________________________________
+   Revision $Revision$ $Date$
+     __________________________________________________________________
 
    Table of Contents
 
    1. Admin Guide
 
-        1.1. Overview
-
-              1.1.1. Known Limitations
-
-        1.2. Dependencies
-
-              1.2.1. Kamailio Modules
-              1.2.2. External Libraries or Applications
-
-        1.3. Exported Functions
-
-              1.3.1. search(re)
-              1.3.2. search_body(re)
-              1.3.3. search_append(re, txt)
-              1.3.4. search_append_body(re, txt)
-              1.3.5. replace(re, txt)
-              1.3.6. replace_body(re, txt)
-              1.3.7. replace_all(re, txt)
-              1.3.8. replace_body_all(re, txt)
-              1.3.9. replace_body_atonce(re, txt)
-              1.3.10. subst('/re/repl/flags')
-              1.3.11. subst_uri('/re/repl/flags')
-              1.3.12. subst_user('/re/repl/flags')
-              1.3.13. subst_body('/re/repl/flags')
-              1.3.14. set_body(txt,content_type)
-              1.3.15. set_reply_body(txt,content_type)
-              1.3.16. filter_body(content_type)
-              1.3.17. append_to_reply(txt)
-              1.3.18. append_hf(txt)
-              1.3.19. append_hf(txt, hdr)
-              1.3.20. insert_hf(txt)
-              1.3.21. insert_hf(txt, hdr)
-              1.3.22. append_urihf(prefix, suffix)
-              1.3.23. is_present_hf(hf_name)
-              1.3.24. is_present_hf_re(hf_name_re)
-              1.3.25. append_time()
-              1.3.26. is_method(name)
-              1.3.27. remove_hf(hname)
-              1.3.28. remove_hf_re(re)
-              1.3.29. has_body(), has_body(mime)
-              1.3.30. is_privacy(privacy_type)
-              1.3.31. cmp_str(str1, str2)
-              1.3.32. cmp_istr(str1, str2)
-
-        1.4. Known Limitations
+        1. Overview
+
+              1.1. Known Limitations
+
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Functions
+
+              3.1. search(re)
+              3.2. search_body(re)
+              3.3. search_append(re, txt)
+              3.4. search_append_body(re, txt)
+              3.5. replace(re, txt)
+              3.6. replace_body(re, txt)
+              3.7. replace_all(re, txt)
+              3.8. replace_body_all(re, txt)
+              3.9. replace_body_atonce(re, txt)
+              3.10. subst('/re/repl/flags')
+              3.11. subst_uri('/re/repl/flags')
+              3.12. subst_user('/re/repl/flags')
+              3.13. subst_body('/re/repl/flags')
+              3.14. set_body(txt,content_type)
+              3.15. set_reply_body(txt,content_type)
+              3.16. filter_body(content_type)
+              3.17. append_to_reply(txt)
+              3.18. append_hf(txt)
+              3.19. append_hf(txt, hdr)
+              3.20. insert_hf(txt)
+              3.21. insert_hf(txt, hdr)
+              3.22. append_urihf(prefix, suffix)
+              3.23. is_present_hf(hf_name)
+              3.24. is_present_hf_re(hf_name_re)
+              3.25. append_time()
+              3.26. is_method(name)
+              3.27. remove_hf(hname)
+              3.28. remove_hf_re(re)
+              3.29. has_body(), has_body(mime)
+              3.30. is_privacy(privacy_type)
+              3.31. cmp_str(str1, str2)
+              3.32. cmp_istr(str1, str2)
+              3.33. msg_apply_changes()
+
+        4. Known Limitations
 
    2. Developer Guide
 
-        2.1. Functions
+        1. Functions
 
-              2.1.1. load_textops(*import_structure)
+              1.1. load_textops(*import_structure)
 
    List of Examples
 
@@ -113,42 +113,130 @@ Daniel-Constantin Mierla
    1.30. is_privacy usage
    1.31. cmp_str usage
    1.32. cmp_str usage
+   1.33. msg_apply_changes() usage
 
 Chapter 1. Admin Guide
 
-1.1. Overview
-
-   The module implements text based operations over the SIP
-   message processed by Kamailio. SIP is a text based protocol and
-   the module provides a large set of very useful functions to
-   manipulate the message at text level, e.g., regular expression
-   search and replace, Perl-like substitutions, checks for method
-   type, header presence, insert of new header and date, etc.
-
-1.1.1. Known Limitations
+   Table of Contents
 
-   search ignores folded lines. For example,
-   search("(From|f):.*@foo.bar") doesn't match the following From
-   header field:
+   1. Overview
+
+        1.1. Known Limitations
+
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Functions
+
+        3.1. search(re)
+        3.2. search_body(re)
+        3.3. search_append(re, txt)
+        3.4. search_append_body(re, txt)
+        3.5. replace(re, txt)
+        3.6. replace_body(re, txt)
+        3.7. replace_all(re, txt)
+        3.8. replace_body_all(re, txt)
+        3.9. replace_body_atonce(re, txt)
+        3.10. subst('/re/repl/flags')
+        3.11. subst_uri('/re/repl/flags')
+        3.12. subst_user('/re/repl/flags')
+        3.13. subst_body('/re/repl/flags')
+        3.14. set_body(txt,content_type)
+        3.15. set_reply_body(txt,content_type)
+        3.16. filter_body(content_type)
+        3.17. append_to_reply(txt)
+        3.18. append_hf(txt)
+        3.19. append_hf(txt, hdr)
+        3.20. insert_hf(txt)
+        3.21. insert_hf(txt, hdr)
+        3.22. append_urihf(prefix, suffix)
+        3.23. is_present_hf(hf_name)
+        3.24. is_present_hf_re(hf_name_re)
+        3.25. append_time()
+        3.26. is_method(name)
+        3.27. remove_hf(hname)
+        3.28. remove_hf_re(re)
+        3.29. has_body(), has_body(mime)
+        3.30. is_privacy(privacy_type)
+        3.31. cmp_str(str1, str2)
+        3.32. cmp_istr(str1, str2)
+        3.33. msg_apply_changes()
+
+   4. Known Limitations
+
+1. Overview
+
+   1.1. Known Limitations
+
+   The module implements text based operations over the SIP message
+   processed by Kamailio. SIP is a text based protocol and the module
+   provides a large set of very useful functions to manipulate the message
+   at text level, e.g., regular expression search and replace, Perl-like
+   substitutions, checks for method type, header presence, insert of new
+   header and date, etc.
+
+1.1. Known Limitations
+
+   search ignores folded lines. For example, search("(From|f):.*@foo.bar")
+   doesn't match the following From header field:
 From: medabeda
  <sip:medameda@foo.bar>;tag=1234
 
-1.2. Dependencies
+2. Dependencies
 
-1.2.1. Kamailio Modules
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
 
    The following modules must be loaded before this module:
      * No dependencies on other Kamailio modules.
 
-1.2.2. External Libraries or Applications
+2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed
-   before running Kamailio with this module loaded:
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
      * None.
 
-1.3. Exported Functions
-
-1.3.1.  search(re)
+3. Exported Functions
+
+   3.1. search(re)
+   3.2. search_body(re)
+   3.3. search_append(re, txt)
+   3.4. search_append_body(re, txt)
+   3.5. replace(re, txt)
+   3.6. replace_body(re, txt)
+   3.7. replace_all(re, txt)
+   3.8. replace_body_all(re, txt)
+   3.9. replace_body_atonce(re, txt)
+   3.10. subst('/re/repl/flags')
+   3.11. subst_uri('/re/repl/flags')
+   3.12. subst_user('/re/repl/flags')
+   3.13. subst_body('/re/repl/flags')
+   3.14. set_body(txt,content_type)
+   3.15. set_reply_body(txt,content_type)
+   3.16. filter_body(content_type)
+   3.17. append_to_reply(txt)
+   3.18. append_hf(txt)
+   3.19. append_hf(txt, hdr)
+   3.20. insert_hf(txt)
+   3.21. insert_hf(txt, hdr)
+   3.22. append_urihf(prefix, suffix)
+   3.23. is_present_hf(hf_name)
+   3.24. is_present_hf_re(hf_name_re)
+   3.25. append_time()
+   3.26. is_method(name)
+   3.27. remove_hf(hname)
+   3.28. remove_hf_re(re)
+   3.29. has_body(), has_body(mime)
+   3.30. is_privacy(privacy_type)
+   3.31. cmp_str(str1, str2)
+   3.32. cmp_istr(str1, str2)
+   3.33. msg_apply_changes()
+
+3.1. search(re)
 
    Searches for the re in the message.
 
@@ -163,7 +251,7 @@ From: medabeda
 if ( search("[Ss][Ii][Pp]") ) { /*....*/ };
 ...
 
-1.3.2.  search_body(re)
+3.2. search_body(re)
 
    Searches for the re in the body of the message.
 
@@ -178,7 +266,7 @@ if ( search("[Ss][Ii][Pp]") ) { /*....*/ };
 if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ };
 ...
 
-1.3.3.  search_append(re, txt)
+3.3. search_append(re, txt)
 
    Searches for the first match of re and appends txt after it.
 
@@ -194,10 +282,10 @@ if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ };
 search_append("[Oo]pen[Ss]er", " SIP Proxy");
 ...
 
-1.3.4.  search_append_body(re, txt)
+3.4. search_append_body(re, txt)
 
-   Searches for the first match of re in the body of the message
-   and appends txt after it.
+   Searches for the first match of re in the body of the message and
+   appends txt after it.
 
    Meaning of the parameters is as follows:
      * re - Regular expression.
@@ -211,7 +299,7 @@ search_append("[Oo]pen[Ss]er", " SIP Proxy");
 search_append_body("[Oo]pen[Ss]er", " SIP Proxy");
 ...
 
-1.3.5.  replace(re, txt)
+3.5. replace(re, txt)
 
    Replaces the first occurrence of re with txt.
 
@@ -227,10 +315,10 @@ search_append_body("[Oo]pen[Ss]er", " SIP Proxy");
 replace("openser", "Kamailio SIP Proxy");
 ...
 
-1.3.6.  replace_body(re, txt)
+3.6. replace_body(re, txt)
 
-   Replaces the first occurrence of re in the body of the message
-   with txt.
+   Replaces the first occurrence of re in the body of the message with
+   txt.
 
    Meaning of the parameters is as follows:
      * re - Regular expression.
@@ -244,7 +332,7 @@ replace("openser", "Kamailio SIP Proxy");
 replace_body("openser", "Kamailio SIP Proxy");
 ...
 
-1.3.7.  replace_all(re, txt)
+3.7. replace_all(re, txt)
 
    Replaces all occurrence of re with txt.
 
@@ -260,10 +348,10 @@ replace_body("openser", "Kamailio SIP Proxy");
 replace_all("openser", "Kamailio SIP Proxy");
 ...
 
-1.3.8.  replace_body_all(re, txt)
+3.8. replace_body_all(re, txt)
 
-   Replaces all occurrence of re in the body of the message with
-   txt. Matching is done on a per-line basis.
+   Replaces all occurrence of re in the body of the message with txt.
+   Matching is done on a per-line basis.
 
    Meaning of the parameters is as follows:
      * re - Regular expression.
@@ -277,10 +365,10 @@ replace_all("openser", "Kamailio SIP Proxy");
 replace_body_all("openser", "Kamailio SIP Proxy");
 ...
 
-1.3.9.  replace_body_atonce(re, txt)
+3.9. replace_body_atonce(re, txt)
 
-   Replaces all occurrence of re in the body of the message with
-   txt. Matching is done over the whole body.
+   Replaces all occurrence of re in the body of the message with txt.
+   Matching is done over the whole body.
 
    Meaning of the parameters is as follows:
      * re - Regular expression.
@@ -296,17 +384,16 @@ if(has_body() && replace_body_atonce("^.+$", ""))
         remove_hf("Content-Type");
 ...
 
-1.3.10.  subst('/re/repl/flags')
+3.10. subst('/re/repl/flags')
 
    Replaces re with repl (sed or perl like).
 
    Meaning of the parameters is as follows:
-     * '/re/repl/flags' - sed like regular expression. flags can
-       be a combination of i (case insensitive), g (global) or s
-       (match newline don't treat it as end of line).
+     * '/re/repl/flags' - sed like regular expression. flags can be a
+       combination of i (case insensitive), g (global) or s (match newline
+       don't treat it as end of line).
        're' - is regular expresion
-       'repl' - is replacement string - may contain
-       pseudo-varibales
+       'repl' - is replacement string - may contain pseudo-varibales
        'flags' - substitution flags (i - ignore case, g - global)
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
@@ -317,25 +404,23 @@ if(has_body() && replace_body_atonce("^.+$", ""))
 # replace the uri in to: with the message uri (just an example)
 if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {};
 
-# replace the uri in to: with the value of avp sip_address (just an exam
-ple)
-if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\
-2/ig') ) {};
+# replace the uri in to: with the value of avp sip_address (just an example)
+if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\2/ig') )
+ {};
 
 ...
 
-1.3.11.  subst_uri('/re/repl/flags')
+3.11. subst_uri('/re/repl/flags')
 
-   Runs the re substitution on the message uri (like subst but
-   works only on the uri)
+   Runs the re substitution on the message uri (like subst but works only
+   on the uri)
 
    Meaning of the parameters is as follows:
-     * '/re/repl/flags' - sed like regular expression. flags can
-       be a combination of i (case insensitive), g (global) or s
-       (match newline don't treat it as end of line).
+     * '/re/repl/flags' - sed like regular expression. flags can be a
+       combination of i (case insensitive), g (global) or s (match newline
+       don't treat it as end of line).
        're' - is regular expresion
-       'repl' - is replacement string - may contain
-       pseudo-varibales
+       'repl' - is replacement string - may contain pseudo-varibales
        'flags' - substitution flags (i - ignore case, g - global)
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
@@ -347,26 +432,23 @@ if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\
 # as a parameter: orig_uri (just an example)
 if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:3463\1@\2;orig_uri=\0/i')){$
 
-# adds the avp 'uri_prefix' as prefix to numeric uris, and save the orig
-inal
+# adds the avp 'uri_prefix' as prefix to numeric uris, and save the original
 # uri (\0 match) as a parameter: orig_uri (just an example)
-if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\
-0/i')){$
+if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\0/i')){$
 
 ...
 
-1.3.12.  subst_user('/re/repl/flags')
+3.12. subst_user('/re/repl/flags')
 
-   Runs the re substitution on the message uri (like subst_uri but
-   works only on the user portion of the uri)
+   Runs the re substitution on the message uri (like subst_uri but works
+   only on the user portion of the uri)
 
    Meaning of the parameters is as follows:
-     * '/re/repl/flags' - sed like regular expression. flags can
-       be a combination of i (case insensitive), g (global) or s
-       (match newline don't treat it as end of line).
+     * '/re/repl/flags' - sed like regular expression. flags can be a
+       combination of i (case insensitive), g (global) or s (match newline
+       don't treat it as end of line).
        're' - is regular expresion
-       'repl' - is replacement string - may contain
-       pseudo-varibales
+       'repl' - is replacement string - may contain pseudo-varibales
        'flags' - substitution flags (i - ignore case, g - global)
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
@@ -383,18 +465,16 @@ if (subst_user('/(.*)3642$/$avp(user_prefix)\13642/')){$
 
 ...
 
-1.3.13.  subst_body('/re/repl/flags')
+3.13. subst_body('/re/repl/flags')
 
-   Replaces re with repl (sed or perl like) in the body of the
-   message.
+   Replaces re with repl (sed or perl like) in the body of the message.
 
    Meaning of the parameters is as follows:
-     * '/re/repl/flags' - sed like regular expression. flags can
-       be a combination of i (case insensitive), g (global) or s
-       (match newline don't treat it as end of line).
+     * '/re/repl/flags' - sed like regular expression. flags can be a
+       combination of i (case insensitive), g (global) or s (match newline
+       don't treat it as end of line).
        're' - is regular expresion
-       'repl' - is replacement string - may contain
-       pseudo-varibales
+       'repl' - is replacement string - may contain pseudo-varibales
        'flags' - substitution flags (i - ignore case, g - global)
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
@@ -406,7 +486,7 @@ if ( subst_body('/^o=(.*) /o=$fU /') ) {};
 
 ...
 
-1.3.14.  set_body(txt,content_type)
+3.14. set_body(txt,content_type)
 
    Set body to a SIP message.
 
@@ -423,7 +503,7 @@ if ( subst_body('/^o=(.*) /o=$fU /') ) {};
 set_body("test", "text/plain");
 ...
 
-1.3.15.  set_reply_body(txt,content_type)
+3.15. set_reply_body(txt,content_type)
 
    Set body to a SIP reply to be generated by Kamailio.
 
@@ -440,10 +520,10 @@ set_body("test", "text/plain");
 set_reply_body("test", "text/plain");
 ...
 
-1.3.16.  filter_body(content_type)
+3.16. filter_body(content_type)
 
-   Filters multipart body by leaving out all other body parts
-   except the first body part of given type.
+   Filters multipart/mixed body by leaving out all other body parts except
+   the first body part of given type.
 
    Meaning of the parameters is as follows:
      * content_type - Content type to be left in the body.
@@ -463,7 +543,7 @@ if (has_body("multipart/mixed")) {
 }
 ...
 
-1.3.17.  append_to_reply(txt)
+3.17. append_to_reply(txt)
 
    Append txt as header to the reply.
 
@@ -479,7 +559,7 @@ append_to_reply("Foo: bar\r\n");
 append_to_reply("Foo: $rm at $Ts\r\n");
 ...
 
-1.3.18.  append_hf(txt)
+3.18. append_hf(txt)
 
    Appends 'txt' as header after the last header field.
 
@@ -487,11 +567,11 @@ append_to_reply("Foo: $rm at $Ts\r\n");
      * txt - Header field to be appended. The value can contain
        pseudo-variables which will be replaced at run time.
 
-   Note: Headers which are added in main route cannot be removed
-   in further routes (e.g. failure routes). So, the idea is not to
-   add there any headers that you might want to remove later. To
-   add headers temporarely use the branch route because the
-   changes you do there are per-branch.
+   Note: Headers which are added in main route cannot be removed in
+   further routes (e.g. failure routes). So, the idea is not to add there
+   any headers that you might want to remove later. To add headers
+   temporarely use the branch route because the changes you do there are
+   per-branch.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE, BRANCH_ROUTE.
@@ -502,7 +582,7 @@ append_hf("P-hint: VOICEMAIL\r\n");
 append_hf("From-username: $fU\r\n");
 ...
 
-1.3.19.  append_hf(txt, hdr)
+3.19. append_hf(txt, hdr)
 
    Appends 'txt' as header after first 'hdr' header field.
 
@@ -520,7 +600,7 @@ append_hf("P-hint: VOICEMAIL\r\n", "Call-ID");
 append_hf("From-username: $fU\r\n", "Call-ID");
 ...
 
-1.3.20.  insert_hf(txt)
+3.20. insert_hf(txt)
 
    Inserts 'txt' as header before the first header field.
 
@@ -537,7 +617,7 @@ insert_hf("P-hint: VOICEMAIL\r\n");
 insert_hf("To-username: $tU\r\n");
 ...
 
-1.3.21.  insert_hf(txt, hdr)
+3.21. insert_hf(txt, hdr)
 
    Inserts 'txt' as header before first 'hdr' header field.
 
@@ -555,7 +635,7 @@ insert_hf("P-hint: VOICEMAIL\r\n", "Call-ID");
 insert_hf("To-username: $tU\r\n", "Call-ID");
 ...
 
-1.3.22.  append_urihf(prefix, suffix)
+3.22. append_urihf(prefix, suffix)
 
    Append header field name with original Request-URI in middle.
 
@@ -571,14 +651,14 @@ insert_hf("To-username: $tU\r\n", "Call-ID");
 append_urihf("CC-Diversion: ", "\r\n");
 ...
 
-1.3.23.  is_present_hf(hf_name)
+3.23. is_present_hf(hf_name)
 
    Return true if a header field is present in message.
 
 Note
 
-   The function is also able to distinguish the compact names. For
-   exmaple "From" will match with "f"
+   The function is also able to distinguish the compact names. For exmaple
+   "From" will match with "f"
 
    Meaning of the parameters is as follows:
      * hf_name - Header field name.(long or compact form)
@@ -591,10 +671,10 @@ Note
 if (is_present_hf("From")) log(1, "From HF Present");
 ...
 
-1.3.24.  is_present_hf_re(hf_name_re)
+3.24. is_present_hf_re(hf_name_re)
 
-   Return true if a header field whose name matches regular
-   expression 'hf_name_re' is present in message.
+   Return true if a header field whose name matches regular expression
+   'hf_name_re' is present in message.
 
    Meaning of the parameters is as follows:
      * hf_name_re - Regular expression to match header field name.
@@ -604,16 +684,15 @@ if (is_present_hf("From")) log(1, "From HF Present");
 
    Example 1.24. is_present_hf_re usage
 ...
-if (is_present_hf_re("^P-")) log(1, "There are headers starting with P-\
-n");
+if (is_present_hf_re("^P-")) log(1, "There are headers starting with P-\n");
 ...
 
-1.3.25.  append_time()
+3.25. append_time()
 
-   Adds a time header to the reply of the request. You must use it
-   before functions that are likely to send a reply, e.g., save()
-   from 'registrar' module. Header format is: "Date: %a, %d %b %Y
-   %H:%M:%S GMT", with the legend:
+   Adds a time header to the reply of the request. You must use it before
+   functions that are likely to send a reply, e.g., save() from
+   'registrar' module. Header format is: "Date: %a, %d %b %Y %H:%M:%S
+   GMT", with the legend:
      * %a abbreviated week of day name (locale)
      * %d day of month as decimal number
      * %b abbreviated month name (locale)
@@ -632,24 +711,24 @@ n");
 append_time();
 ...
 
-1.3.26.  is_method(name)
+3.26. is_method(name)
 
-   Check if the method of the message matches the name. If name is
-   a known method (invite, cancel, ack, bye, options, info,
-   update, register, message, subscribe, notify, refer, prack),
-   the function performs method ID testing (integer comparison)
-   instead of ignore case string comparison.
+   Check if the method of the message matches the name. If name is a known
+   method (invite, cancel, ack, bye, options, info, update, register,
+   message, subscribe, notify, refer, prack), the function performs method
+   ID testing (integer comparison) instead of ignore case string
+   comparison.
 
    The 'name' can be a list of methods in the form of
-   'method1|method2|...'. In this case, the function returns true
-   if the SIP message's method is one from the list. IMPORTANT
-   NOTE: in the list must be only methods defined in Kamailio with
-   ID (invite, cancel, ack, bye, options, info, update, register,
-   message, subscribe, notify, refer, prack, publish; for more
-   see: http://www.iana.org/assignments/sip-parameters).
+   'method1|method2|...'. In this case, the function returns true if the
+   SIP message's method is one from the list. IMPORTANT NOTE: in the list
+   must be only methods defined in Kamailio with ID (invite, cancel, ack,
+   bye, options, info, update, register, message, subscribe, notify,
+   refer, prack, publish; for more see:
+   http://www.iana.org/assignments/sip-parameters).
 
-   If used for replies, the function tests the value of method
-   field from CSeq header.
+   If used for replies, the function tests the value of method field from
+   CSeq header.
 
    Meaning of the parameters is as follows:
      * name - SIP method name
@@ -669,7 +748,7 @@ if(is_method("OPTION|UPDATE"))
 }
 ...
 
-1.3.27.  remove_hf(hname)
+3.27. remove_hf(hname)
 
    Remove from message all headers with name "hname"
 
@@ -689,16 +768,15 @@ if(remove_hf("User-Agent"))
 }
 ...
 
-1.3.28.  remove_hf_re(re)
+3.28. remove_hf_re(re)
 
-   Remove from message all headers with name matching regular
-   expression "re"
+   Remove from message all headers with name matching regular expression
+   "re"
 
    Returns true if at least one header is found and removed.
 
    Meaning of the parameters is as follows:
-     * re - regular expression to match the header name to be
-       removed.
+     * re - regular expression to match the header name to be removed.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
@@ -711,18 +789,17 @@ if(remove_hf_re("^P-"))
 }
 ...
 
-1.3.29.  has_body(), has_body(mime)
+3.29. has_body(), has_body(mime)
 
-   The function returns true if the SIP message has a body
-   attached. The checked includes also the "Content-Lenght" header
-   presence and value.
+   The function returns true if the SIP message has a body attached. The
+   checked includes also the "Content-Lenght" header presence and value.
 
    If a parameter is given, the mime described will be also checked
    against the "Content-Type" header.
 
    Meaning of the parameters is as follows:
-     * mime - mime to be checked against the "Content-Type"
-       header. If not present or 0, this check will be disabled.
+     * mime - mime to be checked against the "Content-Type" header. If not
+       present or 0, this check will be disabled.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
@@ -735,13 +812,12 @@ if(has_body("application/sdp"))
 }
 ...
 
-1.3.30.  is_privacy(privacy_type)
+3.30. is_privacy(privacy_type)
 
-   The function returns true if the SIP message has a Privacy
-   header field that includes the given privacy_type among its
-   privacy values. See
-   http://www.iana.org/assignments/sip-priv-values for possible
-   privacy type values.
+   The function returns true if the SIP message has a Privacy header field
+   that includes the given privacy_type among its privacy values. See
+   http://www.iana.org/assignments/sip-priv-values for possible privacy
+   type values.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
@@ -754,10 +830,10 @@ if(is_privacy("id"))
 }
 ...
 
-1.3.31.  cmp_str(str1, str2)
+3.31. cmp_str(str1, str2)
 
-   The function returns true if the two parameters matches as
-   string case sensitive comparison.
+   The function returns true if the two parameters matches as string case
+   sensitive comparison.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
@@ -770,10 +846,10 @@ if(cmp_str("$rU", "kamailio"))
 }
 ...
 
-1.3.32.  cmp_istr(str1, str2)
+3.32. cmp_istr(str1, str2)
 
-   The function returns true if the two parameters matches as
-   string case insensitive comparison.
+   The function returns true if the two parameters matches as string case
+   insensitive comparison.
 
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE and BRANCH_ROUTE.
@@ -786,20 +862,50 @@ if(cmp_istr("$rU@you", "kamailio@YOU"))
 }
 ...
 
-1.4. Known Limitations
+3.33. msg_apply_changes()
 
-   Search functions are applied to the original request, i.e.,
-   they ignore all changes resulting from message processing in
-   Kamailio script.
+   Use this function to apply changes done on SIP request content. Be
+   careful when using this function -- due to special handling of changes
+   done to SIM message buffer so far, using this function might change the
+   behaviour of your config as it was so far -- do test properly your
+   config!
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 1.33. msg_apply_changes() usage
+...
+append_hf("My-Header: yes\r\n");
+if(msg_apply_changes())
+{
+    # msg buffer has a new content
+        if(is_present_hf("My-Header"))
+        {
+        # will get here always
+    }
+}
+...
+
+4. Known Limitations
+
+   Search functions are applied to the original request, i.e., they ignore
+   all changes resulting from message processing in Kamailio script.
 
 Chapter 2. Developer Guide
 
-2.1. Functions
+   Table of Contents
+
+   1. Functions
+
+        1.1. load_textops(*import_structure)
+
+1. Functions
+
+   1.1. load_textops(*import_structure)
 
-2.1.1.  load_textops(*import_structure)
+1.1. load_textops(*import_structure)
 
    For programmatic use only--import the Textops API.
 
    Meaning of the parameters is as follows:
-     * import_structure - Pointer to the import structure - see
-       "struct textops_binds" in modules/textops/api.h
+     * import_structure - Pointer to the import structure - see "struct
+       textops_binds" in modules/textops/api.h
index ce46bda..356e9c1 100644 (file)
@@ -1208,6 +1208,38 @@ if(cmp_istr("$rU@you", "kamailio@YOU"))
                </example>
        </section>
 
+       <section>
+               <title>
+               <function moreinfo="none">msg_apply_changes()</function>
+               </title>
+               <para>
+               Use this function to apply changes done on SIP request content. Be
+               careful when using this function -- due to special handling of changes
+               done to SIM message buffer so far, using this function might change
+               the behaviour of your config as it was so far -- do test properly
+               your config!
+               </para>
+               <para>
+               This function can be used from REQUEST_ROUTE.
+               </para>
+               <example>
+               <title><function>msg_apply_changes()</function> usage</title>
+               <programlisting format="linespecific">
+...
+append_hf("My-Header: yes\r\n");
+if(msg_apply_changes())
+{
+    # msg buffer has a new content
+       if(is_present_hf("My-Header"))
+       {
+        # will get here always
+    }
+}
+...
+</programlisting>
+               </example>
+       </section>
+
        </section>
        <section>
                <title>Known Limitations</title>
index d1b8418..bc7bdcd 100644 (file)
@@ -65,7 +65,7 @@
 #include "../../parser/parse_content.h"
 #include "../../parser/parse_param.h"
 #include "../../lib/kcore/parse_privacy.h"
-#include "../../mod_fix.h"
+#include "../../msg_translator.h"
 #include "../../ut.h"
 #include "../../lib/kcore/cmpapi.h"
 #include <stdio.h>
@@ -122,6 +122,7 @@ static int cmp_str_f(struct sip_msg *msg, char *str1, char *str2 );
 static int cmp_istr_f(struct sip_msg *msg, char *str1, char *str2 );
 static int remove_hf_re_f(struct sip_msg* msg, char* key, char* foo);
 static int is_present_hf_re_f(struct sip_msg* msg, char* key, char* foo);
+static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2);
 
 static int fixup_substre(void**, int);
 static int hname_fixup(void** param, int param_no);
@@ -241,6 +242,9 @@ static cmd_export_t cmds[]={
        {"cmp_istr",  (cmd_function)cmp_istr_f, 2,
                fixup_spve_spve, 0,
                REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+       {"msg_apply_changes",      (cmd_function)msg_apply_changes_f,     0,
+               0, 0,
+               REQUEST_ROUTE },
 
        {0,0,0,0,0,0}
 };
@@ -880,6 +884,7 @@ static int filter_body_f(struct sip_msg* msg, char* _content_type,
            return -1;
        }
        boundary.s = NULL;
+       boundary.len = 0;
        for (p = list; p; p = p->next) {
            if ((p->name.len == 8)
                && (strncasecmp(p->name.s, "boundary", 8) == 0)) {
@@ -1842,6 +1847,82 @@ static int cmp_istr_f(struct sip_msg *msg, char *str1, char *str2)
        return -2;
 }
 
+static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2)
+{
+       struct dest_info dst;
+       str obuf;
+       sip_msg_t tmp;
+
+       if(get_route_type()!=REQUEST_ROUTE)
+       {
+               LM_ERR("invalid usage - not in request route\n");
+               return -1;
+       }
+
+       init_dest_info(&dst);
+       dst.proto = PROTO_UDP;
+       obuf.s = build_req_buf_from_sip_req(msg,
+                       (unsigned int*)&obuf.len, &dst,
+                       BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
+       if(obuf.s == NULL)
+       {
+               LM_ERR("couldn't update msg buffer content\n");
+               return -1;
+       }
+       if(obuf.len>=BUF_SIZE)
+       {
+               LM_ERR("new buffer overflow (%d)\n", obuf.len);
+               pkg_free(obuf.s);
+               return -1;
+       }
+       /* temporary copy */
+       memcpy(&tmp, msg, sizeof(sip_msg_t));
+
+       /* reset dst uri and path vector to avoid freeing - restored later */
+       if(msg->dst_uri.s!=NULL)
+       {
+               msg->dst_uri.s = NULL;
+               msg->dst_uri.len = 0;
+       }
+       if(msg->path_vec.s!=NULL)
+       {
+               msg->path_vec.s = NULL;
+               msg->path_vec.len = 0;
+       }
+
+       /* free old msg structure */
+       free_sip_msg(msg);
+       memset(msg, 0, sizeof(sip_msg_t));
+
+       /* restore msg fields */
+       msg->buf                = tmp.buf;
+       msg->id                 = tmp.id;
+       msg->rcv                = tmp.rcv;
+       msg->set_global_address = tmp.set_global_address;
+       msg->set_global_port    = tmp.set_global_port;
+       msg->flags              = tmp.flags;
+       msg->msg_flags          = tmp.msg_flags;
+       msg->force_send_socket  = tmp.force_send_socket;
+       msg->dst_uri            = tmp.dst_uri;
+       msg->path_vec           = tmp.path_vec;
+
+       memcpy(msg->buf, obuf.s, obuf.len);
+       msg->len = obuf.len;
+       msg->buf[msg->len] = '\0';
+
+       /* free new buffer - copied in the static buffer from old sip_msg_t */
+       pkg_free(obuf.s);
+
+       /* reparse the message */
+       LM_DBG("SIP Request content updated - reparsing\n");
+       if (parse_msg(msg->buf, msg->len, msg)!=0){
+               LM_ERR("parse_msg failed\n");
+               return -1;
+       }
+
+       return 1;
+}
+
 int fixup_regexpNL_none(void** param, int param_no)
 {
        regex_t* re;
index 79b1df6..c771760 100644 (file)
@@ -13,62 +13,65 @@ Henning Westerholt
 
    Copyright © 2008 1&1 Internet AG
    Revision History
-   Revision $Revision: 4863 $ $Date: 2008-09-05 13:11:33 +0200
-                              (Fr, 05 Sep 2008) $
-     __________________________________________________________
+   Revision $Revision$ $Date$
+     __________________________________________________________________
 
    Table of Contents
 
    1. Admin Guide
 
-        1.1. Overview
-        1.2. Dependencies
+        1. Overview
+        2. Dependencies
 
-              1.2.1. Kamailio Modules
-              1.2.2. External Libraries or Applications
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
 
-        1.3. Exported Parameters
+        3. Exported Parameters
 
-              1.3.1. use_domain (integer)
+              3.1. use_domain (integer)
 
-        1.4. Exported Functions
+        4. Exported Functions
 
-              1.4.1. check_user_blacklist (string user, string
-                      domain, string number, string table)
+              4.1. check_user_blacklist (string user, string domain,
+                      string number, string table)
 
-              1.4.2. check_blacklist (string table)
+              4.2. check_user_whitelist (string user, string domain,
+                      string number, string table)
 
-        1.5. MI Commands
+              4.3. check_blacklist (string table)
 
-              1.5.1. reload_blacklist
+        5. MI Commands
 
-        1.6. Installation and Running
+              5.1. reload_blacklist
 
-              1.6.1. Database setup
+        6. Installation and Running
+
+              6.1. Database setup
 
    2. Module parameter for database access.
 
-        2.1. db_url (String)
-        2.2. userblacklist_table (String)
-        2.3. userblacklist_id_col (string)
-        2.4. userblacklist_username_col (string)
-        2.5. userblacklist_domain_col (string)
-        2.6. userblacklist_prefix_col (string)
-        2.7. userblacklist_whitelist_col (string)
-        2.8. globalblacklist_table (String)
-        2.9. globalblacklist_id_col (string)
-        2.10. globalblacklist_prefix_col (string)
-        2.11. globalblacklist_whitelist_col (string)
-        2.12. globalblacklist_description_col (string)
+        1. db_url (String)
+        2. userblacklist_table (String)
+        3. userblacklist_id_col (string)
+        4. userblacklist_username_col (string)
+        5. userblacklist_domain_col (string)
+        6. userblacklist_prefix_col (string)
+        7. userblacklist_whitelist_col (string)
+        8. globalblacklist_table (String)
+        9. globalblacklist_id_col (string)
+        10. globalblacklist_prefix_col (string)
+        11. globalblacklist_whitelist_col (string)
+        12. globalblacklist_description_col (string)
 
    List of Examples
 
    1.1. Set use_domain parameter
    1.2. check_user_blacklist usage
-   1.3. check_blacklist usage
-   1.4. reload_blacklists usage
-   1.5. Example database content - globalblacklist table
-   1.6. Example database content - userblacklist table
+   1.3. check_user_blacklist usage
+   1.4. check_blacklist usage
+   1.5. reload_blacklists usage
+   1.6. Example database content - globalblacklist table
+   1.7. Example database content - userblacklist table
    2.1. Set db_url parameter
    2.2. Set userblacklist_table parameter
    2.3. Set userblacklist_id_col parameter
@@ -84,55 +87,88 @@ Henning Westerholt
 
 Chapter 1. Admin Guide
 
-1.1. Overview
-
-   The userblacklist module allows Kamailio to handle blacklists
-   on a per user basis. This information is stored in a database
-   table, which is queried to decide if the number (more exactly,
-   the request URI user) is blacklisted or not.
-
-   An additional functionality that this module provides is the
-   ability to handle global blacklists. This lists are loaded on
-   startup into memory, thus providing a better performance then
-   in the userblacklist case. This global blacklists are useful to
-   only allow calls to certain international destinations, i.e.
-   block all not whitelisted numbers. They could also used to
-   prevent the blacklisting of important numbers, as whitelisting
-   is supported too. This is useful for example to prevent the
-   customer from blocking emergency call number or service
-   hotlines.
-
-   The module exports two functions, check_blacklist and
-   check_user_blacklist for usage in the config file. Furthermore
-   its provide a FIFO function to reload the global blacklist
-   cache.
-
-   Please note that only numerical strings for matching are
-   supported at the moment (the used library supports this
-   already, but its not yet implemented in the module). Non-digits
-   on the beginning of the matched string are skipped, any later
-   non-digits will stop the matching on this position.
-
-1.2. Dependencies
-
-1.2.1. Kamailio Modules
-
-   The module depends on the following modules (in the other words
-   the listed modules must be loaded before this module):
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. use_domain (integer)
+
+   4. Exported Functions
+
+        4.1. check_user_blacklist (string user, string domain, string
+                number, string table)
+
+        4.2. check_user_whitelist (string user, string domain, string
+                number, string table)
+
+        4.3. check_blacklist (string table)
+
+   5. MI Commands
+
+        5.1. reload_blacklist
+
+   6. Installation and Running
+
+        6.1. Database setup
+
+1. Overview
+
+   The userblacklist module allows Kamailio to handle blacklists on a per
+   user basis. This information is stored in a database table, which is
+   queried to decide if the number (more exactly, the request URI user) is
+   blacklisted or not.
+
+   An additional functionality that this module provides is the ability to
+   handle global blacklists. This lists are loaded on startup into memory,
+   thus providing a better performance then in the userblacklist case.
+   This global blacklists are useful to only allow calls to certain
+   international destinations, i.e. block all not whitelisted numbers.
+   They could also used to prevent the blacklisting of important numbers,
+   as whitelisting is supported too. This is useful for example to prevent
+   the customer from blocking emergency call number or service hotlines.
+
+   The module exports three functions, check_blacklist
+   check_user_blacklist and check_user_whitelist for usage in the config
+   file. Furthermore its provide a FIFO function to reload the global
+   blacklist cache.
+
+   Please note that only numerical strings for matching are supported at
+   the moment (the used library supports this already, but its not yet
+   implemented in the module). Non-digits on the beginning of the matched
+   string are skipped, any later non-digits will stop the matching on this
+   position.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The module depends on the following modules (in the other words the
+   listed modules must be loaded before this module):
      * database -- Any database module
 
-1.2.2. External Libraries or Applications
+2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed
-   before running Kamailio with this module loaded:
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
      * none
 
-1.3. Exported Parameters
+3. Exported Parameters
 
-1.3.1. use_domain (integer)
+   3.1. use_domain (integer)
 
-   If set to non-zero value, the domain column in the
-   userblacklist is used.
+3.1. use_domain (integer)
+
+   If set to non-zero value, the domain column in the userblacklist is
+   used.
 
    Default value is "0".
 
@@ -141,19 +177,26 @@ Chapter 1. Admin Guide
 modparam("userblacklist", "use_domain", 0)
 ...
 
-1.4. Exported Functions
+4. Exported Functions
+
+   4.1. check_user_blacklist (string user, string domain, string number,
+          string table)
 
-1.4.1.  check_user_blacklist (string user, string domain, string
-number, string table)
+   4.2. check_user_whitelist (string user, string domain, string number,
+          string table)
 
-   Finds the longest prefix that matches the request URI user (or
-   the number parameter) for the given user and domain name in the
-   database. If a match is found and it is not set to whitelist,
-   false is returned. Otherwise, true is returned.
-   Pseudo-variables or AVPs can be used for the user, domain and
-   number parameters. The number and table variables are optional,
-   the defaults are used if they are ommited. The number parameter
-   can be used to check for example against the from URI user.
+   4.3. check_blacklist (string table)
+
+4.1.  check_user_blacklist (string user, string domain, string number, string
+table)
+
+   Finds the longest prefix that matches the request URI user (or the
+   number parameter) for the given user and domain name in the database.
+   If a match is found and it is not set to whitelist, false is returned.
+   Otherwise, true is returned. Pseudo-variables or AVPs can be used for
+   the user, domain and number parameters. The number and table variables
+   are optional, the defaults are used if they are ommited. The number
+   parameter can be used to check for example against the from URI user.
 
    Example 1.2. check_user_blacklist usage
 ...
@@ -165,13 +208,34 @@ if (!check_user_blacklist("$avp(i:80)", "$avp(i:82)"))
 }
 ...
 
-1.4.2.  check_blacklist (string table)
+4.2.  check_user_whitelist (string user, string domain, string number, string
+table)
+
+   Finds the longest prefix that matches the request URI user (or the
+   number parameter) for the given user and domain name in the database.
+   If a match is found and it is set to whitelist, true is returned.
+   Otherwise, false is returned. Pseudo-variables or AVPs can be used for
+   the user, domain and number parameters. The number and table variables
+   are optional, the defaults are used if they are ommited. The number
+   parameter can be used to check for example against the from URI user.
+
+   Example 1.3. check_user_blacklist usage
+...
+$avp(i:80) = $rU;
+# rewrite the R-URI
+if (!check_user_whitelist("$avp(i:80)", "$avp(i:82)"))
+        # process request
+        exit;
+}
+...
+
+4.3.  check_blacklist (string table)
 
-   Finds the longest prefix that matches the request URI for the
-   given table. If a match is found and it is not set to
-   whitelist, false is returned. Otherwise, true is returned.
+   Finds the longest prefix that matches the request URI for the given
+   table. If a match is found and it is not set to whitelist, false is
+   returned. Otherwise, true is returned.
 
-   Example 1.3. check_blacklist usage
+   Example 1.4. check_blacklist usage
 ...
 if (!check_blacklist("global_blacklist")))
         sl_send_reply("403", "Forbidden");
@@ -179,36 +243,38 @@ if (!check_blacklist("global_blacklist")))
 }
 ...
 
-1.5. MI Commands
+5. MI Commands
+
+   5.1. reload_blacklist
 
-1.5.1.  reload_blacklist
+5.1.  reload_blacklist
 
-   Reload the internal global blacklist cache. This is necessary
-   after the database tables for the global blacklist have been
-   changed.
+   Reload the internal global blacklist cache. This is necessary after the
+   database tables for the global blacklist have been changed.
 
-   Example 1.4. reload_blacklists usage
+   Example 1.5. reload_blacklists usage
 ...
 kamctl fifo reload_blacklist
 ...
 
-1.6. Installation and Running
+6. Installation and Running
+
+   6.1. Database setup
 
-1.6.1. Database setup
+6.1. Database setup
 
-   Before running Kamailio with userblacklist, you have to setup
-   the database table where the module will read the blacklist
-   data. For that, if the table was not created by the
-   installation script or you choose to install everything by
-   yourself you can use the userblacklist-create.sql SQL script in
-   the database directories in the kamailio/scripts folder as
-   template. Database and table name can be set with module
-   parameters so they can be changed, but the name of the columns
-   must be as they are in the SQL script. You can also find the
+   Before running Kamailio with userblacklist, you have to setup the
+   database table where the module will read the blacklist data. For that,
+   if the table was not created by the installation script or you choose
+   to install everything by yourself you can use the
+   userblacklist-create.sql SQL script in the database directories in the
+   kamailio/scripts folder as template. Database and table name can be set
+   with module parameters so they can be changed, but the name of the
+   columns must be as they are in the SQL script. You can also find the
    complete database documentation on the project webpage,
    http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
 
-   Example 1.5. Example database content - globalblacklist table
+   Example 1.6. Example database content - globalblacklist table
 ...
 +----+-----------+-----------+
 | id | prefix    | whitelist |
@@ -220,12 +286,12 @@ kamctl fifo reload_blacklist
 +----+-----------+-----------+
 ...
 
-   This table will setup a global blacklist for all numbers, only
-   allowing calls starting with "1". Numbers that starting with
-   "123456" and "123455787" are also blacklisted, because the
-   longest prefix will be matched.
+   This table will setup a global blacklist for all numbers, only allowing
+   calls starting with "1". Numbers that starting with "123456" and
+   "123455787" are also blacklisted, because the longest prefix will be
+   matched.
 
-   Example 1.6. Example database content - userblacklist table
+   Example 1.7. Example database content - userblacklist table
 ...
 +----+----------------+-------------+-----------+----