Merge remote branch 'origin/misi/ua-profile'
authorMészáros Mihály <misi@niif.hu>
Thu, 17 Nov 2011 13:02:56 +0000 (14:02 +0100)
committerMészáros Mihály <misi@niif.hu>
Thu, 17 Nov 2011 13:02:56 +0000 (14:02 +0100)
* origin/misi/ua-profile:
  modules_k/presence_profile: new modul for basic ua-profile event support
  parser: added ua-profile event type

83 files changed:
Makefile
Makefile.defs
docbook/Makefile
lib/srdb1/db.c
lib/srdb1/db.h
lib/srdb1/db_ut.c
main.c
modules/app_python/python_mod.c
modules/auth/README
modules/auth/api.h
modules/auth/auth_mod.c
modules/auth/doc/functions.xml
modules/ctl/ctl_defaults.h
modules/db_berkeley/bdb_mod.c
modules/db_berkeley/km_db_berkeley.c
modules/db_flatstore/flatstore_mod.c
modules/db_mysql/km_db_mysql.c
modules/db_mysql/km_db_mysql.h
modules/db_mysql/km_dbase.c
modules/db_mysql/km_dbase.h
modules/db_mysql/mysql_mod.c
modules/db_postgres/km_db_postgres.c
modules/db_postgres/pg_mod.c
modules/ndb_redis/README
modules/ndb_redis/doc/ndb_redis_admin.xml
modules/sdpops/sdpops_data.c
modules/sdpops/sdpops_data.h
modules/sdpops/sdpops_mod.c
modules/xhttp_rpc/Makefile [new file with mode: 0644]
modules/xhttp_rpc/README [new file with mode: 0644]
modules/xhttp_rpc/doc/Makefile [new file with mode: 0644]
modules/xhttp_rpc/doc/xhttp_rpc.xml [new file with mode: 0644]
modules/xhttp_rpc/doc/xhttp_rpc_admin.xml [new file with mode: 0644]
modules/xhttp_rpc/xhttp_rpc.c [new file with mode: 0644]
modules/xhttp_rpc/xhttp_rpc.h [new file with mode: 0644]
modules/xhttp_rpc/xhttp_rpc_fnc.c [new file with mode: 0644]
modules/xhttp_rpc/xhttp_rpc_fnc.h [new file with mode: 0644]
modules_k/acc/acc_cdr.c
modules_k/auth_db/Makefile
modules_k/auth_db/README
modules_k/auth_db/authdb_mod.c
modules_k/auth_db/authorize.c
modules_k/auth_db/authorize.h
modules_k/auth_db/doc/auth_db_admin.xml
modules_k/db_oracle/db_oracle.c
modules_k/db_sqlite/db_sqlite.c
modules_k/db_text/dbtext.c
modules_k/db_unixodbc/db_unixodbc.c
modules_k/dialog/README
modules_k/dialog/dialog.c
modules_k/dialog/dlg_db_handler.c
modules_k/dialog/dlg_handlers.c
modules_k/dialog/dlg_hash.h
modules_k/dialog/dlg_profile.c
modules_k/dialog/dlg_var.c
modules_k/dialog/doc/dialog_admin.xml
modules_k/dispatcher/README
modules_k/dispatcher/dispatch.c
modules_k/dispatcher/dispatch.h
modules_k/dispatcher/dispatcher.c
modules_k/dispatcher/doc/dispatcher.cfg
modules_k/dispatcher/doc/dispatcher_admin.xml
modules_k/dispatcher/doc/dispatcher_faq.xml
modules_k/msilo/msilo.c
modules_k/presence/subscribe.c
modules_k/pua/doc/pua_admin.xml
modules_k/pua/pua.c
modules_k/pua/pua_db.c
modules_k/rls/resource_notify.c
modules_k/rls/rls_db.c
modules_k/snmpstats/snmpstats.c
modules_k/textops/textops.c
parser/sdp/sdp.c
parser/sdp/sdp_helpr_funcs.c
pkg/kamailio/deb/debian/control
pkg/kamailio/deb/lenny/control
pkg/kamailio/deb/lucid/control
pkg/kamailio/deb/squeeze/control
pkg/kamailio/deb/wheezy/control
sip-router.8
sr_module.c
utils/kamctl/kamctl
utils/sercmd/Makefile

index 2d063f4..8606ab4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -210,8 +210,8 @@ module_group_kstandard=acc alias_db auth auth_db benchmark call_control \
                                avpops cfg_db cfg_rpc ctl db_flatstore dialplan enum \
                                iptrtpproxy lcr mediaproxy mi_rpc pdb sanity tm topoh \
                                blst prefix_route counters debugger matrix mqueue mtree \
-                               pipelimit rtpproxy textopsx xhttp ipops p_usrloc sdpops \
-                               async sipcapture, dmq
+                               pipelimit rtpproxy textopsx xhttp xhttp_rpc ipops p_usrloc \
+                               sdpops async sipcapture dmq
 
 # K mysql module
 module_group_kmysql=db_mysql
index 69f8d74..49f2f82 100644 (file)
@@ -162,7 +162,7 @@ INSTALL_FLAVOUR=$(FLAVOUR)
 VERSION = 3
 PATCHLEVEL = 3
 SUBLEVEL =  0
-EXTRAVERSION = -dev0
+EXTRAVERSION = -dev2
 
 # memory debugger switcher
 # 0 - off (release mode)
index 6d5408d..0fd61e2 100644 (file)
@@ -113,6 +113,12 @@ ifeq ($(validate), 0)
        override xsltproc_flags := $(xsltproc_flags) --novalid
 endif
 
+ifeq ($(nocatalog),yes)
+XMLCATALOGX=
+else
+XMLCATALOGX=XML_CATALOG_FILES=$(catalog)
+endif
+
 all_deps = Makefile $(docbook_dir)/Makefile $(docbook_dir)/entities.xml \
                   $(dep_xsl) $(catalog) $(extra_deps)
 
@@ -133,12 +139,12 @@ txt text plaintext: $(txt_files)
 readme README: ../README
 
 ../README: $(readme_docs) $(readme_deps) $(readme_xsl) $(all_deps)
-       XML_CATALOG_FILES=$(catalog) $(xsltproc) $(xsltproc_flags) \
+       $(XMLCATALOGX) $(xsltproc) $(xsltproc_flags) \
                --xinclude \
         $(readme_xsl) $< | $(lynx) $(lynx_flags) -stdin -dump > $@
 
 $(output_dir)/%.html: %.xml %.d $(single_html_xsl) $(all_deps)
-       XML_CATALOG_FILES=$(catalog) $(xsltproc) $(xsltproc_flags) \
+       $(XMLCATALOGX) $(xsltproc) $(xsltproc_flags) \
         --xinclude \
         --stringparam base.dir "$(output_dir)/" \
         --stringparam root.filename "$(basename $<)" \
@@ -148,7 +154,7 @@ $(output_dir)/%.html: %.xml %.d $(single_html_xsl) $(all_deps)
 
 
 $(output_dir)/%.txt: %.xml %.d $(txt_xsl) $(all_deps)
-       XML_CATALOG_FILES=$(catalog) $(xsltproc) $(xsltproc_flags) \
+       $(XMLCATALOGX) $(xsltproc) $(xsltproc_flags) \
                --xinclude \
         $(txt_xsl) $< | $(lynx) $(lynx_flags) -stdin -dump > $@
 
@@ -164,7 +170,7 @@ $(output_dir)/%.txt: %.xml %.d $(txt_xsl) $(all_deps)
 
 .PHONY: check
 check: $(docs) $(html_docs) $(txt_docs) $(readme_docs)
-       XML_CATALOG_FILES=$(catalog) $(xmllint) $(xmllint_flags) $<
+       $(XMLCATALOGX) $(xmllint) $(xmllint_flags) $<
 
 .PHONY: clean
 clean:
index b8abefa..892a849 100644 (file)
@@ -64,6 +64,7 @@
 #include "db_cap.h"
 #include "db_id.h"
 #include "db_pool.h"
+#include "db_query.h"
 #include "db.h"
 
 static unsigned int MAX_URL_LENGTH = 255;      /*!< maximum length of a SQL URL */
@@ -490,3 +491,17 @@ int db_load_bulk_data(db_func_t* binding, db1_con_t* handle, str* name, db_key_t
 
        return 0;
 }
+
+/**
+ * \brief DB API init function.
+ *
+ * This function must be executed by DB connector modules at load time to
+ * initialize the internals of DB API library.
+ * \return returns 0 on successful initialization, -1 on error.
+ */
+int db_api_init(void)
+{
+       if(db_query_init()<0)
+               return -1;
+       return 0;
+}
index 294488c..64969a8 100644 (file)
@@ -443,4 +443,14 @@ typedef int (*db_bind_api_f)(db_func_t *dbb);
 int db_load_bulk_data(db_func_t* binding, db1_con_t* handle, str* name, db_key_t* cols,
                      unsigned int count, unsigned int strict, db1_res_t* res);
 
+/**
+ * \brief DB API init function.
+ *
+ * This function must be executed by DB connector modules at load time to
+ * initialize the internals of DB API library.
+ * \return returns 0 on successful initialization, -1 on error.
+ */
+int db_api_init(void);
+
+
 #endif /* DB1_H */
index c34930f..26637f4 100644 (file)
@@ -334,21 +334,35 @@ int db_print_where(const db1_con_t* _c, char* _b, const int _l, const db_key_t*
        }
 
        for(i = 0; i < _n; i++) {
-               if (_o) {
-                       ret = snprintf(_b + len, _l - len, "%.*s%s", _k[i]->len, _k[i]->s, _o[i]);
+               if (_o && strncmp(_o[i], OP_BITWISE_AND, 1) == 0) {
+                       char tmp_buf[16];
+                       int tmp_len = 15;
+                       memset(tmp_buf, '0', 16);
+                       if ((*val2str)(_c, &(_v[i]), tmp_buf, &tmp_len) < 0) {
+                               LM_ERR("Error while converting value to string\n");
+                               return -1;
+                       }
+                       ret = snprintf(_b + len, _l - len, "%.*s&%.*s=%.*s", _k[i]->len, _k[i]->s, tmp_len, tmp_buf, tmp_len, tmp_buf);
                        if (ret < 0 || ret >= (_l - len)) goto error;
                        len += ret;
                } else {
-                       ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s);
-                       if (ret < 0 || ret >= (_l - len)) goto error;
-                       len += ret;
-               }
-               l = _l - len;
-               if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) {
-                       LM_ERR("Error while converting value to string\n");
-                       return -1;
+                       if (_o) {
+                               ret = snprintf(_b + len, _l - len, "%.*s%s", _k[i]->len, _k[i]->s, _o[i]);
+                               if (ret < 0 || ret >= (_l - len)) goto error;
+                               len += ret;
+                       } else {
+                               ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s);
+                               if (ret < 0 || ret >= (_l - len)) goto error;
+                               len += ret;
+                       }
+                       l = _l - len;
+                       if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) {
+                               LM_ERR("Error while converting value to string\n");
+                               return -1;
+                       }
+                       len += l;
                }
-               len += l;
+
                if (i != (_n - 1)) {
                        ret = snprintf(_b + len, _l - len, " AND ");
                        if (ret < 0 || ret >= (_l - len)) goto error;
diff --git a/main.c b/main.c
index 5e4defa..741bdad 100644 (file)
--- a/main.c
+++ b/main.c
@@ -282,6 +282,34 @@ void print_ct_constants()
 #endif
 }
 
+/* print compile-time constants */
+void print_internals()
+{
+       printf("Print out of %s internals\n", NAME);
+       printf("  Version: %s\n", full_version);
+       printf("  Default config: %s\n", CFG_FILE);
+       printf("  Default paths to modules: %s\n", MODS_DIR);
+       printf("  Compile flags: %s\n", ver_flags );
+       printf("  MAX_RECV_BUFFER_SIZE=%d\n", MAX_RECV_BUFFER_SIZE);
+       printf("  MAX_LISTEN=%d\n", MAX_LISTEN);
+       printf("  MAX_URI_SIZE=%d\n", MAX_URI_SIZE);
+       printf("  BUF_SIZE=%d\n", BUF_SIZE);
+       printf("  DEFAULT PKG_SIZE=%uMB\n", PKG_MEM_SIZE);
+#ifdef SHM_MEM
+       printf("  DEFAULT SHM_SIZE=%uMB\n", SHM_MEM_SIZE);
+#endif
+#ifdef ADAPTIVE_WAIT
+       printf("  ADAPTIVE_WAIT_LOOPS=%d\n", ADAPTIVE_WAIT_LOOPS);
+#endif
+#ifdef USE_TCP
+       printf("  TCP poll methods: %s\n", poll_support);
+#endif
+       printf("  Source code revision ID: %s\n", ver_id);
+       printf("  Compiled with: %s\n", ver_compiler);
+       printf("  Compiled on: %s\n", ver_compiled_time);
+       printf("Thank you for flying %s!\n", NAME);
+}
+
 /* debugging function */
 /*
 void receive_stdin_loop()
@@ -1788,7 +1816,7 @@ int main(int argc, char** argv)
 
        daemon_status_init();
        /* command line options */
-       options=  ":f:cm:M:dVhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:SQ:O:a:A:"
+       options=  ":f:cm:M:dVIhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:SQ:O:a:A:"
 #ifdef STATS
                "s:"
 #endif
@@ -1896,6 +1924,10 @@ int main(int argc, char** argv)
                                        printf("compiled on %s with %s\n",
                                                        ver_compiled_time, ver_compiler );
 
+                                       exit(0);
+                                       break;
+                       case 'I':
+                                       print_internals();
                                        exit(0);
                                        break;
                        case 'E':
@@ -2048,6 +2080,7 @@ try_again:
                        case 'M':
                        case 'd':
                        case 'V':
+                       case 'I':
                        case 'h':
                        case 'O':
                        case 'A':
index c89c928..c178551 100644 (file)
@@ -85,7 +85,7 @@ struct module_exports exports = {
 static int
 mod_init(void)
 {
-    char *dname, *bname, *tname;
+    char *dname, *bname, *dname_src, *bname_src;
     int i;
     PyObject *sys_path, *pDir, *pModule, *pFunc, *pArgs;
     PyThreadState *mainThreadState;
@@ -100,19 +100,19 @@ mod_init(void)
         child_init_mname.len = strlen(child_init_mname.s);
     }
 
-    tname = as_asciiz(&script_name);
-       if(tname==NULL)
-       {
-               LM_ERR("no more pkg memory\n");
-               return -1;
-       }
-    dname = dirname(tname);
+    dname_src = as_asciiz(&script_name);
+    bname_src = as_asciiz(&script_name);
+    if(dname_src==NULL || bname_src==NULL)
+    {
+            LM_ERR("no more pkg memory\n");
+            return -1;
+    }
+
+    dname = dirname(dname_src);
     if (strlen(dname) == 0)
         dname = ".";
-       memcpy(tname, script_name.s, script_name.len);
-    bname = basename(tname);
+    bname = basename(bname_src);
     i = strlen(bname);
-       pkg_free(tname);
     if (bname[i - 1] == 'c' || bname[i - 1] == 'o')
         i -= 1;
     if (bname[i - 3] == '.' && bname[i - 2] == 'p' && bname[i - 1] == 'y') {
@@ -159,6 +159,9 @@ mod_init(void)
         return -1;
     }
 
+    pkg_free(dname_src);
+    pkg_free(bname_src);
+
     pFunc = PyObject_GetAttrString(pModule, mod_init_fname.s);
     Py_DECREF(pModule);
     /* pFunc is a new reference */
index 04d7594..a29bcf5 100644 (file)
@@ -15,7 +15,7 @@ Daniel-Constantin Mierla
    asipto.com
    <miconda@gmail.com>
 
-   Copyright Â© 2002, 2003 FhG FOKUS
+   Copyright © 2002, 2003 FhG FOKUS
      __________________________________________________________________
 
    1.1. Overview
@@ -43,8 +43,9 @@ Daniel-Constantin Mierla
         1.4.1. consume_credentials()
         1.4.2. www_challenge(realm, flags)
         1.4.3. proxy_challenge(realm, flags)
-        1.4.4. pv_www_authenticate(realm, passwd, flags)
-        1.4.5. pv_proxy_authenticate(realm, passwd, flags)
+        1.4.4. auth_challenge(realm, flags)
+        1.4.5. pv_www_authenticate(realm, passwd, flags)
+        1.4.6. pv_proxy_authenticate(realm, passwd, flags)
 
 1.1. Overview
 
@@ -66,7 +67,7 @@ Daniel-Constantin Mierla
 
 1.3. Parameters
 
-1.3.1.  auth_checks_register, auth_checks_no_dlg, and auth_checks_in_dlg
+1.3.1. auth_checks_register, auth_checks_no_dlg, and auth_checks_in_dlg
 (flags)
 
    These three module parameters control which optional integrity checks
@@ -514,7 +515,7 @@ if (www_authenticate("realm", "subscriber)) {
 };
 ...
 
-1.4.2.  www_challenge(realm, flags)
+1.4.2. www_challenge(realm, flags)
 
    The function challenges a user agent. It will generate a WWW-Authorize
    header field containing a digest challenge, it will put the header
@@ -528,7 +529,7 @@ if (www_authenticate("realm", "subscriber)) {
      * realm - Realm is a opaque string that the user agent should present
        to the user so he can decide what username and password to use.
        Usually this is domain of the host the server is running on.
-       It must not be empty string “”. In case of REGISTER requests To
+       It must not be empty string "". In case of REGISTER requests To
        header field domain (e.g., variable $td) can be used (because this
        header field represents the user being registered), for all other
        messages From header field domain can be used (e.g., variable $fd).
@@ -550,7 +551,7 @@ if (!www_authenticate("$td", "subscriber")) {
 }
 ...
 
-1.4.3.  proxy_challenge(realm, flags)
+1.4.3. proxy_challenge(realm, flags)
 
    The function challenges a user agent. It will generate a
    Proxy-Authorize header field containing a digest challenge, it will put
@@ -567,12 +568,31 @@ if (!www_authenticate("$td", "subscriber")) {
 
    Example 16. proxy_challenge usage
 ...
-if (!proxy_authenticate("$fd", "subscriber)) {
+if (!proxy_authenticate("$fd", "subscriber")) {
         proxy_challenge("$fd", "1");
 };
 ...
 
-1.4.4.  pv_www_authenticate(realm, passwd, flags)
+1.4.4. auth_challenge(realm, flags)
+
+   The function challenges a user agent for authentication. It combines
+   the functions www_challenge() and proxy_challenge(), by calling
+   internally the first one for REGISTER requests and the second one for
+   the rest of the request types.
+
+   Meaning of the parameters the same as for function www_challenge(realm,
+   flags)
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 17. proxy_challenge usage
+...
+if (!auth_check("$fd", "subscriber", "1")) {
+        auth_challenge("$fd", "1");
+};
+...
+
+1.4.5. pv_www_authenticate(realm, passwd, flags)
 
    The function verifies credentials according to RFC2617. If the
    credentials are verified successfully then the function will succeed
@@ -596,7 +616,7 @@ if (!proxy_authenticate("$fd", "subscriber)) {
      * realm - Realm is a opaque string that the user agent should present
        to the user so he can decide what username and password to use.
        Usually this is domain of the host the server is running on.
-       It must not be empty string “”. In case of REGISTER requests To
+       It must not be empty string "". In case of REGISTER requests To
        header field domain (e.g., varibale $td) can be used (because this
        header field represents a user being registered), for all other
        messages From header field domain can be used (e.g., varibale $fd).
@@ -616,14 +636,14 @@ if (!proxy_authenticate("$fd", "subscriber)) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 17. pv_www_authenticate usage
+   Example 18. pv_www_authenticate usage
 ...
 if (!pv_www_authenticate("$td", "123abc", "0")) {
         www_challenge("$td", "1");
 };
 ...
 
-1.4.5.  pv_proxy_authenticate(realm, passwd, flags)
+1.4.6. pv_proxy_authenticate(realm, passwd, flags)
 
    The function verifies credentials according to RFC2617. If the
    credentials are verified successfully then the function will succeed
@@ -638,7 +658,7 @@ if (!pv_www_authenticate("$td", "123abc", "0")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 18. pv_proxy_authenticate usage
+   Example 19. pv_proxy_authenticate usage
 ...
 $avp(password)="xyz";
 if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
index 976a547..9e21c0c 100644 (file)
@@ -44,6 +44,7 @@
  * return codes to config by auth functions
  */
 typedef enum auth_cfg_result {
+       AUTH_USER_MISMATCH = -8,    /*!< Auth user != From/To user */
        AUTH_NONCE_REUSED = -6,     /*!< Returned if nonce is used more than once */
        AUTH_NO_CREDENTIALS = -5,   /*!< Credentials missing */
        AUTH_STALE_NONCE = -4,      /*!< Stale nonce */
index a28c2ba..4dabc10 100644 (file)
@@ -87,6 +87,7 @@ static int fixup_pv_auth(void **param, int param_no);
 
 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags);
 static int www_challenge(struct sip_msg *msg, char* realm, char *flags);
+static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags);
 static int fixup_auth_challenge(void **param, int param_no);
 
 
@@ -138,6 +139,8 @@ static cmd_export_t cmds[] = {
                        fixup_auth_challenge, REQUEST_ROUTE},
     {"proxy_challenge",        (cmd_function)proxy_challenge,        2,
                        fixup_auth_challenge, REQUEST_ROUTE},
+    {"auth_challenge",         (cmd_function)w_auth_challenge,       2,
+                       fixup_auth_challenge, REQUEST_ROUTE},
     {"pv_www_authorize",       (cmd_function)pv_www_authenticate,    3,
                        fixup_pv_auth, REQUEST_ROUTE},
     {"pv_www_authenticate",    (cmd_function)pv_www_authenticate,    3,
@@ -749,6 +752,47 @@ error:
        return -1;
 }
 
+/**
+ *
+ */
+static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags)
+{
+       int vflags = 0;
+       str srealm  = {0, 0};
+
+       if((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
+               return 1;
+       }
+
+       if(get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
+               LM_ERR("failed to get realm value\n");
+               goto error;
+       }
+
+       if(srealm.len==0) {
+               LM_ERR("invalid realm value - empty content\n");
+               goto error;
+       }
+
+       if(get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
+               LM_ERR("invalid flags value\n");
+               goto error;
+       }
+
+       if(msg->REQ_METHOD==METHOD_REGISTER)
+               return auth_challenge(msg, &srealm, vflags, HDR_AUTHORIZATION_T);
+       else
+               return auth_challenge(msg, &srealm, vflags, HDR_PROXYAUTH_T);
+
+error:
+       if(!(vflags&4)) {
+               if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
+                       return -4;
+       }
+       return -1;
+}
+
+
 /**
  * @brief fixup function for {www,proxy}_challenge
  */
index d328def..f094e44 100644 (file)
@@ -126,7 +126,7 @@ if (!www_authenticate("$td", "subscriber")) {
                <title>proxy_challenge usage</title>
                <programlisting format="linespecific">
 ...
-if (!proxy_authenticate("$fd", "subscriber)) {
+if (!proxy_authenticate("$fd", "subscriber")) {
        proxy_challenge("$fd", "1");
 };
 ...
@@ -134,6 +134,33 @@ if (!proxy_authenticate("$fd", "subscriber)) {
                </example>
        </section>
 
+       <section id="auth_challenge">
+               <title>
+                       <function moreinfo="none">auth_challenge(realm, flags)</function>
+               </title>
+               <para>
+               The function challenges a user agent for authentication. It combines
+               the functions www_challenge() and proxy_challenge(), by calling
+               internally the first one for REGISTER requests and the second one for
+               the rest of the request types.
+               </para>
+               <para>Meaning of the parameters the same as for function
+               www_challenge(realm, flags)</para>
+               <para>
+               This function can be used from REQUEST_ROUTE.
+               </para>
+               <example>
+               <title>proxy_challenge usage</title>
+               <programlisting format="linespecific">
+...
+if (!auth_check("$fd", "subscriber", "1")) {
+       auth_challenge("$fd", "1");
+};
+...
+</programlisting>
+               </example>
+       </section>
+
        <section id="pv_www_authenticate">
                <title>
                <function moreinfo="none">pv_www_authenticate(realm, passwd, flags)</function>
index c50af47..5c10c17 100644 (file)
@@ -4,7 +4,13 @@
 #ifndef __ctl_defaults_h
 #define __ctl_defaults_h
 /*listen by default on: */
+#ifdef SRNAME
+/* this is used when compiling sercmd tool */
+#define DEFAULT_CTL_SOCKET  "unixs:/tmp/" SRNAME "_ctl"
+#else
+/* this is used when compiling sip server */
 #define DEFAULT_CTL_SOCKET  "unixs:/tmp/" NAME "_ctl"
+#endif
 /* port used by default for tcp/udp if no port is explicitely specified */
 #define DEFAULT_CTL_PORT 2049
 
index 1adf86d..81bac08 100644 (file)
@@ -108,6 +108,13 @@ struct module_exports exports = {
 };
 
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 static int bdb_mod_init(void)
 {
        bdb_params_t p;
index d350d8e..61af129 100644 (file)
@@ -129,7 +129,7 @@ int km_mod_init(void)
        if(km_bdblib_init(&p))
                return -1;
 
-       return db_query_init();
+       return 0;
 }
 
 void km_destroy(void)
index efbe6e1..043a8fd 100644 (file)
@@ -139,6 +139,13 @@ struct module_exports exports = {
 };
 
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 static int mod_init(void)
 {
        if (flat_delimiter.len != 1) {
index 1f758ba..462dc60 100644 (file)
@@ -90,7 +90,7 @@ struct kam_module_exports kam_exports = {
 int kam_mysql_mod_init(void)
 {
        LM_DBG("MySQL client version is %s\n", mysql_get_client_info());
-       return db_mysql_alloc_buffer();
+       return 0;
 }
 
 int db_mysql_bind_api(db_func_t *dbb)
index 4705ca0..9fd9747 100644 (file)
@@ -47,4 +47,11 @@ int db_mysql_bind_api(db_func_t *dbb);
 
 int kam_mysql_mod_init(void);
 
+/**
+ * Allocate a buffer for database module
+ * No function should be called before this
+ * \return zero on success, negative value on failure
+ */
+int db_mysql_alloc_buffer(void);
+
 #endif /* KM_DB_MOD_H */
index d85fe5f..da429d6 100644 (file)
@@ -50,7 +50,7 @@
 #include "km_db_mysql.h"
 #include "km_dbase.h"
 
-static char *sql_buf;
+static char *mysql_sql_buf;
 
 
 /**
@@ -513,32 +513,32 @@ int db_mysql_affected_rows(const db1_con_t* _h)
                return -1;
        }
  
-       ret = snprintf(sql_buf, sql_buffer_size, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s);
+       ret = snprintf(mysql_sql_buf, sql_buffer_size, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s);
        if (ret < 0 || ret >= sql_buffer_size) goto error;
        off = ret;
 
-       ret = db_print_columns(sql_buf + off, sql_buffer_size - off, _k, _n);
+       ret = db_print_columns(mysql_sql_buf + off, sql_buffer_size - off, _k, _n);
        if (ret < 0) return -1;
        off += ret;
 
-       ret = snprintf(sql_buf + off, sql_buffer_size - off, ") values (");
+       ret = snprintf(mysql_sql_buf + off, sql_buffer_size - off, ") values (");
        if (ret < 0 || ret >= (sql_buffer_size - off)) goto error;
        off += ret;
-       ret = db_print_values(_h, sql_buf + off, sql_buffer_size - off, _v, _n, db_mysql_val2str);
+       ret = db_print_values(_h, mysql_sql_buf + off, sql_buffer_size - off, _v, _n, db_mysql_val2str);
        if (ret < 0) return -1;
        off += ret;
 
-       *(sql_buf + off++) = ')';
+       *(mysql_sql_buf + off++) = ')';
        
-       ret = snprintf(sql_buf + off, sql_buffer_size - off, " on duplicate key update ");
+       ret = snprintf(mysql_sql_buf + off, sql_buffer_size - off, " on duplicate key update ");
        if (ret < 0 || ret >= (sql_buffer_size - off)) goto error;
        off += ret;
        
-       ret = db_print_set(_h, sql_buf + off, sql_buffer_size - off, _k, _v, _n, db_mysql_val2str);
+       ret = db_print_set(_h, mysql_sql_buf + off, sql_buffer_size - off, _k, _v, _n, db_mysql_val2str);
        if (ret < 0) return -1;
        off += ret;
        
-       sql_str.s = sql_buf;
+       sql_str.s = mysql_sql_buf;
        sql_str.len = off;
  
        if (db_mysql_submit_query(_h, &sql_str) < 0) {
@@ -587,14 +587,14 @@ int db_mysql_use_table(db1_con_t* _h, const str* _t)
  */
 int db_mysql_alloc_buffer(void)
 {
-    if (db_query_init())
+    if (db_api_init())
     {
-        LM_ERR("Failed to initialise db_query\n");
+        LM_ERR("Failed to initialise db api\n");
                return -1;
     }
 
-    sql_buf = (char*)malloc(sql_buffer_size);
-    if (sql_buf == NULL)
+    mysql_sql_buf = (char*)malloc(sql_buffer_size);
+    if (mysql_sql_buf == NULL)
         return -1;
     else
         return 0;
index 68e4e8e..a2b5f92 100644 (file)
@@ -139,11 +139,4 @@ int db_mysql_insert_delayed(const db1_con_t* _h, const db_key_t* _k,
 int db_mysql_use_table(db1_con_t* _h, const str* _t);
 
 
-/**
- * Allocate a buffer for database module
- * No function should be called before this
- * \return zero on success, negative value on failure
- */
-int db_mysql_alloc_buffer(void);
-
 #endif /* KM_DBASE_H */
index 127ad52..74c61af 100644 (file)
@@ -124,6 +124,13 @@ struct module_exports exports = {
 };
 
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_mysql_alloc_buffer()<0)
+               return -1;
+       return 0;
+}
+
 static int mysql_mod_init(void)
 {
 #if MYSQL_VERSION_ID >= 40101
index 73da2ae..9b0bd8b 100644 (file)
@@ -73,7 +73,7 @@ struct kam_module_exports kam_exports = {
 
 int km_postgres_mod_init(void)
 {
-       return db_query_init();
+       return 0;
 }
 
 int db_postgres_bind_api(db_func_t *dbb)
index 3c1e2d9..c560c9d 100644 (file)
@@ -530,6 +530,13 @@ int pg_test(void)
 }
 #endif /* PG_TEST */
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 static int pg_mod_init(void)
 {
 #ifdef PG_TEST
index ad30b57..d678e65 100644 (file)
@@ -10,7 +10,7 @@ Daniel-Constantin Mierla
 
    <miconda@gmail.com>
 
-   Copyright Â© 2011 asipto.com
+   Copyright © 2011 asipto.com
      __________________________________________________________________
 
    Table of Contents
@@ -25,7 +25,7 @@ Daniel-Constantin Mierla
 
         3. Parameters
 
-              3.1. server (int)
+              3.1. server (str)
 
         4. Functions
 
@@ -48,7 +48,7 @@ Chapter 1. Admin Guide
 
    3. Parameters
 
-        3.1. server (int)
+        3.1. server (str)
 
    4. Functions
 
@@ -81,9 +81,9 @@ Chapter 1. Admin Guide
 
 3. Parameters
 
-   3.1. server (int)
+   3.1. server (str)
 
-3.1. server (int)
+3.1. server (str)
 
    Specify the details to connect to REDIS server. It takes a list of
    attribute=value separated by semicolon, the attributes can be name,
@@ -92,18 +92,23 @@ Chapter 1. Admin Guide
    REDIS server. db is the DB number to use (defaults to 0 if not
    specified).
 
+   You can set this parameter many times, in case you want to connect to
+   many REDIS servers, just give different attributes and use the specific
+   server name when querying the REDIS instance.
+
    Default value is NULL.
 
    Example 1.1. Set server parameter
 ...
 modparam("ndb_redis", "server", "name=srvN;addr=127.0.0.1;port=6379;db=1")
+modparam("ndb_redis", "server", "name=srvX;addr=127.0.0.2;port=6379;db=4")
 ...
 
 4. Functions
 
    4.1. redis_cmd(srvname, command, replyid)
 
-4.1.  redis_cmd(srvname, command, replyid)
+4.1. redis_cmd(srvname, command, replyid)
 
    Send a command to REDIS server identified by srvname. The reply will be
    stored in a local continer identified by replyid. All the parameters
index c6bfb8f..02e3ac7 100644 (file)
        <section>
        <title>Parameters</title>
        <section>
-               <title><varname>server</varname> (int)</title>
+               <title><varname>server</varname> (str)</title>
                <para>
                        Specify the details to connect to REDIS server. It takes a list of
                        attribute=value separated by semicolon, the attributes can be
                        name, addr, port and db. Name is a generic identifier to be used with
                        module functions. addr and port are the IP address and the port to
-                       connect to REDIS server. db is the DB number to use (defaults to 0 if not specified).
+                       connect to REDIS server. db is the DB number to use (defaults to 0 if
+                       not specified).
+               </para>
+               <para>
+                       You can set this parameter many times, in case you want to connect to
+                       many REDIS servers, just give different attributes and use the specific
+                       server name when querying the REDIS instance.
                </para>
                <para>
                <emphasis>
@@ -78,6 +84,7 @@
                <programlisting format="linespecific">
 ...
 modparam("ndb_redis", "server", "name=srvN;addr=127.0.0.1;port=6379;db=1")
+modparam("ndb_redis", "server", "name=srvX;addr=127.0.0.2;port=6379;db=4")
 ...
 </programlisting>
                </example>
index 178c705..a360b88 100644 (file)
@@ -137,17 +137,74 @@ int sdpops_get_ids_by_name(str *name, str *ids)
        return -1;
 }
 
+/**
+ * get codec IDs from a= lines based on name
+ */
+int sdpops_sdp_get_ids_by_name(sdp_info_t *sdp, str *cname, str *cids, int n)
+{
+       int sdp_session_num;
+       int sdp_stream_num;
+       sdp_session_cell_t *sdp_session;
+       sdp_stream_cell_t *sdp_stream;
+       sdp_payload_attr_t *sdp_payload;
+       int i;
+
+       sdp_session_num = 0;
+       i = 0;
+       for(;;)
+       {
+               sdp_session = get_sdp_session_sdp(sdp, sdp_session_num);
+               if(!sdp_session) break;
+               sdp_stream_num = 0;
+               for(;;)
+               {
+                       sdp_stream = get_sdp_stream_sdp(sdp, sdp_session_num,
+                                                       sdp_stream_num);
+                       if(!sdp_stream) break;
+                       sdp_payload = sdp_stream->payload_attr;
+                       while (sdp_payload) {
+                               if(sdp_payload->rtp_enc.len==cname->len
+                                               && strncasecmp(cname->s, sdp_payload->rtp_enc.s,
+                                                               cname->len)==0)
+                               {
+                                       if(i==n)
+                                               goto notfound;
+                                       cids[i] = sdp_payload->rtp_payload;
+                                       i++;
+                               }
+
+                               sdp_payload=sdp_payload->next;
+                       }
+                       sdp_stream_num++;
+               }
+               sdp_session_num++;
+       }
+
+       if(i==0)
+               goto notfound;
+       if(i<n)
+               cids[i].s = NULL;
+       return 0;
+
+notfound:
+       cids[0].s = NULL;
+       cids[0].len = 0;
+       return -1;
+}
+
 /**
  * build the csv list of ids from csv list of names
  */
-int sdpops_build_ids_list(str *names, str *ids)
+int sdpops_build_ids_list(sdp_info_t *sdp, str *names, str *ids)
 {
 #define SDPOPS_MAX_LIST_SIZE   64
        static char _local_idslist[SDPOPS_MAX_LIST_SIZE];
        str tmp;
        str codec;
-       str cids;
+#define SDPOPS_CIDS_SIZE       8
+       str cids[SDPOPS_CIDS_SIZE];
        char *p;
+       int i;
 
        tmp = *names;
        ids->len = 0;
@@ -159,23 +216,34 @@ int sdpops_build_ids_list(str *names, str *ids)
                tmp.len -= (int)(&codec.s[codec.len]-codec.s);
                tmp.s = codec.s + codec.len;
 
-               if( sdpops_get_ids_by_name(&codec, &cids)==0) {
-                       LM_DBG("codecs list [%.*s] - at name [%.*s] with ids [%.*s]\n",
+               cids[0].s = NULL;
+               if(sdpops_get_ids_by_name(&codec, &cids[0])==0) {
+                       LM_DBG("codecs list [%.*s] - at name [%.*s] with list ids [%.*s]\n",
                                names->len, names->s,
                                codec.len, codec.s,
-                               cids.len,  cids.s);
-                       if(ids->len + cids.len>=SDPOPS_MAX_LIST_SIZE)
+                               cids[0].len,  cids[0].s);
+                       cids[1].s = NULL;
+               } else {
+                       if(sdpops_sdp_get_ids_by_name(sdp, &codec, cids, SDPOPS_CIDS_SIZE)==0) {
+                               LM_DBG("codecs list [%.*s] - at name [%.*s] with first sdp id [%.*s]\n",
+                                       names->len, names->s,
+                                       codec.len, codec.s,
+                                       cids[0].len,  cids[0].s);
+                       }
+               }
+               for(i=0; i<SDPOPS_CIDS_SIZE && cids[i].s!=NULL; i++) {
+                       if(ids->len + cids[i].len>=SDPOPS_MAX_LIST_SIZE)
                        {
                                LM_ERR("the list with codecs ids is too big\n");
                                ids->len = 0;
                                ids->s = 0;
                                return -1;
                        }
-                       strncpy(p, cids.s, cids.len);
-                       p += cids.len;
+                       strncpy(p, cids[i].s, cids[i].len);
+                       p += cids[i].len;
                        *p = ',';
                        p++;
-                       ids->len += cids.len + 1;
+                       ids->len += cids[i].len + 1;
                }
        }
        if(ids->len>0)
index 2d05f2b..3112ac2 100644 (file)
 
 #ifndef _SDPOPS_DATA_H_
 #define _SDPOPS_DATA_H_
+
 #include "../../str.h"
+#include "../../parser/sdp/sdp.h"
 
 int sdpops_get_ids_by_name(str *name, str *ids);
 int str_find_token(str *text, str *result, char delim);
-int sdpops_build_ids_list(str *names, str *ids);
+int sdpops_build_ids_list(sdp_info_t *sdp, str *names, str *ids);
 
 #endif
index 732acf2..cfd4ae0 100644 (file)
@@ -200,7 +200,7 @@ int sdp_codec_in_str(str *allcodecs, str* codec, char delim)
                                }
                        }
                }
-               if(allcodecs->s[i]==' ')
+               if(allcodecs->s[i]==delim)
                        cmp = 1;
                else
                        cmp = 0;
@@ -276,11 +276,16 @@ int sdp_remove_codecs_by_id(sip_msg_t* msg, str* codecs)
                return -1;
        }
 
-       LM_ERR("attempting to remove codecs from sdp: [%.*s]\n",
-                       codecs->len, codecs->s);
-
        sdp = (sdp_info_t*)msg->body;
 
+       if(sdp==NULL) {
+               LM_DBG("No sdp body\n");
+               return -1;
+       }
+
+       LM_DBG("attempting to remove codecs from sdp: [%.*s]\n",
+                       codecs->len, codecs->s);
+
        sdp_session_num = 0;
        for(;;)
        {
@@ -346,6 +351,7 @@ static int w_sdp_remove_codecs_by_id(sip_msg_t* msg, char* codecs, char* bar)
  */
 int sdp_remove_codecs_by_name(sip_msg_t* msg, str* codecs)
 {
+       sdp_info_t *sdp = NULL;
        str idslist;
 
        if(parse_sdp(msg) < 0) {
@@ -353,10 +359,17 @@ int sdp_remove_codecs_by_name(sip_msg_t* msg, str* codecs)
                return -1;
        }
 
-       LM_ERR("attempting to remove codecs from sdp: [%.*s]\n",
+       sdp = (sdp_info_t*)msg->body;
+
+       if(sdp==NULL) {
+               LM_DBG("No sdp body\n");
+               return -1;
+       }
+
+       LM_DBG("attempting to remove codecs from sdp: [%.*s]\n",
                        codecs->len, codecs->s);
 
-       if(sdpops_build_ids_list(codecs, &idslist)<0)
+       if(sdpops_build_ids_list(sdp, codecs, &idslist)<0)
                return -1;
 
        if(sdp_remove_codecs_by_id(msg, &idslist)<0)
@@ -409,11 +422,16 @@ int sdp_keep_codecs_by_id(sip_msg_t* msg, str* codecs)
                return -1;
        }
 
-       LM_ERR("attempting to keep codecs in sdp: [%.*s]\n",
-                       codecs->len, codecs->s);
-
        sdp = (sdp_info_t*)msg->body;
 
+       if(sdp==NULL) {
+               LM_DBG("No sdp body\n");
+               return -1;
+       }
+
+       LM_DBG("attempting to keep codecs in sdp: [%.*s]\n",
+                       codecs->len, codecs->s);
+
        sdp_session_num = 0;
        for(;;)
        {
@@ -481,6 +499,7 @@ static int w_sdp_keep_codecs_by_id(sip_msg_t* msg, char* codecs, char* bar)
  */
 int sdp_keep_codecs_by_name(sip_msg_t* msg, str* codecs)
 {
+       sdp_info_t *sdp = NULL;
        str idslist;
 
        if(parse_sdp(msg) < 0) {
@@ -488,10 +507,17 @@ int sdp_keep_codecs_by_name(sip_msg_t* msg, str* codecs)
                return -1;
        }
 
-       LM_ERR("attempting to keep codecs in sdp: [%.*s]\n",
+       sdp = (sdp_info_t*)msg->body;
+
+       if(sdp==NULL) {
+               LM_DBG("No sdp body\n");
+               return -1;
+       }
+
+       LM_DBG("attempting to keep codecs in sdp: [%.*s]\n",
                        codecs->len, codecs->s);
 
-       if(sdpops_build_ids_list(codecs, &idslist)<0)
+       if(sdpops_build_ids_list(sdp, codecs, &idslist)<0)
                return -1;
 
        if(sdp_keep_codecs_by_id(msg, &idslist)<0)
diff --git a/modules/xhttp_rpc/Makefile b/modules/xhttp_rpc/Makefile
new file mode 100644 (file)
index 0000000..eeb7897
--- /dev/null
@@ -0,0 +1,14 @@
+# $Id$
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=xhttp_rpc.so
+DEFS +=
+LIBS +=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules
diff --git a/modules/xhttp_rpc/README b/modules/xhttp_rpc/README
new file mode 100644 (file)
index 0000000..b8328e6
--- /dev/null
@@ -0,0 +1,165 @@
+xHTTP_RPC Module
+
+Ovidiu Sas
+
+   <osas@voipembedded.com>
+
+Edited by
+
+Ovidiu Sas
+
+   <osas@voipembedded.com>
+
+   Copyright © 2011 VoIPEmbedded Inc.
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+
+              1.1. Limitations
+
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. xhttp_rpc_root (str)
+              3.2. xhttp_rpc_buf_size (str)
+
+        4. Functions
+
+              4.1. dispatch_xhttp_rpc()
+
+   List of Examples
+
+   1.1. Set xhttp_rpc_root parameter
+   1.2. Set xhttp_rpc_buf_size parameter
+   1.3. dispatch_xhttp_rpc usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+
+        1.1. Limitations
+
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. xhttp_rpc_root (str)
+        3.2. xhttp_rpc_buf_size (str)
+
+   4. Functions
+
+        4.1. dispatch_xhttp_rpc()
+
+1. Overview
+
+   1.1. Limitations
+
+   This module provides an HTTP transport layer implementation for the RPC
+   management interface in a human easy readable format.
+
+   The xHTTP_RPC module uses the xHTTP module to handle HTTP requests.
+   Read the documentation of the xHTTP module for more details.
+
+1.1. Limitations
+
+   This module does not implement asynchronous RPC commands. It is
+   unlikely that asynchronous RPC commands will be executed from an RPC
+   web interface.
+
+   This module does not accept parameters embedded in a structure (see RPC
+   documentation for more info about how parameters can be passed to RPC).
+
+   At startup, all RPC commands are sorted and grouped based on their
+   format. The expected format is [group].[subcommand]. The initial
+   xhttp_rpc webpage displays all the retrieved groups. All RPC commands
+   are disponible as submenus of each [group]. If an RPC command is not in
+   the expected format, it will be dropped from the initial xhttp_rpc home
+   page menu.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * xhttp - xHTTP.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None
+
+3. Parameters
+
+   3.1. xhttp_rpc_root (str)
+   3.2. xhttp_rpc_buf_size (str)
+
+3.1. xhttp_rpc_root (str)
+
+   It specifies the root path for RPC http requests. The link to the RPC
+   web interface must be constructed using the following patern:
+   http://[server_IP]:[tcp_port]/[xhttp_rpc_root]
+
+   Default value is "rpc".
+
+   Example 1.1. Set xhttp_rpc_root parameter
+...
+modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc")
+...
+
+3.2. xhttp_rpc_buf_size (str)
+
+   It specifies the maximum length of the buffer used to write in the RPC
+   reply information in order to build the html response.
+
+   Default value is 0 (auto set to 1/3 of the size of the configured pkg
+   mem).
+
+   Example 1.2. Set xhttp_rpc_buf_size parameter
+...
+modparam("xhttp", "xhttp_rpc_buf_size", )
+...
+
+4. Functions
+
+   4.1. dispatch_xhttp_rpc()
+
+4.1.  dispatch_xhttp_rpc()
+
+   Handle the HTTP request and generate back a response.
+
+   Example 1.3. dispatch_xhttp_rpc usage
+...
+tcp_accept_no_cl=yes
+...
+loadmodule "sl.so"
+loadmodule "xhttp.so"
+loadmodule "xhttp_rpc.so"
+...
+modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc")
+...
+event_route[xhttp:request] {
+        $var(xhttp_rpc_root) = $(hu{s.substr,0,9});
+        if ($var(xhttp_rpc_root) == "/http_rpc")
+                dispatch_xhttp_rpc();
+        else
+                xhttp_reply("200", "OK", "text/html",
+                        "<html><body>Wrong URL $hu</body></html>");
+}
+...
diff --git a/modules/xhttp_rpc/doc/Makefile b/modules/xhttp_rpc/doc/Makefile
new file mode 100644 (file)
index 0000000..a47dae3
--- /dev/null
@@ -0,0 +1,4 @@
+docs = xhttp_rpc.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module
diff --git a/modules/xhttp_rpc/doc/xhttp_rpc.xml b/modules/xhttp_rpc/doc/xhttp_rpc.xml
new file mode 100644 (file)
index 0000000..4332606
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+       <title>xHTTP_RPC Module</title>
+       <productname class="trade">sip-router.org</productname>
+       <authorgroup>
+           <author>
+               <firstname>Ovidiu</firstname>
+               <surname>Sas</surname>
+               <email>osas@voipembedded.com</email>
+           </author>
+           <editor>
+               <firstname>Ovidiu</firstname>
+               <surname>Sas</surname>
+               <email>osas@voipembedded.com</email>
+           </editor>
+       </authorgroup>
+       <copyright>
+           <year>2011</year>
+           <holder><ulink url='http://www.voipembedded.com'>VoIPEmbedded Inc.</ulink></holder>
+       </copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="xhttp_rpc_admin.xml"/>
+    
+    
+</book>
diff --git a/modules/xhttp_rpc/doc/xhttp_rpc_admin.xml b/modules/xhttp_rpc/doc/xhttp_rpc_admin.xml
new file mode 100644 (file)
index 0000000..2929340
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+       
+       <title>&adminguide;</title>
+       
+       <section>
+       <title>Overview</title>
+       <para>
+               This module provides an HTTP transport layer implementation for
+               the RPC management interface in a human easy readable format.
+       <para>
+       </para>
+               The xHTTP_RPC module uses the xHTTP module to handle HTTP requests.
+               Read the documentation of the xHTTP module for more details.
+       </para>
+
+       <section>
+       <title>Limitations</title>
+       <para>
+               This module does not implement asynchronous RPC commands.
+               It is unlikely that asynchronous RPC commands will be executed
+               from an RPC web interface.
+       </para>
+       <para>
+               This module does not accept parameters embedded in a structure
+               (see RPC documentation for more info about how parameters can be
+               passed to RPC).
+       </para>
+       <para>
+               At startup, all RPC commands are sorted and grouped based on
+               their format.  The expected format is [group].[subcommand].
+               The initial xhttp_rpc webpage displays all the retrieved groups.
+               All RPC commands are disponible as submenus of each [group].
+               If an RPC command is not in the expected format, it will be
+               dropped from the initial xhttp_rpc home page menu.
+       </para>
+       </section>
+       </section>
+
+       <section>
+       <title>Dependencies</title>
+       <section>
+               <title>&kamailio; Modules</title>
+               <para>
+               The following modules must be loaded before this module:
+                       <itemizedlist>
+                       <listitem>
+                       <para>
+                               <emphasis>xhttp</emphasis> - xHTTP.
+                       </para>
+                       </listitem>
+                       </itemizedlist>
+               </para>
+       </section>
+       <section>
+               <title>External Libraries or Applications</title>
+               <para>
+               The following libraries or applications must be installed before running
+               &kamailio; with this module loaded:
+                       <itemizedlist>
+                       <listitem>
+                       <para>
+                               <emphasis>None</emphasis>
+                       </para>
+                       </listitem>
+                       </itemizedlist>
+               </para>
+       </section>
+       </section>
+       <section>
+       <title>Parameters</title>
+       <section>
+               <title><varname>xhttp_rpc_root</varname> (str)</title>
+               <para>
+                       It specifies the root path for RPC http requests.
+                       The link to the RPC web interface must be constructed
+                       using the following patern:
+                       http://[server_IP]:[tcp_port]/[xhttp_rpc_root] 
+               </para>
+               <para>
+               <emphasis>
+                       Default value is "rpc".
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>xhttp_rpc_root</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc")
+...
+</programlisting>
+               </example>
+       </section>
+       <section>
+               <title><varname>xhttp_rpc_buf_size</varname> (str)</title>
+               <para>
+                       It specifies the maximum length of the buffer used
+                       to write in the RPC reply information in order to
+                       build the html response.
+               </para>
+               <para>
+               <emphasis>
+                       Default value is 0 (auto set to 1/3 of the size of the configured pkg mem).
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>xhttp_rpc_buf_size</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("xhttp", "xhttp_rpc_buf_size", )
+...
+</programlisting>
+               </example>
+       </section>
+       </section>
+
+       <section>
+       <title>Functions</title>
+       <section>
+           <title>
+               <function moreinfo="none">dispatch_xhttp_rpc()</function>
+           </title>
+           <para>
+               Handle the HTTP request and generate back a response.
+           </para>
+               <example>
+               <title><function>dispatch_xhttp_rpc</function> usage</title>
+               <programlisting format="linespecific">
+...
+tcp_accept_no_cl=yes
+...
+loadmodule "sl.so"
+loadmodule "xhttp.so"
+loadmodule "xhttp_rpc.so"
+...
+modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc")
+...
+event_route[xhttp:request] {
+       $var(xhttp_rpc_root) = $(hu{s.substr,0,9});
+       if ($var(xhttp_rpc_root) == "/http_rpc")
+               dispatch_xhttp_rpc();
+       else
+               xhttp_reply("200", "OK", "text/html",
+                       "&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
+}
+...
+</programlisting>
+           </example>
+       </section>
+       </section>
+</chapter>
+
diff --git a/modules/xhttp_rpc/xhttp_rpc.c b/modules/xhttp_rpc/xhttp_rpc.c
new file mode 100644 (file)
index 0000000..b251409
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2011 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2011-11-11  initial version (osas)
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "../../ver.h"
+#include "../../trim.h"
+#include "../../sr_module.h"
+#include "../../nonsip_hooks.h"
+#include "../../modules/xhttp/api.h"
+#include "xhttp_rpc.h"
+#include "xhttp_rpc_fnc.h"
+
+/** @addtogroup xhttp_rpc
+ * @ingroup modules
+ * @{
+ *
+ * <h1>Overview of Operation</h1>
+ * This module provides a web interface for RPC management interface.
+ * It is built on top of the xhttp API module.
+ */
+
+/** @file
+ *
+ * This is the main file of xhttp_rpc module which contains all the functions
+ * related to http processing, as well as the module interface.
+ */
+
+MODULE_VERSION
+
+str XHTTP_RPC_REASON_OK = str_init("OK");
+str XHTTP_RPC_CONTENT_TYPE_TEXT_HTML = str_init("text/html");
+
+
+xhttp_rpc_mod_cmds_t *xhttp_rpc_mod_cmds = NULL;
+int xhttp_rpc_mod_cmds_size = 0;
+
+/* FIXME: this should be initialized in ../../ver.c */
+int full_version_len;
+int ver_name_len;
+
+static int mod_init(void);
+static int child_init(int rank);
+static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2);
+
+
+/** The context of the xhttp_rpc request being processed.
+ *
+ * This is a global variable that records the context of the xhttp_rpc request
+ * being currently processed.
+ * @sa rpc_ctx
+ */
+static rpc_ctx_t ctx;
+
+static xhttp_api_t xhttp_api;
+
+/** Pointers to the functions that implement the RPC interface
+ * of xhttp_rpc module
+ */
+static rpc_t func_param;
+
+str xhttp_rpc_root = str_init("rpc");
+int buf_size = 0;
+char error_buf[ERROR_REASON_BUF_LEN];
+
+static cmd_export_t cmds[] = {
+       {"dispatch_xhttp_rpc",(cmd_function)xhttp_rpc_dispatch,0,0,0,REQUEST_ROUTE},
+       {0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+       {"xhttp_rpc_root",      STR_PARAM,      &xhttp_rpc_root.s},
+       {"xhttp_rpc_buf_size",  INT_PARAM,      &buf_size},
+       {0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+       "xhttp_rpc",
+       DEFAULT_DLFLAGS, /* dlopen flags */
+       cmds,
+       params,
+       0,              /* exported statistics */
+       0,              /* exported MI functions */
+       0,              /* exported pseudo-variables */
+       0,              /* extra processes */
+       mod_init,       /* module initialization function */
+       0,
+       0,
+       child_init      /* per-child init function */
+};
+
+
+/** Implementation of rpc_fault function required by the management API.
+ *
+ * This function will be called whenever a management function
+ * indicates that an error ocurred while it was processing the request. The
+ * function takes the reply code and reason phrase as parameters, these will
+ * be put in the body of the reply.
+ *
+ * @param ctx A pointer to the context structure of the request being
+ *            processed.
+ * @param code Reason code.
+ * @param fmt Formatting string used to build the reason phrase.
+ */
+static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
+{
+       va_list ap;
+       struct xhttp_rpc_reply *reply = &ctx->reply;
+
+       reply->code = code;
+       va_start(ap, fmt);
+       vsnprintf(error_buf, ERROR_REASON_BUF_LEN, fmt, ap);
+       va_end(ap);
+       reply->reason.len = strlen(error_buf);
+       reply->reason.s = error_buf;
+       /* reset body so we can print the error */
+       reply->body.len = 0;
+
+       return;
+}
+
+
+/**
+ */
+static void free_data_struct(struct rpc_data_struct *rpc_d)
+{
+       struct rpc_data_struct *ds;
+
+       if (!rpc_d) {
+               LM_ERR("Atempting to free NULL rpc_data_struct\n");
+               return;
+       }
+       while (rpc_d) {
+               ds = rpc_d->next;
+               pkg_free(rpc_d);
+               rpc_d = ds;
+       }
+       return;
+}
+
+
+/**
+ */
+static struct rpc_data_struct *new_data_struct(rpc_ctx_t* ctx)
+{
+       struct rpc_data_struct *ds;
+
+       if (!ctx) return NULL;
+       ds = (struct rpc_data_struct*)pkg_malloc(sizeof(struct rpc_data_struct));
+       if (!ds) {
+               rpc_fault(ctx, 500, "Internal Server Error (oom)");
+               return NULL;
+       }
+       memset(ds, 0, sizeof(struct rpc_data_struct));
+       ds->ctx = ctx;
+
+       return ds;
+}
+
+
+/** Initialize xhttp_rpc reply data structure.
+ *
+ * This function initializes the data structure that contains all data related
+ * to the xhttp_rpc reply being created. The function must be called before any
+ * other function that adds data to the reply.
+ * @param ctx rpc_ctx_t structure to be initialized.
+ * @return 0 on success, a negative number on error.
+ */
+static int init_xhttp_rpc_reply(rpc_ctx_t *ctx)
+{
+       struct xhttp_rpc_reply *reply = &ctx->reply;
+
+       reply->code = 200;
+       reply->reason = XHTTP_RPC_REASON_OK;
+       reply->buf.s = pkg_malloc(buf_size);
+       if (!reply->buf.s) {
+               LM_ERR("oom\n");
+               rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
+               return -1;
+       }
+       reply->buf.len = buf_size;
+       reply->body.s = reply->buf.s;
+       reply->body.len = 0;
+       return 0;
+}
+
+
+/** Implementation of rpc_send function required by the management API.
+ *
+ * This is the function that will be called whenever a management function
+ * asks the management interface to send the reply to the client.
+ * The SIP/HTTP reply sent to
+ * the client will be always 200 OK, if an error ocurred on the server then it
+ * will be indicated in the html document in body.
+ *
+ * @param ctx A pointer to the context structure of the xhttp_rpc request that
+ *            generated the reply.
+ * @return 1 if the reply was already sent, 0 on success, a negative number on
+ *            error
+ */
+static int rpc_send(rpc_ctx_t* ctx)
+{
+       struct xhttp_rpc_reply* reply;
+
+       if (ctx->reply_sent) return 1;
+
+       reply = &ctx->reply;
+
+       if (0!=xhttp_rpc_build_page(ctx)){
+               rpc_fault(ctx, 500, "Internal Server Error");
+       }
+
+       ctx->reply_sent = 1;
+       if (reply->body.len)
+               xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
+                       &XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->body);
+       else
+               xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
+                       &XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->reason);
+
+       if (reply->buf.s) {
+               pkg_free(reply->buf.s);
+               reply->buf.s = NULL;
+               reply->buf.len = 0;
+       }
+       if (ctx->arg.s) {
+               pkg_free(ctx->arg.s);
+               ctx->arg.s = NULL;
+               ctx->arg.len = 0;
+       }
+       if (ctx->data_structs) {
+               free_data_struct(ctx->data_structs);
+               ctx->data_structs = NULL;
+       }
+
+       return 0;
+}
+
+
+/** Converts the variables provided in parameter ap according to formatting
+ * string provided in parameter fmt into HTML format.
+ *
+ * This function takes the parameters provided in ap parameter and creates
+ * HTML formatted parameters that will be put in the html document.
+ * The format of input parameters is described in formatting string
+ * fmt which follows the syntax of the management API. In the case of
+ * an error the function will generate an error reply in err_reply parameter
+ * instead.
+ * @param ctx An error reply document will be generated here if the
+ *                  function encounters a problem while processing input
+ *                  parameters.
+ * @param fmt Formatting string of the management API.
+ * @param ap A pointer to the array of input parameters.
+ *
+ */
+static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap, str *id)
+
+{
+       str body;
+       str *sp;
+       char buf[PRINT_VALUE_BUF_LEN];
+       time_t dt;
+       struct tm* t;
+
+       switch(fmt) {
+       case 'd':
+               body.s = sint2str(va_arg(*ap, int), &body.len);
+               break;
+       case 'f':
+               body.s = buf;
+               body.len = snprintf(buf, PRINT_VALUE_BUF_LEN,
+                               "%f", va_arg(*ap, double));
+               if (body.len < 0) {
+                       LM_ERR("Error while converting double\n");
+                       return -1;
+               }
+               break;
+       case 'b':
+               body.len = 1;
+               body.s = ((va_arg(*ap, int)==0)?"0":"1");
+               break;
+       case 't':
+               body.s = buf;
+               body.len = sizeof("19980717T14:08:55") - 1;
+               dt = va_arg(*ap, time_t);
+               t = gmtime(&dt);
+               if (strftime(buf, PRINT_VALUE_BUF_LEN,
+                               "%Y%m%dT%H:%M:%S", t) == 0) {
+                       LM_ERR("Error while converting time\n");
+                       return -1;
+               }
+               break;
+       case 's':
+               body.s = va_arg(*ap, char*);
+               body.len = strlen(body.s);
+               break;
+       case 'S':
+               sp = va_arg(*ap, str*);
+               body = *sp;
+               break;
+       default:
+               body.len = 0;
+               body.s = NULL;
+               LM_ERR("Invalid formatting character [%c]\n", fmt);
+               return -1;
+       }
+       if (0!=xhttp_rpc_build_content(ctx, &body, id)) {
+               rpc_fault(ctx, 500, "Internal Server Error");
+               return -1;
+       }
+       return 0;
+}
+
+
+/** Implementation of rpc_add function required by the management API.
+ *
+ * This function will be called when an RPC management function calls
+ * rpc->add to add a parameter to the xhttp_rpc reply being generated.
+ */
+static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
+{
+       void **void_ptr;
+       struct rpc_data_struct *ds;
+       va_list ap;
+
+       if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
+               rpc_fault(ctx, 500, "Internal Server Error");
+               return -1;
+       }
+       va_start(ap, fmt);
+       while(*fmt) {
+               if (*fmt == '{') {
+                       void_ptr = va_arg(ap, void**);
+                       ds = new_data_struct(ctx);
+                       if (!ds) goto err;
+                       if (ctx->data_structs) free_data_struct(ctx->data_structs);
+                       ctx->data_structs = ds;
+                       *void_ptr = ds;
+               } else {
+                       if (print_value(ctx, *fmt, &ap, NULL) < 0) goto err;
+               }
+               fmt++;
+       }
+       va_end(ap);
+       return 0;
+err:
+       va_end(ap);
+       return -1;
+}
+
+
+/** Implementation of rpc->scan function required by the management API.
+ *
+ * This is the function that will be called whenever a management function
+ * calls rpc->scan to get the value of parameter from the xhttp_rpc
+ * request. This function will extract the current parameter from the xhttp_rpc
+ * URL and attempts to convert it to the type requested by the management
+ * function that called it.
+ */
+static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
+{
+       int *int_ptr;
+       char **char_ptr;
+       double *double_ptr;
+       str *str_ptr;
+
+       str arg;
+
+       int mandatory_param = 1;
+       int modifiers = 0;
+       char* orig_fmt;
+       va_list ap;
+
+       orig_fmt=fmt;
+       va_start(ap, fmt);
+       while(*fmt) {
+               switch(*fmt) {
+               case '*': /* start of optional parameters */
+                       mandatory_param = 0;
+                       modifiers++;
+                       fmt++;
+                       continue;
+                       break;
+               case '.': /* autoconvert */
+                       modifiers++;
+                       fmt++;
+                       continue;
+                       break;
+               case 'b': /* Bool */
+               case 't': /* Date and time */
+               case 'd': /* Integer */
+                       xhttp_rpc_get_next_arg(ctx, &arg);
+                       if (arg.len==0)
+                               goto read_error;
+                       int_ptr = va_arg(ap, int*);
+                       *int_ptr = strtol(arg.s, 0, 0);
+                       break;
+               case 'f': /* double */
+                       xhttp_rpc_get_next_arg(ctx, &arg);
+                       if (arg.len==0)
+                               goto read_error;
+                       double_ptr = va_arg(ap, double*);
+                       *double_ptr = strtod(arg.s, 0);
+                       break;
+               case 's': /* zero terminated string */
+                       xhttp_rpc_get_next_arg(ctx, &arg);
+                       if (arg.len==0)
+                               goto read_error;
+                       char_ptr = va_arg(ap, char**);
+                       *char_ptr = arg.s;
+                       break;
+               case 'S': /* str structure */
+                       xhttp_rpc_get_next_arg(ctx, &arg);
+                       if (arg.len==0)
+                               goto read_error;
+                       str_ptr = va_arg(ap, str*);
+                       *str_ptr = arg;
+                       break;
+               case '{':
+                       xhttp_rpc_get_next_arg(ctx, &arg);
+                       if (arg.len==0)
+                               goto read_error;
+                       LM_ERR("Unsupported param type [{]\n");
+                       rpc_fault(ctx, 500, "Unsupported param type [{]");
+                       goto error;
+                       break;
+               default:
+                       LM_ERR("Invalid param type in formatting string: [%c]\n", *fmt);
+                       rpc_fault(ctx, 500,
+                               "Internal Server Error (inval formatting str)");
+                       goto error;
+               }
+               fmt++;
+       }
+       va_end(ap);
+       return (int)(fmt-orig_fmt)-modifiers;
+read_error:
+       if (mandatory_param) rpc_fault(ctx, 400, "Invalid parameter value");
+error:
+       va_end(ap);
+       return -((int)(fmt-orig_fmt)-modifiers);
+}
+
+
+/** Implementation of rpc_printf function required by the management API.
+ *
+ * This function will be called whenever an RPC management function calls
+ * rpc-printf to add a parameter to the xhttp_rpc reply being constructed.
+ */
+static int rpc_printf(rpc_ctx_t* ctx, char* fmt, ...)
+{
+       int n, size;
+       char *p;
+       va_list ap;
+
+       if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
+               rpc_fault(ctx, 500, "Internal Server Error");
+               return -1;
+       }
+
+       p = ctx->reply.body.s + ctx->reply.body.len;
+       size = ctx->reply.buf.len - ctx->reply.body.len;
+       va_start(ap, fmt);
+       n = vsnprintf(p, size, fmt, ap);
+       va_end(ap);
+       if (n > -1 && n < size) {
+               ctx->reply.body.len += n;
+               p += n;
+       } else {
+               LM_ERR("oom\n");
+               rpc_fault(ctx, 500, "Internal Server Error (oom)");
+               return -1;
+       }
+       if (0!=xhttp_rpc_insert_break(ctx)) {
+               LM_ERR("oom\n");
+               rpc_fault(ctx, 500, "Internal Server Error (oom)");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/** Adds a new member to structure.
+ */
+static int rpc_struct_add(struct rpc_data_struct* rpc_s, char* fmt, ...)
+{
+       va_list ap;
+       void **void_ptr;
+       str member_name;
+       rpc_ctx_t *ctx = rpc_s->ctx;
+       struct rpc_data_struct *ds, *s;
+
+       if (!ctx) {
+               LM_ERR("Invalid context\n");
+               return -1;
+       }
+       if (!ctx->data_structs) {
+               LM_ERR("Invalid structs\n");
+               return -1;
+       }
+       s = ds = ctx->data_structs;
+       ctx->struc_depth = 0;
+       while (s) {
+               if (s == rpc_s) {
+                       if (s->next) {
+                               free_data_struct(s->next);
+                               s->next = NULL;
+                       }
+                       break;
+               }
+               ctx->struc_depth++;
+               ds = s;
+               s = s->next;
+       }
+       if (!s)
+               s = ds;
+       va_start(ap, fmt);
+       while(*fmt) {
+               member_name.s = va_arg(ap, char*);
+               member_name.len = (member_name.s?strlen(member_name.s):0);
+               if (*fmt == '{') {
+                       void_ptr = va_arg(ap, void**);
+                       ds = new_data_struct(ctx);
+                       if (!ds) goto err;
+                       s->next = ds;
+                       *void_ptr = ds;
+                       if (0!=xhttp_rpc_build_content(ctx, NULL, &member_name))
+                               goto err;
+               } else {
+                       if (print_value(ctx, *fmt, &ap, &member_name) < 0) goto err;
+               }
+               fmt++;
+       }
+       va_end(ap);
+       return 0;
+err:
+       va_end(ap);
+       return -1;
+}
+
+
+static int rpc_struct_scan(void* s, char* fmt, ...)
+{
+       LM_ERR("Not implemented\n");
+       return -1;
+}
+
+
+/** Create a new member from formatting string and add it to a structure.
+ */
+static int rpc_struct_printf(void* s, char* member_name, char* fmt, ...)
+{
+       LM_ERR("Not implemented\n");
+       return -1;
+}
+
+
+/** Returns the RPC capabilities supported by the xmlrpc driver.
+ */
+static rpc_capabilities_t rpc_capabilities(rpc_ctx_t* ctx)
+{
+       /* No support for async commands.
+        */
+       return 0;
+}
+
+
+/** Returns a new "delayed reply" context.
+ * Creates a new delayed reply context in shm and returns it.
+ * @return 0 - not supported, already replied, or no more memory;
+ *         !=0 pointer to the special delayed ctx.
+ * Note1: one should use the returned ctx reply context to build a reply and
+ *  when finished call rpc_delayed_ctx_close().
+ * Note2: adding pieces to the reply in different processes is not supported.
+ */
+static struct rpc_delayed_ctx* rpc_delayed_ctx_new(rpc_ctx_t* ctx)
+{
+       return NULL;
+}
+
+
+/** Closes a "delayed reply" context and sends the reply.
+ * If no reply has been sent the reply will be built and sent automatically.
+ * See the notes from rpc_new_delayed_ctx()
+ */
+static void rpc_delayed_ctx_close(struct rpc_delayed_ctx* dctx)
+{
+       return;
+}
+
+
+static int mod_init(void)
+{
+       int i;
+
+       /* bind the XHTTP API */
+       if (xhttp_load_api(&xhttp_api) < 0) {
+               LM_ERR("cannot bind to XHTTP API\n");
+               return -1;
+       }
+
+       /* Check xhttp_rpc_buf_size param */
+       if (buf_size == 0)
+               buf_size = pkg_mem_size/3;
+
+       /* Check xhttp_rpc_root param */
+       xhttp_rpc_root.len = strlen(xhttp_rpc_root.s);
+       for(i=0;i<xhttp_rpc_root.len;i++){
+               if ( !isalnum(xhttp_rpc_root.s[i]) && xhttp_rpc_root.s[i]!='_') {
+                       LM_ERR("bad xhttp_rpc_root param [%.*s], char [%c] "
+                               "- use only alphanumerical chars\n",
+                               xhttp_rpc_root.len, xhttp_rpc_root.s,
+                               xhttp_rpc_root.s[i]);
+                       return -1;
+               }
+       }
+
+       func_param.send = (rpc_send_f)rpc_send;
+       func_param.fault = (rpc_fault_f)rpc_fault;
+       func_param.add = (rpc_add_f)rpc_add;
+       func_param.scan = (rpc_scan_f)rpc_scan;
+       func_param.printf = (rpc_printf_f)rpc_printf;
+       func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
+       func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;
+       func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
+       func_param.capabilities = (rpc_capabilities_f)rpc_capabilities;
+       func_param.delayed_ctx_new = (rpc_delayed_ctx_new_f)rpc_delayed_ctx_new;
+       func_param.delayed_ctx_close =
+               (rpc_delayed_ctx_close_f)rpc_delayed_ctx_close;
+
+       return 0;
+}
+
+static int child_init(int rank)
+{
+       int i, j;
+       int len;
+       xhttp_rpc_mod_cmds_t *cmds;
+       /* rpc_export_t *rpc_e; */
+
+       if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
+               return 0; /* do nothing for the main process */
+
+       if (rank==PROC_INIT)
+       {
+               /* building a cache of rpc module commands */
+               xhttp_rpc_mod_cmds =
+                       (xhttp_rpc_mod_cmds_t*)pkg_malloc(sizeof(xhttp_rpc_mod_cmds_t));
+               if (xhttp_rpc_mod_cmds==NULL){
+                       LM_ERR("oom\n");
+                       return -1;
+               }
+               xhttp_rpc_mod_cmds->rpc_e_index = 0;
+               xhttp_rpc_mod_cmds->mod.s = NULL;
+               xhttp_rpc_mod_cmds->mod.len = 0;
+               xhttp_rpc_mod_cmds->size = 0;
+               xhttp_rpc_mod_cmds_size = 1;
+               cmds = xhttp_rpc_mod_cmds;
+               for(i=0; i<rpc_sarray_crt_size; i++){
+                       len = strlen(rpc_sarray[i]->name);
+                       j = 0;
+                       while (j<len && rpc_sarray[i]->name[j]!='.')
+                               j++;
+                       if (j==len) {
+                               LM_DBG("dropping invalid command format [%.*s]\n",
+                                               len, rpc_sarray[i]->name);
+                       } else {
+                               if (cmds->mod.len==0) {
+                                       /* this is the first module */
+                                       cmds->rpc_e_index = i;
+                                       cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
+                                       cmds->mod.len = j;
+                                       cmds->size++;
+                               } else if (cmds->mod.len==j &&
+                                       strncmp(cmds->mod.s,
+                                               (char*)&rpc_sarray[i]->name[0],
+                                               j)==0){
+                                       cmds->size++;
+                               } else {
+                                       cmds = (xhttp_rpc_mod_cmds_t*)
+                                               pkg_realloc(xhttp_rpc_mod_cmds,
+                                                       (xhttp_rpc_mod_cmds_size+1)*
+                                                       sizeof(xhttp_rpc_mod_cmds_t));
+                                       if (cmds==NULL){
+                                               LM_ERR("oom\n");
+                                               return -1;
+                                       }
+                                       xhttp_rpc_mod_cmds = cmds;
+                                       cmds = &xhttp_rpc_mod_cmds[xhttp_rpc_mod_cmds_size];
+                                       cmds->rpc_e_index = i;
+                                       cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
+                                       cmds->mod.len = j;
+                                       xhttp_rpc_mod_cmds_size++;
+                                       cmds->size = 1;
+                               }
+                       }
+               }
+               /*
+               for(i=0; i<xhttp_rpc_mod_cmds_size; i++){
+                       for (j=0; j<xhttp_rpc_mod_cmds[i].size; j++){
+                               rpc_e = rpc_sarray[xhttp_rpc_mod_cmds[i].rpc_e_index+j];
+                               LM_DBG("[%p] => [%p]->[%.*s] [%p]->[%s]\n",
+                                       rpc_e,
+                                       xhttp_rpc_mod_cmds[i].mod.s,
+                                       xhttp_rpc_mod_cmds[i].mod.len,
+                                       xhttp_rpc_mod_cmds[i].mod.s,
+                                       rpc_e->name,
+                                       rpc_e->name);
+                       }
+               }
+               */
+       }
+
+       full_version_len = strlen(full_version);
+       ver_name_len = strlen(ver_name);
+       return 0;
+}
+
+
+static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2)
+{
+       rpc_export_t* rpc_e;
+       str arg = {NULL, 0};
+       int ret = 0;
+       int i;
+
+       if(!IS_HTTP(msg)) {
+               LM_DBG("Got non HTTP msg\n");
+               return NONSIP_MSG_PASS;
+       }
+
+       /* Init xhttp_rpc context */
+       if (ctx.reply.buf.s) LM_ERR("Unexpected buf value [%p][%d]\n",
+                               ctx.reply.buf.s, ctx.reply.buf.len);
+       memset(&ctx, 0, sizeof(rpc_ctx_t));
+       ctx.msg = msg;
+       ctx.mod = ctx.cmd = -1;
+       if (init_xhttp_rpc_reply(&ctx) < 0) goto send_reply;
+
+       /* Extract arguments from url */
+       if (0!=xhttp_rpc_parse_url(&msg->first_line.u.request.uri,
+                               &ctx.mod, &ctx.cmd, &arg)){
+               rpc_fault(&ctx, 500, "Bad URL");
+               goto send_reply;
+       }
+
+       if (arg.s) {
+               if (arg.len) {
+                       /* Unescape args */
+                       ctx.arg.s = pkg_malloc((arg.len+1)*sizeof(char));
+                       if (ctx.arg.s==NULL){
+                               LM_ERR("oom\n");
+                               rpc_fault(&ctx, 500, "Internal Server Error (oom)");
+                               goto send_reply;
+                       }
+                       for(i=0;i<arg.len;i++) if (arg.s[i]=='+') arg.s[i]=' ';
+                       if (0>un_escape(&arg, &ctx.arg)) {
+                               LM_ERR("unable to escape [%.*s]\n", arg.len, arg.s);
+                               rpc_fault(&ctx, 500, "Bad arg in URL");
+                               goto send_reply;
+                       }
+                       ctx.arg.s[ctx.arg.len] = '\0';
+                       ctx.arg.len++;
+                       ctx.arg2scan = ctx.arg;
+               }
+               ctx.arg_received = 1;
+       } else {
+               goto send_reply;
+       }
+       
+       /*
+       rpc_e=find_rpc_export((char*)rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name, 0);
+       if ((rpc_e==NULL) || (rpc_e->function==NULL)){
+               LM_ERR("Unable to find rpc command [%s]\n",
+               rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name);
+               rpc_fault(&ctx, 500, "Method not found");
+               goto send_reply;
+       }
+       */
+       rpc_e=rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd];
+       rpc_e->function(&func_param, &ctx);
+
+send_reply:
+       if (!ctx.reply_sent) {
+               ret = rpc_send(&ctx);
+       }
+       if (ret < 0) return -1;
+       return 0;
+}
+
diff --git a/modules/xhttp_rpc/xhttp_rpc.h b/modules/xhttp_rpc/xhttp_rpc.h
new file mode 100644 (file)
index 0000000..6728aa5
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2011 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2011-11-11  initial version (osas)
+ */
+
+
+#ifndef _XHTTP_RPC_H
+#define _XHTTP_RPC_H
+
+#include "../../str.h"
+#include "../../rpc_lookup.h"
+#include "../../parser/msg_parser.h"
+
+
+#define ERROR_REASON_BUF_LEN 1024
+#define PRINT_VALUE_BUF_LEN 256
+
+
+
+struct rpc_data_struct {
+       struct rpc_ctx* ctx;
+       struct rpc_data_struct* next;
+};
+
+
+/** Representation of the xhttp_rpc reply being constructed.
+ *
+ * This data structure describes the xhttp_rpc reply that is being constructed
+ * and will be sent to the client.
+ */
+struct xhttp_rpc_reply {
+       int code;       /**< Reply code which indicates the type of the reply */
+       str reason;     /**< Reason phrase text which provides human-readable
+                        * description that augments the reply code */
+       str body;       /**< The xhttp_rpc http body built so far */
+       str buf;        /**< The memory buffer allocated for the reply, this is
+                        * where the body attribute of the structure points to */
+};
+
+
+/** The context of the xhttp_rpc request being processed.
+ *
+ * This is the data structure that contains all data related to the xhttp_rpc
+ * request being processed, such as the reply code and reason, data to be sent
+ * to the client in the reply, and so on.
+ *
+ * There is always one context per xhttp_rpc request.
+ */
+typedef struct rpc_ctx {
+       sip_msg_t* msg;                 /**< The SIP/HTTP received message. */
+       struct xhttp_rpc_reply reply;   /**< xhttp_rpc reply to be sent to the client */
+       int reply_sent;
+       int mod;                        /**< Module being processed */
+       int cmd;                        /**< RPC command being processed */
+       int arg_received;               /**< RPC argument flag */
+       str arg;                        /**< RPC command argument */
+       str arg2scan;                   /**< RPC command args to be parsed */
+       struct rpc_struct *structs;
+       struct rpc_data_struct *data_structs;
+       unsigned int struc_depth;
+} rpc_ctx_t;
+
+
+/* An RPC module representation.
+ *
+ * The module is the first substring of the RPC commands (delimited by '.'.
+ */
+typedef struct xhttp_rpc_mod_cmds_ {
+       int rpc_e_index;        /**< Index to the first module RPC rec in rpc_sarray */
+       str mod;                /**< Module name */
+       int size;               /**< Number of commands provided by the above module */
+} xhttp_rpc_mod_cmds_t;
+
+#endif
+
diff --git a/modules/xhttp_rpc/xhttp_rpc_fnc.c b/modules/xhttp_rpc/xhttp_rpc_fnc.c
new file mode 100644 (file)
index 0000000..5c9ffbd
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2011 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2011-11-11  initial version (osas)
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "../../trim.h"
+#include "../../ver.h"
+#include "../../rpc_lookup.h"
+#include "xhttp_rpc.h"
+
+extern str xhttp_rpc_root;
+extern int xhttp_rpc_mod_cmds_size;
+extern xhttp_rpc_mod_cmds_t *xhttp_rpc_mod_cmds;
+
+extern int ver_name_len;
+extern int full_version_len;
+
+#define XHTTP_RPC_COPY(p,str)  \
+do{    \
+       if ((int)((p)-buf)+(str).len>max_page_len) {    \
+               goto error;     \
+       }       \
+       memcpy((p), (str).s, (str).len); (p) += (str).len;      \
+}while(0)
+
+#define XHTTP_RPC_COPY_2(p,str1,str2)  \
+do{    \
+       if ((int)((p)-buf)+(str1).len+(str2).len>max_page_len) {        \
+               goto error;     \
+       }       \
+       memcpy((p), (str1).s, (str1).len); (p) += (str1).len;   \
+       memcpy((p), (str2).s, (str2).len); (p) += (str2).len;   \
+}while(0)
+
+#define XHTTP_RPC_COPY_3(p,str1,str2,str3)     \
+do{    \
+       if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len>max_page_len) {     \
+               goto error;     \
+       }       \
+       memcpy((p), (str1).s, (str1).len); (p) += (str1).len;   \
+       memcpy((p), (str2).s, (str2).len); (p) += (str2).len;   \
+       memcpy((p), (str3).s, (str3).len); (p) += (str3).len;   \
+}while(0)
+
+#define XHTTP_RPC_COPY_4(p,str1,str2,str3,str4)        \
+do{    \
+       if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len+(str4).len>max_page_len) {  \
+               goto error;     \
+       }       \
+       memcpy((p), (str1).s, (str1).len); (p) += (str1).len;   \
+       memcpy((p), (str2).s, (str2).len); (p) += (str2).len;   \
+       memcpy((p), (str3).s, (str3).len); (p) += (str3).len;   \
+       memcpy((p), (str4).s, (str4).len); (p) += (str4).len;   \
+}while(0)
+
+#define XHTTP_RPC_COPY_5(p,s1,s2,s3,s4,s5)     \
+do{    \
+       if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len>max_page_len) { \
+               goto error;     \
+       }       \
+       memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \
+       memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \
+       memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \
+       memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \
+       memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \
+}while(0)
+
+#define XHTTP_RPC_COPY_6(p,s1,s2,s3,s4,s5,s6)  \
+do{    \
+       if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len>max_page_len) {        \
+               goto error;     \
+       }       \
+       memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \
+       memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \
+       memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \
+       memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \
+       memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \
+       memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \
+}while(0)
+
+#define XHTTP_RPC_COPY_10(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)    \
+do{    \
+       if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len>max_page_len) {   \
+               goto error;     \
+       }       \
+       memcpy((p), (s1).s, (s1).len); (p) += (s1).len; \
+       memcpy((p), (s2).s, (s2).len); (p) += (s2).len; \
+       memcpy((p), (s3).s, (s3).len); (p) += (s3).len; \
+       memcpy((p), (s4).s, (s4).len); (p) += (s4).len; \
+       memcpy((p), (s5).s, (s5).len); (p) += (s5).len; \
+       memcpy((p), (s6).s, (s6).len); (p) += (s6).len; \
+       memcpy((p), (s7).s, (s7).len); (p) += (s7).len; \
+       memcpy((p), (s8).s, (s8).len); (p) += (s8).len; \
+       memcpy((p), (s9).s, (s9).len); (p) += (s9).len; \
+       memcpy((p), (s10).s, (s10).len); (p) += (s10).len;      \
+}while(0)
+
+
+static const str XHTTP_RPC_Response_Head_1 = str_init("<html><head><title>");
+static const str XHTTP_RPC_Response_Head_2 = str_init(" RPC Management Interface</title>"\
+        "<style type=\"text/css\">"\
+                "body{margin:0;}body,p,div,td,th,tr,form,ol,ul,li,input,textarea,select,"\
+                "a{font-family:\"lucida grande\",verdana,geneva,arial,helvetica,sans-serif;font-size:14px;}"\
+                "a:hover{text-decoration:none;}a{text-decoration:underline;}"\
+                ".foot{padding-top:40px;font-size:10px;color:#333333;}"\
+                ".foot a{font-size:10px;color:#000000;}"
+                "table.center{margin-left:auto;margin-right:auto;}\n"\
+        "</style>"\
+        "<meta http-equiv=\"Expires\" content=\"0\">"\
+        "<meta http-equiv=\"Pragma\" content=\"no-cache\">");
+
+static const str XHTTP_RPC_Response_Head_3 = str_init(\
+"</head>"\
+"<body alink=\"#000000\" bgcolor=\"#ffffff\" link=\"#000000\" text=\"#000000\" vlink=\"#000000\">");
+
+static const str XHTTP_RPC_Response_Title_Table_1 = str_init(\
+"<table cellspacing=\"0\" cellpadding=\"5\" width=\"100%%\" border=\"0\">"\
+        "<tr bgcolor=\"#BBDDFF\">"\
+        "<td colspan=2 valign=\"top\" align=\"left\" bgcolor=\"#EFF7FF\" width=\"100%%\">"\
+        "<br/><h2 align=\"center\">");
+static const str XHTTP_RPC_Response_Title_Table_2 = str_init(" RPC Interface</h2>"\
+        "<p align=\"center\">");
+static const str XHTTP_RPC_Response_Title_Table_4 = str_init("</p><br/></td></tr></table>\n<center>\n");
+
+static const str XHTTP_RPC_Response_Menu_Table_1 = str_init("<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\"><tbody><tr>\n");
+static const str XHTTP_RPC_Response_Menu_Table_2 = str_init("<td><a href='");
+static const str XHTTP_RPC_Response_Menu_Table_2b = str_init("<td><b><a href='");
+static const str XHTTP_RPC_Response_Menu_Table_3 = str_init("'>");
+static const str XHTTP_RPC_Response_Menu_Table_4 = str_init("</a><td>\n");
+static const str XHTTP_RPC_Response_Menu_Table_4b = str_init("</a></b><td>\n");
+static const str XHTTP_RPC_Response_Menu_Table_5 = str_init("</tr></tbody></table>\n");
+
+static const str XHTTP_RPC_Response_Menu_Cmd_Table_1 = str_init("<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" width=\"90%\"><tbody>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_tr_1 = str_init("<tr>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_1a = str_init("   <td width=\"10%\"><a href='");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_3a = str_init("'>");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_4a = str_init("</a></td>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_1b = str_init("   <td align=\"left\"><b>");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_1c = str_init("   <td valign=\"top\" align=\"left\" rowspan=\"");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_1d = str_init("   <td>");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_3c = str_init("\">");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_4b = str_init("</b></td>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_4c = str_init("   </td>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_td_4d = str_init("</td>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_tr_2 = str_init("</tr>\n");
+static const str XHTTP_RPC_Response_Menu_Cmd_Table_2 = str_init("</tbody></table>\n");
+
+static const str XHTTP_RPC_NBSP = str_init("&nbsp;");
+static const str XHTTP_RPC_SLASH = str_init("/");
+static const str XHTTP_RPC_SEMICOLON = str_init(": ");
+
+static const str XHTTP_RPC_NODE_INDENT = str_init("\t");
+static const str XHTTP_RPC_NODE_SEPARATOR = str_init(":: ");
+static const str XHTTP_RPC_ATTR_SEPARATOR = str_init(" ");
+static const str XHTTP_RPC_ATTR_VAL_SEPARATOR = str_init("=");
+
+const str XHTTP_RPC_BREAK = str_init("<br/>");
+static const str XHTTP_RPC_CODE_1 = str_init("<pre>");
+static const str XHTTP_RPC_CODE_2 = str_init("</pre>");
+
+static const str XHTTP_RPC_Post_1 = str_init("\n"\
+"               <form name=\"input\" method=\"get\">\n"\
+"                       <input type=\"text\" name=\"arg\"/>\n"\
+"                       <input type=\"submit\" value=\"Submit\"/>\n"\
+"               </form>\n");
+
+static const str XHTTP_RPC_Post_1a = str_init("\n"\
+"               <form name=\"input\" method=\"get\">\n"\
+"                       <textarea name=\"arg\" rows=\"2\" cols=\"60\"></textarea>\n"\
+"                       <input type=\"submit\" value=\"Submit\"/>\n"\
+"               </form>\n");
+
+static const str XHTTP_RPC_Response_Foot = str_init(\
+"\n</center>\n<div align=\"center\" class=\"foot\" style=\"margin:20px auto\">"\
+        "<span style='margin-left:5px;'></span>"\
+        "<a href=\"http://sip-router.org\">SIP Router web site</a> .:. "\
+        "<a href=\"http://www.kamailio.org\">Kamailio web site</a><br/>"\
+        "Copyright &copy; 2011 <a href=\"http://www.voipembedded.com/\">VoIP Embedded</a>"\
+                                                                ". All rights reserved."\
+"</div></body></html>");
+
+#define XHTTP_RPC_ROWSPAN 5
+static const str XHTTP_RPC_CMD_ROWSPAN = str_init("5");
+
+
+
+static const str XHTTP_RPC_ARG = str_init("?arg=");
+str XHTTP_RPC_NULL_ARG = str_init("");
+
+int xhttp_rpc_parse_url(str *http_url, int* mod, int* cmd, str *arg)
+{
+       int index = 0;
+       int i;
+       int mod_len, cmd_len;
+       int url_len = http_url->len;
+       char *url = http_url->s;
+
+       if (url_len==0) {
+               LM_ERR("No URL\n");
+               return -1;
+       }
+       if (url[0] != '/') {
+               LM_ERR("URL starting with [%c] instead of'/'\n", url[0]);
+               return -1;
+       }
+       index++;
+       if (url_len - index < xhttp_rpc_root.len) {
+               LM_ERR("root path 2 short [%.*s]\n", url_len, url);
+               return -1;
+       }
+       if (strncmp(xhttp_rpc_root.s, &url[index], xhttp_rpc_root.len) != 0) {
+               LM_ERR("wrong root path [%.*s]\n", url_len, url);
+               return -1;
+       }
+       if (xhttp_rpc_root.len) {
+               index += xhttp_rpc_root.len;
+               if (url_len - index <= 0)
+                       return 0;
+               if (url[index] != '/') {
+                       LM_ERR("invalid root path [%s]\n", url);
+                       return -1;
+               }
+               index++;
+       }
+       if (index>=url_len)
+               return 0;
+
+       for(i=index;i<url_len && url[i]!='/';i++);
+       mod_len = i - index;
+       for(i=0; i<xhttp_rpc_mod_cmds_size &&
+                       !(xhttp_rpc_mod_cmds[i].mod.s[mod_len]=='.' &&
+                       strncmp(&url[index],
+                               xhttp_rpc_mod_cmds[i].mod.s,
+                               mod_len)==0);
+                       i++);
+       if (i==xhttp_rpc_mod_cmds_size) {
+               LM_ERR("Invalid mod [%.*s] in url [%s]\n",
+                       mod_len, &url[index], url);
+                       return -1;
+       }
+       *mod = i;
+
+       index += mod_len;
+       if (index>=url_len)
+               return 0;
+
+       /* skip over '/' */
+       index++;
+
+       /* Looking for "cmd" */
+       if (index>=url_len)
+               return 0;
+       for(i=index;i<url_len && url[i]!='?';i++);
+       cmd_len = i - index;
+       for(i=0;i<xhttp_rpc_mod_cmds[*mod].size &&
+               !(strncmp(&url[index],
+                       rpc_sarray[xhttp_rpc_mod_cmds[*mod].rpc_e_index+i]->name,
+                       cmd_len) == 0 &&
+               cmd_len==
+               strlen(rpc_sarray[xhttp_rpc_mod_cmds[*mod].rpc_e_index+i]->name));
+               i++);
+       if (i==xhttp_rpc_mod_cmds[*mod].size) {
+               LM_ERR("Invalid cmd [%.*s] in url [%.*s]\n",
+                       cmd_len, &url[index], url_len, url);
+               return -1;
+       }
+       *cmd = i;
+       index += cmd_len;
+       if (index>=url_len) return 0;
+       i = url_len - index;
+       if (i<XHTTP_RPC_ARG.len &&
+               (0!=strncmp(&url[index], XHTTP_RPC_ARG.s, XHTTP_RPC_ARG.len))){
+               LM_ERR("Invalid arg string [%.*s]\n", i, &url[index]);
+               return -1;
+       }
+       index += XHTTP_RPC_ARG.len;
+       arg->s = &url[index];
+       arg->len = url_len - index;
+
+       return 0;
+}
+
+
+void xhttp_rpc_get_next_arg(rpc_ctx_t* ctx, str *arg)
+{
+       int i;
+
+       trim_leading(&ctx->arg2scan);
+       if (ctx->arg2scan.len) {
+               *arg = ctx->arg2scan;
+               for(i=1; i<arg->len-1; i++) {
+                       if(arg->s[i]==' '||arg->s[i]=='\t'||
+                               arg->s[i]=='\r'||arg->s[i]=='\n')
+                               break;
+               }
+               arg->len = i;
+               arg->s[i] = '\0';
+               i++;
+               ctx->arg2scan.s += i;
+               ctx->arg2scan.len -= i;
+       } else {
+               *arg = XHTTP_RPC_NULL_ARG;
+       }
+       return;
+}
+
+int xhttp_rpc_build_header(rpc_ctx_t *ctx)
+{
+       int i, j;
+       char *p = ctx->reply.body.s;
+       char *buf = ctx->reply.buf.s;
+       str code;
+       int max_page_len = ctx->reply.buf.len;
+       int mod = ctx->mod;
+       int cmd = ctx->cmd;
+
+       str name;
+
+       str exec_name = {(char*)ver_name, ver_name_len};
+       str server_hdr = {(char*)full_version, full_version_len};
+
+
+       XHTTP_RPC_COPY_10(p,XHTTP_RPC_Response_Head_1,
+                       exec_name,
+                       XHTTP_RPC_Response_Head_2,
+                       XHTTP_RPC_Response_Head_3,
+                       XHTTP_RPC_Response_Title_Table_1,
+                       exec_name,
+                       XHTTP_RPC_Response_Title_Table_2,
+                       server_hdr,
+                       XHTTP_RPC_Response_Title_Table_4,
+                       /* Building module menu */
+                       XHTTP_RPC_Response_Menu_Table_1);
+       for(i=0;i<xhttp_rpc_mod_cmds_size;i++) {
+               if(i!=mod) {
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Table_2);
+               } else {
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Table_2b);
+               }
+               XHTTP_RPC_COPY(p,XHTTP_RPC_SLASH);
+               if (xhttp_rpc_root.len) {
+                       XHTTP_RPC_COPY_2(p,xhttp_rpc_root,XHTTP_RPC_SLASH);
+               }
+               XHTTP_RPC_COPY_3(p,xhttp_rpc_mod_cmds[i].mod,
+                               XHTTP_RPC_Response_Menu_Table_3,
+                               xhttp_rpc_mod_cmds[i].mod);
+               if(i!=mod) {
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Table_4);
+               } else {
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Table_4b);
+               }
+       }
+       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Table_5);
+
+       if (ctx->arg_received) { /* Build an rpc reply */
+               name.s =
+               (char*)rpc_sarray[xhttp_rpc_mod_cmds[mod].rpc_e_index+cmd]->name;
+               name.len = strlen(name.s);
+               /* Print comand name */
+               XHTTP_RPC_COPY_4(p,XHTTP_RPC_Response_Menu_Cmd_Table_1,
+                               XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                               XHTTP_RPC_Response_Menu_Cmd_td_1a,
+                               XHTTP_RPC_SLASH);
+               if (xhttp_rpc_root.len) {
+                       XHTTP_RPC_COPY_2(p,xhttp_rpc_root, XHTTP_RPC_SLASH);
+               }
+               XHTTP_RPC_COPY_6(p,xhttp_rpc_mod_cmds[mod].mod,
+                               XHTTP_RPC_SLASH,
+                               name,
+                               XHTTP_RPC_Response_Menu_Cmd_td_3a,
+                               name,
+                               XHTTP_RPC_Response_Menu_Cmd_td_4a);
+               /* Print response code */
+               XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Cmd_td_1d);
+               code.s = int2str((unsigned long)ctx->reply.code, &code.len);
+               XHTTP_RPC_COPY_10(p,code,
+                               XHTTP_RPC_SEMICOLON,
+                               ctx->reply.reason,
+                               XHTTP_RPC_Response_Menu_Cmd_td_4d,
+                               XHTTP_RPC_Response_Menu_Cmd_tr_2,
+                               XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                               XHTTP_RPC_Response_Menu_Cmd_td_1d,
+                               XHTTP_RPC_Response_Menu_Cmd_td_4d,
+                               XHTTP_RPC_Response_Menu_Cmd_td_1d,
+                               XHTTP_RPC_CODE_1);
+       } else if (mod>=0) { /* Building command menu */
+               if (ctx->reply.body.len==0 && ctx->reply.code!=200) {
+                       code.s = int2str((unsigned long)ctx->reply.code, &code.len);
+                       XHTTP_RPC_COPY_5(p,XHTTP_RPC_CODE_1,
+                                       code,
+                                       XHTTP_RPC_SEMICOLON,
+                                       ctx->reply.reason,
+                                       XHTTP_RPC_CODE_2);
+               } else {
+                       name.s =
+                       (char*)rpc_sarray[xhttp_rpc_mod_cmds[mod].rpc_e_index]->name;
+                       name.len = strlen(name.s);
+                       /* Build the list of comands for the selected module */
+                       XHTTP_RPC_COPY_4(p,XHTTP_RPC_Response_Menu_Cmd_Table_1,
+                                       XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                                       XHTTP_RPC_Response_Menu_Cmd_td_1a,
+                                       XHTTP_RPC_SLASH);
+                       if (xhttp_rpc_root.len) {
+                               XHTTP_RPC_COPY_2(p,xhttp_rpc_root,XHTTP_RPC_SLASH);
+                       }
+                       XHTTP_RPC_COPY_6(p,xhttp_rpc_mod_cmds[mod].mod,
+                                       XHTTP_RPC_SLASH,
+                                       name,
+                                       XHTTP_RPC_Response_Menu_Cmd_td_3a,
+                                       name,
+                                       XHTTP_RPC_Response_Menu_Cmd_td_4a);
+                       if (cmd>=0) {
+                               name.s =
+                       (char*)rpc_sarray[xhttp_rpc_mod_cmds[mod].rpc_e_index+cmd]->name;
+                               name.len = strlen(name.s);
+                               XHTTP_RPC_COPY_3(p,XHTTP_RPC_Response_Menu_Cmd_td_1b,
+                                               name,
+                                               XHTTP_RPC_Response_Menu_Cmd_td_4b);
+                       }
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Cmd_tr_2);
+                       for(j=1;j<xhttp_rpc_mod_cmds[mod].size;j++) {
+                               name.s =
+                       (char*)rpc_sarray[xhttp_rpc_mod_cmds[mod].rpc_e_index+j]->name;
+                               name.len = strlen(name.s);
+                               XHTTP_RPC_COPY_3(p,XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                                               XHTTP_RPC_Response_Menu_Cmd_td_1a,
+                                               XHTTP_RPC_SLASH);
+                               if (xhttp_rpc_root.len) {
+                                       XHTTP_RPC_COPY_2(p,xhttp_rpc_root, XHTTP_RPC_SLASH);
+                               }
+                               XHTTP_RPC_COPY_6(p,xhttp_rpc_mod_cmds[mod].mod,
+                                               XHTTP_RPC_SLASH,
+                                               name,
+                                               XHTTP_RPC_Response_Menu_Cmd_td_3a,
+                                               name,
+                                               XHTTP_RPC_Response_Menu_Cmd_td_4a);
+                               if (cmd>=0){
+                                       if (j==1) {
+                                               XHTTP_RPC_COPY_5(p,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_1c,
+                                                       XHTTP_RPC_CMD_ROWSPAN,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_3c,
+                                                       XHTTP_RPC_Post_1,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_4c);
+                                       } else if (j>XHTTP_RPC_ROWSPAN) {
+                                               XHTTP_RPC_COPY_3(p,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_1d,
+                                                       XHTTP_RPC_NBSP,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_4d);
+                                       }
+                               }
+                               XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Menu_Cmd_tr_2);
+                       }
+                       if (cmd>=0){
+                               if (j==1) {
+                                       XHTTP_RPC_COPY_10(p,XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_1d,
+                                                       XHTTP_RPC_NBSP,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_4d,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_1c,
+                                                       XHTTP_RPC_CMD_ROWSPAN,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_3c,
+                                                       XHTTP_RPC_Post_1,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_4c,
+                                                       XHTTP_RPC_Response_Menu_Cmd_tr_2);
+                                       j++;
+                               }
+                               for(;j<=XHTTP_RPC_ROWSPAN;j++) {
+                                       XHTTP_RPC_COPY_5(p,XHTTP_RPC_Response_Menu_Cmd_tr_1,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_1d,
+                                                       XHTTP_RPC_NBSP,
+                                                       XHTTP_RPC_Response_Menu_Cmd_td_4d,
+                                                       XHTTP_RPC_Response_Menu_Cmd_tr_2);
+                               }
+                       }
+               }
+               XHTTP_RPC_COPY_2(p,XHTTP_RPC_Response_Menu_Cmd_Table_2,
+                               XHTTP_RPC_Response_Foot);
+       } else {
+               if (ctx->reply.body.len==0 && ctx->reply.code!=200) {
+                       code.s = int2str((unsigned long)ctx->reply.code, &code.len);
+                       XHTTP_RPC_COPY_5(p,XHTTP_RPC_CODE_1,
+                                       code,
+                                       XHTTP_RPC_SEMICOLON,
+                                       ctx->reply.reason,
+                                       XHTTP_RPC_CODE_2);
+               }
+               XHTTP_RPC_COPY(p,XHTTP_RPC_Response_Foot);
+       }
+
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return 0;
+
+
+error:
+       LM_ERR("buffer 2 small\n");
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return -1;
+}
+
+
+int xhttp_rpc_build_content(rpc_ctx_t *ctx, str *val, str *id)
+{
+       char *p;
+       char *buf = ctx->reply.buf.s;
+       int max_page_len = ctx->reply.buf.len;
+       int i;
+
+       if (ctx->reply.body.len==0)
+               if (0!=xhttp_rpc_build_header(ctx))
+                       return -1;
+
+       p = ctx->reply.body.s + ctx->reply.body.len;
+
+       if (val && val->s && val->len) {
+               if (id && id->s && id->len) {
+                       for(i=0;i<ctx->struc_depth;i++)
+                               XHTTP_RPC_COPY(p,XHTTP_RPC_NODE_INDENT);
+                       if ((int)(p-buf)+id->len>max_page_len) {
+                               goto error;
+                       }
+                       memcpy(p, id->s, id->len);
+                       p += id->len;
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_SEMICOLON);
+               }
+               if ((int)(p-buf)+val->len>max_page_len) {
+                       goto error;
+               }
+               memcpy(p, val->s, val->len);
+               p += val->len;
+               XHTTP_RPC_COPY(p,XHTTP_RPC_BREAK);
+       } else {
+               if (id && id->s && id->len) {
+                       for(i=0;i<ctx->struc_depth;i++)
+                               XHTTP_RPC_COPY(p,XHTTP_RPC_NODE_INDENT);
+                       if ((int)(p-buf)+id->len>max_page_len) {
+                               goto error;
+                       }
+                       memcpy(p, id->s, id->len);
+                       p += id->len;
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_SEMICOLON);
+                       XHTTP_RPC_COPY(p,XHTTP_RPC_BREAK);
+               }
+       }
+       ctx->reply.body.len = p - ctx->reply.body.s;
+
+       return 0;
+error:
+       LM_ERR("buffer 2 small\n");
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return -1;
+}
+
+
+int xhttp_rpc_insert_break(rpc_ctx_t *ctx)
+{
+       char *p = ctx->reply.body.s + ctx->reply.body.len;;
+       char *buf = ctx->reply.buf.s;
+       int max_page_len = ctx->reply.buf.len;
+
+       XHTTP_RPC_COPY(p,XHTTP_RPC_BREAK);
+
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return 0;
+error:
+       LM_ERR("buffer 2 small\n");
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return -1;
+}
+
+int xhttp_rpc_build_page(rpc_ctx_t *ctx)
+{
+       char *p;
+       char *buf = ctx->reply.buf.s;
+       int max_page_len = ctx->reply.buf.len;
+
+       if (ctx->reply.body.len==0)
+               if (0!=xhttp_rpc_build_content(ctx, NULL, NULL))
+                       return -1;
+
+       p = ctx->reply.body.s + ctx->reply.body.len;
+
+       if (ctx->arg_received) {
+               XHTTP_RPC_COPY_5(p,XHTTP_RPC_CODE_2,
+                               XHTTP_RPC_Response_Menu_Cmd_td_4d,
+                               XHTTP_RPC_Response_Menu_Cmd_tr_2,
+                               XHTTP_RPC_Response_Menu_Cmd_Table_2,
+                               XHTTP_RPC_Response_Foot);
+               ctx->reply.body.len = p - ctx->reply.body.s;
+       }
+
+       return 0;
+error:
+       LM_ERR("buffer 2 small\n");
+       ctx->reply.body.len = p - ctx->reply.body.s;
+       return -1;
+}
diff --git a/modules/xhttp_rpc/xhttp_rpc_fnc.h b/modules/xhttp_rpc/xhttp_rpc_fnc.h
new file mode 100644 (file)
index 0000000..7dd0d53
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2011 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2011-11-11  initial version (osas)
+ */
+
+
+#ifndef _XHTTP_RPC_FNC_H
+#define _XHTTP_RPC_FNC_H
+
+
+int xhttp_rpc_parse_url(str *url, int *mod, int *cmd, str *arg);
+void xhttp_rpc_get_next_arg(rpc_ctx_t* ctx, str *arg);
+int xhttp_rpc_build_content(rpc_ctx_t *ctx, str *val, str *id);
+int xhttp_rpc_insert_break(rpc_ctx_t *ctx);
+int xhttp_rpc_build_page(rpc_ctx_t *ctx);
+
+#endif
+
index 88d4a22..a3760b9 100644 (file)
 
 #include <sys/time.h>
 
+/* Solaris does not provide timersub macro in <sys/time.h> */
+#ifdef __OS_solaris
+#define timersub(tvp, uvp, vvp)                     \
+    do {                                \
+        (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;      \
+        (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;   \
+        if ((vvp)->tv_usec < 0) {               \
+            (vvp)->tv_sec--;                \
+            (vvp)->tv_usec += 1000000;          \
+        }                           \
+    } while (0)
+#endif // __OS_solaris
+
 #define TIME_STR_BUFFER_SIZE 20
 #define TIME_BUFFER_LENGTH 256
 
index 988d2f6..9fb7f47 100644 (file)
@@ -13,5 +13,5 @@ LIBS=
 DEFS+=-DOPENSER_MOD_INTERFACE
 
 SERLIBPATH=../../lib
-SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore $(SERLIBPATH)/srdb1/srdb1
 include ../../Makefile.modules
index 252a823..e77eac1 100644 (file)
@@ -24,9 +24,9 @@ Jan Janak
 
    <jan@iptel.org>
 
-   Copyright Â© 2002, 2003 FhG FOKUS
+   Copyright © 2002, 2003 FhG FOKUS
 
-   Copyright Â© 2005 Voice Sistem SRL
+   Copyright © 2005 Voice Sistem SRL
      __________________________________________________________________
 
    Table of Contents
@@ -57,6 +57,7 @@ Jan Janak
               4.2. www_authorize(realm, table)
               4.3. proxy_authenticate(realm, table)
               4.4. proxy_authorize(realm, table)
+              4.5. auth_check(realm, table, flags)
 
    List of Examples
 
@@ -71,6 +72,7 @@ Jan Janak
    1.9. version_table parameter usage
    1.10. www_authorize usage
    1.11. proxy_authorize usage
+   1.12. auth_check usage
 
 Chapter 1. Admin Guide
 
@@ -100,6 +102,7 @@ Chapter 1. Admin Guide
         4.2. www_authorize(realm, table)
         4.3. proxy_authenticate(realm, table)
         4.4. proxy_authorize(realm, table)
+        4.5. auth_check(realm, table, flags)
 
 1. Overview
 
@@ -148,7 +151,7 @@ Chapter 1. Admin Guide
    For dbtext module (which stores data in plaintext files) it is
    directory in which the database resides.
 
-   Default value is “mysql://openserro:openserro@localhost/openser”.
+   Default value is "mysql://openserro:openserro@localhost/openser".
 
    Example 1.1. db_url parameter usage
 ...
@@ -160,7 +163,7 @@ modparam("auth_db", "db_url", "dbdriver://username:password@dbhost/dbname")
    This is the name of the column holding usernames. Default value is fine
    for most people. Use the parameter if you really need to change it.
 
-   Default value is “username”.
+   Default value is "username".
 
    Example 1.2. user_column parameter usage
 ...
@@ -173,7 +176,7 @@ modparam("auth_db", "user_column", "user")
    is fine for most people. Use the parameter if you really need to change
    it.
 
-   Default value is “domain”.
+   Default value is "domain".
 
    Example 1.3. domain_column parameter usage
 ...
@@ -188,7 +191,7 @@ modparam("auth_db", "domain_column", "domain")
    safe because the server doesn't need to know plaintext passwords and
    they cannot be obtained from HA1 strings.
 
-   Default value is “ha1”.
+   Default value is "ha1".
 
    Example 1.4. password_column parameter usage
 ...
@@ -216,16 +219,16 @@ modparam("auth_db", "password_column_2", "ha1_2")
    HA1 string or plaintext passwords for authentification.
 
    If the parameter is set to 0 and the username parameter of credentials
-   contains also “@domain” (some user agents append the domain to the
+   contains also "@domain" (some user agents append the domain to the
    username parameter), then the server will use the HA1 values from the
-   column specified in the “password_column_2” parameter. If the username
+   column specified in the "password_column_2" parameter. If the username
    parameter doesn't contain a domain, the server will use the HA1 values
-   from the column given in the “password_column”parameter.
+   from the column given in the "password_column"parameter.
 
    If the parameter is set to 1 then the HA1 value will be calculated from
-   the column specified in the “password_column” parameter.
+   the column specified in the "password_column" parameter.
 
-   The “password_column_2”column contain also HA1 strings but they should
+   The "password_column_2"column contain also HA1 strings but they should
    be calculated including the domain in the username parameter (as
    opposed to password_column which (when containing HA1 strings) should
    always contains HA1 strings calculated without domain in username.
@@ -251,7 +254,7 @@ modparam("auth_db", "calculate_ha1", 1)
    IMPORTANT: before turning on this parameter, be sure that the domain
    column in subscriber table is properly populated.
 
-   Default value is “0 (false)”.
+   Default value is "0 (false)".
 
    Example 1.7. use_domain parameter usage
 ...
@@ -270,7 +273,7 @@ modparam("auth_db", "use_domain", 1)
      * credential = (avp_specification '=' column_name) | (column_name)
      * avp_specification = '$avp(' + 'i:'ID | 's:'NAME | alias + ')'
 
-   Default value of this parameter is “rpid”.
+   Default value of this parameter is "rpid".
 
    Example 1.8. load_credentials parameter usage
 ...
@@ -284,7 +287,7 @@ modparam("auth_db", "load_credentials", "$avp(i:123)=rpid;email_address")
    If set to 0, the module will skip checking the version for subscriber
    table.
 
-   Default value is “1 (check for table version)”.
+   Default value is "1 (check for table version)".
 
    Example 1.9. version_table parameter usage
 ...
@@ -297,8 +300,9 @@ modparam("auth_db", "version_table", 0)
    4.2. www_authorize(realm, table)
    4.3. proxy_authenticate(realm, table)
    4.4. proxy_authorize(realm, table)
+   4.5. auth_check(realm, table, flags)
 
-4.1.  www_authenticate(realm, table)
+4.1. www_authenticate(realm, table)
 
    Name alias: www_authorize(realm, table)
 
@@ -319,7 +323,7 @@ modparam("auth_db", "version_table", 0)
      * realm - Realm is a opaque string that the user agent should present
        to the user so he can decide what username and password to use.
        Usually this is domain of the host the server is running on.
-       It must not be empty string “”. In case of REGISTER requests To
+       It must not be empty string "". In case of REGISTER requests To
        header field domain (e.g., variable $td) can be used (because this
        header field represents the user being registered), for all other
        messages From header field domain can be used (e.g., variable $fd).
@@ -336,13 +340,13 @@ if (!www_authorize("kamailio.org", "subscriber")) {
 };
 ...
 
-4.2.  www_authorize(realm, table)
+4.2. www_authorize(realm, table)
 
    It is same function as www_authenticate(realm, table). This name is
    kept for backward compatibility, since it was named this way first time
    by it actually does user authentication.
 
-4.3.  proxy_authenticate(realm, table)
+4.3. proxy_authenticate(realm, table)
 
    Name alias: proxy_authorize(realm, table)
 
@@ -359,9 +363,9 @@ if (!www_authorize("kamailio.org", "subscriber")) {
      * realm - Realm is a opaque string that the user agent should present
        to the user so he can decide what username and password to use.
        Usually this is domain of the host the server is running on.
-       It must not be empty string “”. Apart of a static strinh, typical
+       It must not be empty string "". Apart of a static string, typical
        value is From header field domain (e.g., variable $fd).
-       If an empty string “” is used then the server will generate it from
+       If an empty string "" is used then the server will generate it from
        the request. From header field domain will be used as realm.
        The string may contain pseudo variables.
      * table - Table to be used to lookup usernames and passwords (usually
@@ -376,8 +380,43 @@ if (!proxy_authorize("$fd", "subscriber)) {
 };
 ...
 
-4.4.  proxy_authorize(realm, table)
+4.4. proxy_authorize(realm, table)
 
    It is same function as proxy_authenticate(realm, table). This name is
    kept for backward compatibility, since it was named this way first time
    but it actually does user authentication.
+
+4.5. auth_check(realm, table, flags)
+
+   The function combines the functionalities of www_authenticate and
+   proxy_authenticate, first being exectuted if the SIP request is a
+   REGISTER, the second for the rest.
+
+   In addition, a matter of flags parameter value, the function checks if
+   authentication username matches From/To header username.
+
+   Meaning of the parameters is as follows:
+     * realm - Realm is a opaque string that the user agent should present
+       to the user so he can decide what username and password to use.
+       Usually this is domain of the host the server is running on.
+       It must not be empty string "". Apart of a static string, typical
+       value is From header field domain (e.g., variable $fd).
+       The string may contain pseudo variables.
+     * table - Table to be used to lookup usernames and passwords (usually
+       subscribers table).
+       The string may contain pseudo variables.
+     * flags - set of flags to control the behaviour of the function. If
+       it is 1, then the function will check to see if the authentication
+       username matches either To or From header username, a matter of
+       whether it is for a REGISTER request or not..
+       The string may contain pseudo variables.
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 1.12. auth_check usage
+...
+if (!auth_check("$fd", "subscriber", "1")) {
+    auth_challenge("$fd", "1");
+    exit;
+}
+...
index 65a3326..5a1e2ef 100644 (file)
@@ -69,6 +69,7 @@ static int mod_init(void);
 
 
 static int auth_fixup(void** param, int param_no);
+static int auth_check_fixup(void** param, int param_no);
 int parse_aaa_pvs(char *definition, pv_elem_t **pv_def, int *cnt);
 
 #define USER_COL "username"
@@ -119,6 +120,8 @@ static cmd_export_t cmds[] = {
                REQUEST_ROUTE},
        {"proxy_authenticate", (cmd_function)proxy_authenticate, 2, auth_fixup, 0,
                REQUEST_ROUTE},
+       {"auth_check",         (cmd_function)auth_check,         3, auth_check_fixup, 0,
+               REQUEST_ROUTE},
        {"bind_auth_db",       (cmd_function)bind_auth_db,       0, 0, 0,
                0},
        {0, 0, 0, 0, 0, 0}
@@ -265,6 +268,27 @@ static int auth_fixup(void** param, int param_no)
        return 0;
 }
 
+/*
+ * Convert cfg parameters to run-time structures
+ */
+static int auth_check_fixup(void** param, int param_no)
+{
+       if(strlen((char*)*param)<=0) {
+               LM_ERR("empty parameter %d not allowed\n", param_no);
+               return -1;
+       }
+       if (param_no == 1) {
+               return fixup_var_str_12(param, 1);
+       }
+       if (param_no == 2) {
+               return fixup_var_str_12(param, 2);
+       }
+       if (param_no == 3) {
+               return fixup_igp_null(param, 1);
+       }
+       return 0;
+}
+
 /*
  * Parse extra credentials list
  */
index 070a875..6912d80 100644 (file)
@@ -45,6 +45,7 @@
 #include "../../usr_avp.h"
 #include "../../mod_fix.h"
 #include "../../mem/mem.h"
+#include "../../lib/kcore/parser_helpers.h"
 #include "api.h"
 #include "authdb_mod.h"
 
@@ -305,6 +306,83 @@ int www_authenticate(struct sip_msg* _m, char* _realm, char* _table)
        return digest_authenticate(_m, &srealm, &stable, HDR_AUTHORIZATION_T);
 }
 
+/*
+ * Authenticate using WWW/Proxy-Authorize header field
+ */
+#define AUTH_CHECK_ID_F 1<<0
+
+int auth_check(struct sip_msg* _m, char* _realm, char* _table, char *_flags)
+{
+       str srealm;
+       str stable;
+       int iflags;
+       int ret;
+       hdr_field_t *hdr;
+       sip_uri_t *uri;
+
+       if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) {
+               return AUTH_OK;
+       }
+
+       if(_m==NULL || _realm==NULL || _table==NULL || _flags==NULL) {
+               LM_ERR("invalid parameters\n");
+               return AUTH_ERROR;
+       }
+
+       if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0) {
+               LM_ERR("failed to get realm value\n");
+               return AUTH_ERROR;
+       }
+
+       if (srealm.len==0) {
+               LM_ERR("invalid realm parameter - empty value\n");
+               return AUTH_ERROR;
+       }
+
+       if (get_str_fparam(&stable, _m, (fparam_t*)_table) < 0) {
+               LM_ERR("failed to get realm value\n");
+               return AUTH_ERROR;
+       }
+
+       if (stable.len==0) {
+               LM_ERR("invalid table parameter - empty value\n");
+               return AUTH_ERROR;
+       }
+
+       if(fixup_get_ivalue(_m, (gparam_p)_flags, &iflags)!=0)
+       {
+               LM_ERR("invalid flags parameter\n");
+               return -1;
+       }
+
+       LM_DBG("realm [%.*s] table [%.*s] flags [%d]\n", srealm.len, srealm.s,
+                       stable.len,  stable.s, iflags);
+
+       if(_m->REQ_METHOD==METHOD_REGISTER)
+               ret = digest_authenticate(_m, &srealm, &stable, HDR_AUTHORIZATION_T);
+       else
+               ret = digest_authenticate(_m, &srealm, &stable, HDR_PROXYAUTH_T);
+
+       if(ret==AUTH_OK && (iflags&AUTH_CHECK_ID_F)) {
+               hdr = (_m->proxy_auth==0)?_m->authorization:_m->proxy_auth;
+               srealm = ((auth_body_t*)(hdr->parsed))->digest.username.user;
+               if(_m->REQ_METHOD==METHOD_REGISTER) {
+                       if((uri=parse_to_uri(_m))==NULL)
+                               return AUTH_ERROR;
+               } else {
+                       if((uri=parse_from_uri(_m))==NULL)
+                               return AUTH_ERROR;
+               }
+               if(srealm.len==uri->user.len
+                                       && strncmp(srealm.s, uri->user.s, srealm.len)==0)
+                       return ret;
+               return AUTH_USER_MISMATCH;
+       }
+
+       return ret;
+}
+
+
 /**
  * @brief bind functions to AUTH_DB API structure
  */
index 9903931..1cf3caa 100644 (file)
@@ -46,6 +46,11 @@ int proxy_authenticate(struct sip_msg* _msg, char* _realm, char* _table);
 int www_authenticate(struct sip_msg* _msg, char* _realm, char* _table);
 
 
+/*
+ * Authenticate using WWW/Proxy-Authorize header field
+ */
+int auth_check(struct sip_msg* _m, char* _realm, char* _table, char *_flags);
+
 /*
  * Bind to AUTH_DB API
  */
index 92edb46..8f21b2f 100644 (file)
@@ -416,7 +416,7 @@ if (!www_authorize("kamailio.org", "subscriber")) {
                        </para>
                        <para>
                        It must not be empty string <quote></quote>. Apart of a static
-                       strinh, typical value is From header field domain
+                       string, typical value is From header field domain
                        (e.g., variable $fd).
                        </para>
                        <para>
@@ -460,6 +460,74 @@ if (!proxy_authorize("$fd", "subscriber)) {
                </para>
        </section>
 
+       <section>
+               <title>
+                       <function moreinfo="none">auth_check(realm, table, flags)</function>
+               </title>
+               <para>The function combines the functionalities of
+               <function moreinfo="none">www_authenticate</function> and
+               <function moreinfo="none">proxy_authenticate</function>, first being
+               exectuted if the SIP request is a REGISTER, the second for the rest.
+               </para>
+               <para>
+               In addition, a matter of <emphasis>flags</emphasis> parameter value,
+               the function checks if authentication username matches From/To header
+               username.
+               </para>
+               <para>Meaning of the parameters is as follows:</para>
+               <itemizedlist>
+               <listitem>
+                       <para><emphasis>realm</emphasis> - Realm is a opaque string that
+                       the user agent should present to the user so he can decide what
+                       username and password to use. Usually this is domain of the host
+                       the server is running on.
+                       </para>
+                       <para>
+                       It must not be empty string <quote></quote>. Apart of a static
+                       string, typical value is From header field domain
+                       (e.g., variable $fd).
+                       </para>
+                       <para>
+                       The string may contain pseudo variables.
+                       </para>
+               </listitem>
+               <listitem>
+                       <para><emphasis>table</emphasis> - Table to be used to lookup
+                       usernames and passwords (usually subscribers table).
+                       </para>
+                       <para>
+                       The string may contain pseudo variables.
+                       </para>
+               </listitem>
+               <listitem>
+                       <para><emphasis>flags</emphasis> - set of flags to control the
+                       behaviour of the function. If it is 1, then the function will
+                       check to see if the authentication username matches either To or
+                       From header username, a matter of whether it is for a REGISTER
+                       request or not..
+                       </para>
+                       <para>
+                       The string may contain pseudo variables.
+                       </para>
+               </listitem>
+               </itemizedlist>
+               <para>
+               This function can be used from REQUEST_ROUTE.
+               </para>
+               <example>
+               <title>auth_check usage</title>
+               <programlisting format="linespecific">
+...
+if (!auth_check("$fd", "subscriber", "1")) {
+    auth_challenge("$fd", "1");
+    exit;
+}
+...
+</programlisting>
+               </example>
+       </section>
+
+
        </section>
 </chapter>
 
index f72d043..9a6df8e 100644 (file)
@@ -76,6 +76,13 @@ struct module_exports exports = {
 };
 
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 static int oracle_mod_init(void)
 {
        sword major, minor, update, patch, port;
@@ -83,7 +90,7 @@ static int oracle_mod_init(void)
        OCIClientVersion(&major, &minor, &update, &patch, &port);
        LM_DBG("Oracle client version is %d.%d.%d.%d.%d\n",
                major, minor, update, patch, port);
-       return db_query_init();
+       return 0;
 }
 
 
index 520cc38..54b2fe8 100644 (file)
@@ -57,6 +57,13 @@ static cmd_export_t cmds[] = {
        {0, 0, 0, 0, 0, 0}
 };
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 static int sqlite_mod_init(void)
 {
        sqlite3_initialize();
@@ -64,7 +71,7 @@ static int sqlite_mod_init(void)
        LM_INFO("SQlite library version %s (compiled using %s)\n",
                sqlite3_libversion(),
                SQLITE_VERSION);
-       return db_query_init();
+       return 0;
 }
 
 
index 2739b92..633dbb3 100644 (file)
@@ -83,6 +83,12 @@ struct module_exports exports = {
        0         /* per-child init function */
 };
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
 
 static int mod_init(void)
 {
index 1debdef..60dbb79 100644 (file)
@@ -100,8 +100,15 @@ int db_unixodbc_bind_api(db_func_t *dbb)
        return 0;
 }
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+       if(db_api_init()<0)
+               return -1;
+       return 0;
+}
+
 int unixodbc_mod_init(void)
 {
-       return db_query_init();
+       return 0;
 }
 
index 699e2b7..2725b54 100644 (file)
@@ -16,9 +16,9 @@ Edited by
 
 Carsten Bock
 
-   Copyright Â© 2006 Voice Sistem SRL
+   Copyright © 2006 Voice Sistem SRL
 
-   Copyright Â© 2011 Carsten Bock, http://www.ng-voice.com
+   Copyright © 2011 Carsten Bock, http://www.ng-voice.com
      __________________________________________________________________
 
    Table of Contents
@@ -329,14 +329,14 @@ Chapter 1. Admin Guide
 2. How it works
 
    To create the dialog associated to an initial request, the flag
-   “dlg_flag” (Section 5.4, “dlg_flag (integer)”) must be set before
+   "dlg_flag" (Section 5.4, "dlg_flag (integer)") must be set before
    creating the corresponding transaction.
 
-   The dialog is automatically destroyed when a “BYE” is received. In case
-   of no “BYE”, the dialog lifetime is controlled via the default timeout
-   (see “default_timeout” - Section 5.6, “default_timeout (integer)”) and
-   custom timeout (see “timeout_avp” - Section 5.5, “timeout_avp
-   (string)). The dialog timeout is reset each time a sequential request
+   The dialog is automatically destroyed when a "BYE" is received. In case
+   of no "BYE", the dialog lifetime is controlled via the default timeout
+   (see "default_timeout" - Section 5.6, "default_timeout (integer)") and
+   custom timeout (see "timeout_avp" - Section 5.5, "timeout_avp
+   (string)"). The dialog timeout is reset each time a sequential request
    passes.
 
 3. Dialog profiling
@@ -432,7 +432,7 @@ Chapter 1. Admin Guide
    variables, the module provide information about the dialog processing.
    Set it to zero to disable or to non-zero to enable it.
 
-   Default value is “1 (enabled)”.
+   Default value is "1 (enabled)".
 
    Example 1.1. Set enable_stats parameter
 ...
@@ -450,7 +450,7 @@ modparam("dialog", "enable_stats", 0)
    not take place. If you really want to modify the hash_size you must
    delete all table's rows before restarting the server.
 
-   Default value is “4096”.
+   Default value is "4096".
 
    Example 1.2. Set hash_size parameter
 ...
@@ -462,7 +462,7 @@ modparam("dialog", "hash_size", 1024)
    Name of the Record-Route parameter to be added with the dialog cookie.
    It is used for the fast dialog matching of sequential requests.
 
-   Default value is “did”.
+   Default value is "did".
 
    Example 1.3. Set rr_param parameter
 ...
@@ -474,7 +474,7 @@ modparam("dialog", "rr_param", "xyz")
    Flag to be used for marking if a dialog should be constructed for the
    current request (this make sense only for initial requests).
 
-   Default value is “none”.
+   Default value is "none".
 
    Example 1.4. Set dlg_flag parameter
 ...
@@ -487,7 +487,7 @@ modparam("dialog", "dlg_flag", 4)
    for the dialog. It may be used only in a request (initial or
    sequential) context
 
-   Default value is “none”.
+   Default value is "none".
 
    Example 1.5. Set timeout_avp parameter
 ...
@@ -498,7 +498,7 @@ modparam("dialog", "timeout_avp", "$avp(i:10)")
 
    The default dialog timeout (in seconds) if no custom one is set.
 
-   Default value is “43200 (12 hours)”.
+   Default value is "43200 (12 hours)".
 
    Example 1.6. Set default_timeout parameter
 ...
@@ -510,7 +510,7 @@ modparam("dialog", "default_timeout", 21600)
    A string containing the extra headers (full format, with EOH) to be
    added in the requests generated by the module (like BYEs).
 
-   Default value is “NULL”.
+   Default value is "NULL".
 
    Example 1.7. Set dlf_extra_hdrs parameter
 ...
@@ -531,7 +531,7 @@ modparam("dialog", "dlg_extra_hdrs", "Hint: credit expired\r\n")
      * 2 - DID_NONE - the match is done exclusively based on SIP elements;
        no DID information is added in RR.
 
-   Default value is “0 (DID_ONLY)”.
+   Default value is "0 (DID_ONLY)".
 
    Example 1.8. Set dlg_match_mode parameter
 ...
@@ -560,7 +560,7 @@ modparam("dialog", "detect_spirals", 1)
    If you want to store the information about the dialogs in a database a
    database url must be specified.
 
-   Default value is “mysql://openser:openserrw@localhost/openser”.
+   Default value is "mysql://openser:openserrw@localhost/openser".
 
    Example 1.10. Set db_url parameter
 ...
@@ -580,7 +580,7 @@ modparam("dialog", "db_url", "dbdriver://username:password@dbhost/dbname")
      * 3 - SHUTDOWN - the dialog information will be flushed into DB only
        at shutdown - no runtime updates.
 
-   Default value is “0”.
+   Default value is "0".
 
    Example 1.11. Set db_mode parameter
 ...
@@ -594,7 +594,7 @@ modparam("dialog", "db_mode", 1)
    interval will generate intensive database operations, a too large one
    will not notice short dialogs.
 
-   Default value is “60”.
+   Default value is "60".
 
    Example 1.12. Set db_update_period parameter
 ...
@@ -609,7 +609,7 @@ modparam("dialog", "db_update_period", 120)
    it should be below 400. The database driver must support fetch_result()
    capability. A value of 0 means the functionality is disabled.
 
-   Default value is “200”.
+   Default value is "200".
 
    Example 1.13. Set db_fetch_rows parameter
 ...
@@ -621,7 +621,7 @@ modparam("dialog", "db_fetch_rows", 500)
    If you want to store the information about the dialogs in a database a
    table name must be specified.
 
-   Default value is “dialog”.
+   Default value is "dialog".
 
    Example 1.14. Set table_name parameter
 ...
@@ -632,7 +632,7 @@ modparam("dialog", "table_name", "my_dialog")
 
    The column name in the database to store the dialogs' callid.
 
-   Default value is “callid”.
+   Default value is "callid".
 
    Example 1.15. Set callid_column parameter
 ...
@@ -643,7 +643,7 @@ modparam("dialog", "callid_column", "callid_c_name")
 
    The column name in the database to store the caller's sip address.
 
-   Default value is “from_uri”.
+   Default value is "from_uri".
 
    Example 1.16. Set from_uri_column parameter
 ...
@@ -655,7 +655,7 @@ modparam("dialog", "from_uri_column", "from_uri_c_name")
    The column name in the database to store the From tag from the INVITE
    request.
 
-   Default value is “from_tag”.
+   Default value is "from_tag".
 
    Example 1.17. Set from_tag_column parameter
 ...
@@ -666,7 +666,7 @@ modparam("dialog", "from_tag_column", "from_tag_c_name")
 
    The column name in the database to store the callee's sip address.
 
-   Default value is “to_uri”.
+   Default value is "to_uri".
 
    Example 1.18. Set to_uri_column parameter
 ...
@@ -678,7 +678,7 @@ modparam("dialog", "to_uri_column", "to_uri_c_name")
    The column name in the database to store the To tag from the 200 OK
    response to the INVITE request, if present.
 
-   Default value is “to_tag”.
+   Default value is "to_tag".
 
    Example 1.19. Set to_tag_column parameter
 ...
@@ -689,7 +689,7 @@ modparam("dialog", "to_tag_column", "to_tag_c_name")
 
    The column name in the database to store the cseq from caller side.
 
-   Default value is “caller_cseq”.
+   Default value is "caller_cseq".
 
    Example 1.20. Set caller_cseq_column parameter
 ...
@@ -700,7 +700,7 @@ modparam("dialog", "caller_cseq_column", "column_name")
 
    The column name in the database to store the cseq from callee side.
 
-   Default value is “callee_cseq”.
+   Default value is "callee_cseq".
 
    Example 1.21. Set callee_cseq_column parameter
 ...
@@ -712,7 +712,7 @@ modparam("dialog", "callee_cseq_column", "column_name")
    The column name in the database to store the route records from caller
    side (proxy to caller).
 
-   Default value is “caller_route_set”.
+   Default value is "caller_route_set".
 
    Example 1.22. Set caller_route_column parameter
 ...
@@ -724,7 +724,7 @@ modparam("dialog", "caller_route_column", "column_name")
    The column name in the database to store the route records from callee
    side (proxy to callee).
 
-   Default value is “callee_route_set”.
+   Default value is "callee_route_set".
 
    Example 1.23. Set to_route_column parameter
 ...
@@ -735,7 +735,7 @@ modparam("dialog", "to_route_column", "column_name")
 
    The column name in the database to store the caller's contact uri.
 
-   Default value is “from_contact”.
+   Default value is "from_contact".
 
    Example 1.24. Set caller_contact_column parameter
 ...
@@ -746,7 +746,7 @@ modparam("dialog", "caller_contact_column", "column_name")
 
    The column name in the database to store the callee's contact uri.
 
-   Default value is “callee_contact”.
+   Default value is "callee_contact".
 
    Example 1.25. Set callee_contact_column parameter
 ...
@@ -758,7 +758,7 @@ modparam("dialog", "callee_contact_column", "column_name")
    The column name in the database to store the information about the
    local interface receiving the traffic from caller.
 
-   Default value is “caller_sock”.
+   Default value is "caller_sock".
 
    Example 1.26. Set caller_sock_column parameter
 ...
@@ -770,7 +770,7 @@ modparam("dialog", "caller_sock_column", "column_name")
    The column name in the database to store information about the local
    interface receiving the traffic from callee.
 
-   Default value is “callee_contact”.
+   Default value is "callee_contact".
 
    Example 1.27. Set callee_sock_column parameter
 ...
@@ -782,7 +782,7 @@ modparam("dialog", "callee_sock_column", "column_name")
    The column name in the database to store the dialogs' hash id
    information.
 
-   Default value is “hash_id”.
+   Default value is "hash_id".
 
    Example 1.28. Set h_id_column parameter
 ...
@@ -794,7 +794,7 @@ modparam("dialog", "h_id_column", "hash_id_c_name")
    The column name in the database to store the dialogs' hash entry
    information.
 
-   Default value is “hash_entry”.
+   Default value is "hash_entry".
 
    Example 1.29. Set h_entry_column parameter
 ...
@@ -806,7 +806,7 @@ modparam("dialog", "h_entry_column", "h_entry_c_name")
    The column name in the database to store the dialogs' state
    information.
 
-   Default value is “state”.
+   Default value is "state".
 
    Example 1.30. Set state_column parameter
 ...
@@ -818,7 +818,7 @@ modparam("dialog", "state_column", "state_c_name")
    The column name in the database to store the dialogs' start time
    information.
 
-   Default value is “start_time”.
+   Default value is "start_time".
 
    Example 1.31. Set start_time_column parameter
 ...
@@ -829,7 +829,7 @@ modparam("dialog", "start_time_column", "start_time_c_name")
 
    The column name in the database to store the dialogs' timeout.
 
-   Default value is “timeout”.
+   Default value is "timeout".
 
    Example 1.32. Set timeout_column parameter
 ...
@@ -840,7 +840,7 @@ modparam("dialog", "timeout_column", "timeout_c_name")
 
    The column name in the database to store the script flags.
 
-   Default value is “sflags”.
+   Default value is "sflags".
 
    Example 1.33. Set sflags_column parameter
 ...
@@ -852,7 +852,7 @@ modparam("dialog", "sflags_column", "s_flags")
    The column name in the database to store the index of the route to be
    executed at timeout.
 
-   Default value is “toroute”.
+   Default value is "toroute".
 
    Example 1.34. Set toroute_column parameter
 ...
@@ -864,7 +864,7 @@ modparam("dialog", "toroute_column", "timeout_route")
    If you want to store the variables for a dialog in a database a table
    name must be specified.
 
-   Default value is “dialog_vars”.
+   Default value is "dialog_vars".
 
    Example 1.35. Set vars_table_name parameter
 ...
@@ -876,7 +876,7 @@ modparam("dialog", "vars_table_name", "my_dialog_vars")
    The column name in the database to store the dialogs' hash id
    information (as a reference to the dialog table).
 
-   Default value is “hash_id”.
+   Default value is "hash_id".
 
    Example 1.36. Set vars_h_id_column parameter
 ...
@@ -888,7 +888,7 @@ modparam("dialog", "vars_h_id_column", "vars_h_id_name")
    The column name in the database to store the dialogs' hash entry
    information (as a reference to the dialog table).
 
-   Default value is “hash_entry”.
+   Default value is "hash_entry".
 
    Example 1.37. Set vars_h_entry_column parameter
 ...
@@ -899,7 +899,7 @@ modparam("dialog", "vars_h_entry_column", "vars_h_entry_name")
 
    The column name in the database to store the keys of a variable.
 
-   Default value is “dialog_key”.
+   Default value is "dialog_key".
 
    Example 1.38. Set vars_key_column parameter
 ...
@@ -910,7 +910,7 @@ modparam("dialog", "vars_key_column", "vars_key_name")
 
    The column name in the database to store the keys of a variable.
 
-   Default value is “dialog_value”.
+   Default value is "dialog_value".
 
    Example 1.39. Set vars_value_column parameter
 ...
@@ -921,7 +921,7 @@ modparam("dialog", "vars_value_column", "vars_value_name")
 
    List of names for profiles with values.
 
-   Default value is “empty”.
+   Default value is "empty".
 
    Example 1.40. Set profiles_with_value parameter
 ...
@@ -932,7 +932,7 @@ modparam("dialog", "profiles_with_value", "caller ; my_profile")
 
    List of names for profiles without values.
 
-   Default value is “empty”.
+   Default value is "empty".
 
    Example 1.41. Set profiles_no_value parameter
 ...
@@ -943,7 +943,7 @@ modparam("dialog", "profiles_no_value", "inbound ; outbound")
 
    SIP address to be used in From header when initiating a call bridge.
 
-   Default value is “sip:controller@kamailio.org”.
+   Default value is "sip:controller@kamailio.org".
 
    Example 1.42. Set bridge_controller parameter
 ...
@@ -966,7 +966,7 @@ modparam("dialog", "bridge_controller", "sip:ctd@kamailio.org")
      * 1 - IN-SCRIPT - execute initial callbacks during script execution,
        i.e., right after dlg_manage() is called;
 
-   Default value is “1”.
+   Default value is "1".
 
    Example 1.43. Set initial_cbs_inscript parameter
 ...
@@ -989,7 +989,7 @@ modparam("dialog", "initial_cbs_inscript", "0")
    6.12. dlg_get(callid, ftag, ttag)
    6.13. is_known_dlg()
 
-6.1.  set_dlg_profile(profile,[value])
+6.1. set_dlg_profile(profile,[value])
 
    Inserts the current dialog into a profile. Note that if the profile
    does not supports values, this will be silently discarded. Also, there
@@ -1011,7 +1011,7 @@ set_dlg_profile("inbound_call");
 set_dlg_profile("caller","$fu");
 ...
 
-6.2.  unset_dlg_profile(profile,[value])
+6.2. unset_dlg_profile(profile,[value])
 
    Removes the current dialog from a profile.
 
@@ -1030,7 +1030,7 @@ unset_dlg_profile("inbound_call");
 unset_dlg_profile("caller","$fu");
 ...
 
-6.3.  is_in_profile(profile,[value])
+6.3. is_in_profile(profile,[value])
 
    Checks if the current dialog belongs to a profile. If the profile
    supports values, the check can be reinforced to take into account a
@@ -1058,7 +1058,7 @@ if (is_in_profile("caller","XX")) {
 }
 ...
 
-6.4.  get_profile_size(profile,[value],size)
+6.4. get_profile_size(profile,[value],size)
 
    Returns the number of dialogs belonging to a profile. If the profile
    supports values, the check can be reinforced to take into account a
@@ -1085,7 +1085,7 @@ if(get_profile_size("caller","$fu","$avp(size)"))
     xlog("currently, the user $fu has $avp(size) active outgoing calls\n");
 ...
 
-6.5.  dlg_isflagset(flag)
+6.5. dlg_isflagset(flag)
 
    Check if the dialog flag is set or not.
 
@@ -1103,7 +1103,7 @@ if(dlg_isflagset("1"))
 }
 ...
 
-6.6.  dlg_setflag(flag)
+6.6. dlg_setflag(flag)
 
    Set the dialog flag.
 
@@ -1118,7 +1118,7 @@ if(dlg_isflagset("1"))
 dlg_setflag("1");
 ...
 
-6.7.  dlg_resetflag(flag)
+6.7. dlg_resetflag(flag)
 
    Reset the dialog flag.
 
@@ -1133,7 +1133,7 @@ dlg_setflag("1");
 redlg_setflag("1");
 ...
 
-6.8.  dlg_bye(side)
+6.8. dlg_bye(side)
 
    Send BYE to parties in dialog.
 
@@ -1149,7 +1149,7 @@ redlg_setflag("1");
 dlg_bye("all");
 ...
 
-6.9.  dlg_refer(side, address)
+6.9. dlg_refer(side, address)
 
    Refer the 'side' to a new SIP 'address'.
 
@@ -1165,7 +1165,7 @@ dlg_bye("all");
 dlg_refer("caller", "sip:annoucement@kamailio.org");
 ...
 
-6.10.  dlg_manage()
+6.10. dlg_manage()
 
    Process current SIP request with dialog module. It is alternative to
    setting dialog flag for initial INVITE and Route-parameter-callback
@@ -1173,6 +1173,13 @@ dlg_refer("caller", "sip:annoucement@kamailio.org");
 
    This function can be used from REQUEST_ROUTE.
 
+   IMPORTANT: Users of this function should make sure that the dialog
+   created is further processed statefully. Specifically, if a stateless
+   response is sent out after dlg_manage() is called, the dialog cannot be
+   handled properly. So make sure that a transaction exists or create it
+   explicitly using the tm module. This is a shortcoming of the current
+   implementation that may be resolved in a future version hopefully.
+
    Example 1.53. dlg_manage usage
 ...
 modparam("dialog", "default_timeout", 100)
@@ -1189,13 +1196,13 @@ route {
 }
 ...
 
-6.11.  dlg_bridge(from, to, op)
+6.11. dlg_bridge(from, to, op)
 
    Bridge 'from' SIP address to 'to' SIP address via outbound proxy 'op'.
 
    Meaning of the parameters is as follows:
      * from - SIP address of first side to call.
-     * to - SIP address to refer “from” to.
+     * to - SIP address to refer "from" to.
      * op - outbound proxy SIP address.
 
    This function can be used from BRANCH_ROUTE, REQUEST_ROUTE,
@@ -1207,7 +1214,7 @@ dlg_bridge("sip:user@kamailio.org", "sip:annoucement@kamailio.org",
    "sip:kamailio.org:5080");
 ...
 
-6.12.  dlg_get(callid, ftag, ttag)
+6.12. dlg_get(callid, ftag, ttag)
 
    Search and set current dialog based on Call-ID, From-Tag and To-Tag
    parameters.
@@ -1228,7 +1235,7 @@ if(dlg_get("abcdef", "123", "456"))
 }
 ...
 
-6.13.  is_known_dlg()
+6.13. is_known_dlg()
 
    This function checks if the current SIP message being processed belongs
    to any transaction within an active dialog that the dialog module is
@@ -1317,12 +1324,12 @@ if(!uri == myself) {
 
 8.2. dlg_list_ctx
 
-   The same as the “dlg_list” but including in the dialog description the
+   The same as the "dlg_list" but including in the dialog description the
    associated context from modules sitting on top of the dialog module.
 
    Name: dlg_list_ctx
 
-   Parameters: see “dlg_list”
+   Parameters: see "dlg_list"
 
    MI FIFO Command Format:
                 :dlg_list_ctx:_reply_fifo_file_
@@ -1455,7 +1462,7 @@ if(!uri == myself) {
 
 9.2. dlg.list_ctx
 
-   The same as the “dlg_list” but including in the dialog description the
+   The same as the "dlg_list" but including in the dialog description the
    associated context from modules sitting on top of the dialog module.
 
    Name: dlg.list_ctx
@@ -1480,13 +1487,13 @@ if(!uri == myself) {
 
 9.4. dlg.dlg_list_ctx
 
-   The same as the “dlg.list_ctx” but including in the dialog description
+   The same as the "dlg.list_ctx" but including in the dialog description
    the associated context from modules sitting on top of the dialog
    module.
 
    Name: dlg.dlg_list_ctx
 
-   Parameters: see “dlg_list”
+   Parameters: see "dlg_list"
 
    RPC Command Format:
                 serctl dlg.list_ctx abcdrssfrs122444@192.168.1.1 AAdfeEFF33
@@ -1625,7 +1632,7 @@ Chapter 2. Developer Guide
    1.1. register_dlgcb (dialog, type, cb, param, free_param_cb)
    1.2. terminate_dlg (dlg, hdrs)
 
-1.1.  register_dlgcb (dialog, type, cb, param, free_param_cb)
+1.1. register_dlgcb (dialog, type, cb, param, free_param_cb)
 
    Register a new callback to the dialog.
 
@@ -1667,14 +1674,14 @@ Chapter 2. Developer Guide
           + DLGCB_SPIRALED - called when the dialog matches a spiraling
             request - it's a per dialog type.
           + DLGCB_DESTROY
-     * dialog_cb cb - callback function to be called. Prototype is: void
+     * dialog_cb cb - callback function to be called. Prototype is: "void
        (dialog_cb) (struct dlg_cell* dlg, int type, struct dlg_cb_params *
-       params); 
+       params); "
      * void *param - parameter to be passed to the callback function.
      * param_free callback_param_free - callback function to be called to
-       free the param. Prototype is: “void (param_free_cb) (void *param);”
+       free the param. Prototype is: "void (param_free_cb) (void *param);"
 
-1.2.  terminate_dlg (dlg, hdrs)
+1.2. terminate_dlg (dlg, hdrs)
 
    Terminate a Dialog
 
@@ -1685,46 +1692,46 @@ Chapter 2. Developer Guide
 
 Chapter 3. Frequently Asked Questions
 
-   3.1. What happend with “use_tight_match” parameter?
+   3.1. What happend with "use_tight_match" parameter?
    3.2. Where can I find more about Kamailio?
    3.3. Where can I post a question about this module?
    3.4. How can I report a bug?
 
    3.1.
 
-       What happend with “use_tight_match” parameter?
+   What happend with "use_tight_match" parameter?
 
-       The parameter was removed with version 1.3 as the option of tight
-       matching became mandatory and not configurable. Now, the tight matching
-       is done all the time (when using DID matching).
+   The parameter was removed with version 1.3 as the option of tight
+   matching became mandatory and not configurable. Now, the tight matching
+   is done all the time (when using DID matching).
 
    3.2.
 
-       Where can I find more about Kamailio?
+   Where can I find more about Kamailio?
 
-       Take a look at http://www.kamailio.org/.
+   Take a look at http://www.kamailio.org/.
 
    3.3.
 
-       Where can I post a question about this module?
+   Where can I post a question about this module?
 
-       First at all check if your question was already answered on one of our
-       mailing lists:
-         * User Mailing List -
-           http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-users
-         * Developer Mailing List -
-           http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev
+   First at all check if your question was already answered on one of our
+   mailing lists:
+     * User Mailing List -
+       http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-users
+     * Developer Mailing List -
+       http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev
 
-       E-mails regarding any stable Kamailio release should be sent to
-       <sr-users@lists.sip-router.org> and e-mails regarding development
-       versions should be sent to <sr-dev@lists.sip-router.org>.
+   E-mails regarding any stable Kamailio release should be sent to
+   <sr-users@lists.sip-router.org> and e-mails regarding development
+   versions should be sent to <sr-dev@lists.sip-router.org>.
 
-       If you want to keep the mail private, send it to
-       <sr-users@lists.sip-router.org>.
+   If you want to keep the mail private, send it to
+   <sr-users@lists.sip-router.org>.
 
    3.4.
 
-       How can I report a bug?
+   How can I report a bug?
 
-       Please follow the guidelines provided at:
-       http://sip-router.org/tracker.
+   Please follow the guidelines provided at:
+   http://sip-router.org/tracker.
index 7d8f239..14e8d81 100644 (file)
@@ -507,7 +507,8 @@ static int mod_init(void)
        }
 
        if (initial_cbs_inscript != 0 && initial_cbs_inscript != 1) {
-               LM_ERR("invalid parameter for running initial callbacks in-script (must be either 0 or 1)\n");
+               LM_ERR("invalid parameter for running initial callbacks in-script"
+                               " (must be either 0 or 1)\n");
                return -1;
        }
 
index c0b8fbc..5e0a7a0 100644 (file)
@@ -526,8 +526,10 @@ static int load_dialog_vars_from_db(int fetch_num_rows)
                                dlg = (d_table->entries)[VAL_INT(values)].first;
                                while (dlg) {
                                        if (dlg->h_id == VAL_INT(values+1)) {
-                                               set_dlg_variable_unsafe(dlg, &VAL_STR(values+2), &VAL_STR(values+3));
-                                               continue;
+                                               str key = { VAL_STR(values+2).s, strlen(VAL_STRING(values+2)) };
+                                               str value = { VAL_STR(values+3).s, strlen(VAL_STRING(values+3)) };
+                                               set_dlg_variable_unsafe(dlg, &key, &value);
+                                               break;
                                        }
                                        dlg = dlg->next;
                                        if (!dlg) {
index ccc95f1..490501b 100644 (file)
@@ -106,6 +106,8 @@ static unsigned int CURR_DLG_ID  = 0xffffffff;      /*!< current dialog id */
 /*! separator inside the record-route paramter */
 #define DLG_SEPARATOR      '.'
 
+int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg,
+               int mode);
 
 /*!
  * \brief Initialize the dialog handlers
@@ -609,19 +611,28 @@ static inline int pre_match_parse( struct sip_msg *req, str *callid,
  */
 void dlg_onreq(struct cell* t, int type, struct tmcb_params *param)
 {
-       struct sip_msg *req = param->req;
+       sip_msg_t *req = param->req;
 
-       if (!initial_cbs_inscript) {
-               if (spiral_detected == 1)
-                       run_dlg_callbacks( DLGCB_SPIRALED, current_dlg_pointer, req, NULL, DLG_DIR_DOWNSTREAM, 0);
-               else if (spiral_detected == 0)
-                       run_create_callbacks( current_dlg_pointer, req);
-       }
-       if((req->flags&dlg_flag)!=dlg_flag)
-               return;
-       if (current_dlg_pointer!=NULL)
+       if(req->first_line.u.request.method_value != METHOD_INVITE)
                return;
-       dlg_new_dialog(req, t, 1);
+
+       if (current_dlg_pointer!=NULL) {
+               if (!initial_cbs_inscript) {
+                       if (spiral_detected == 1)
+                               run_dlg_callbacks( DLGCB_SPIRALED, current_dlg_pointer,
+                                               req, NULL, DLG_DIR_DOWNSTREAM, 0);
+                       else if (spiral_detected == 0)
+                               run_create_callbacks( current_dlg_pointer, req);
+               }
+       }
+       if (current_dlg_pointer==NULL) {
+               if((req->flags&dlg_flag)!=dlg_flag)
+                       return;
+               dlg_new_dialog(req, t, 1);
+       }
+       if (current_dlg_pointer!=NULL) {
+               dlg_set_tm_callbacks(t, req, current_dlg_pointer, spiral_detected);
+       }
 }
 
 
@@ -782,9 +793,10 @@ int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cb
             spiral_detected = 1;
 
             if (run_initial_cbs)
-                run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0);
-            // get_dlg has incremented the ref count by 1
-            unref_dlg(dlg, 1);
+                run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL,
+                                               DLG_DIR_DOWNSTREAM, 0);
+            /* get_dlg() has incremented the ref count by 1
+                        * - it's ok, dlg will be used to set current_dialog_pointer */
             goto finish;
         }
     }
@@ -811,12 +823,20 @@ int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cb
                return -1;
        }
 
-
        /* Populate initial varlist: */
        dlg->vars = get_local_varlist_pointer(req, 1);
 
        link_dlg(dlg,0);
 
+       dlg->lifetime = get_dlg_timeout(req);
+       s.s   = _dlg_ctx.to_route_name;
+       s.len = strlen(s.s);
+       dlg_set_toroute(dlg, &s);
+       dlg->sflags |= _dlg_ctx.flags;
+
+       if (_dlg_ctx.to_bye!=0)
+               dlg->dflags |= DLG_FLAG_TOBYE;
+
     if (run_initial_cbs)  run_create_callbacks( dlg, req);
 
        /* first INVITE seen (dialog created, unconfirmed) */
@@ -826,27 +846,46 @@ int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cb
                goto error;
        }
 
-       if ( d_tmb.register_tmcb( req, t,
-                               TMCB_RESPONSE_READY|TMCB_RESPONSE_FWDED,
-                               dlg_onreply, (void*)dlg, unref_new_dialog)<0 ) {
-               LM_ERR("failed to register TMCB\n");
-               goto error;
-       }
-    // increase reference counter because of registered callback
+       /* reference it once for current_dialog_pointer */
     ref_dlg(dlg, 1);
 
-       dlg->lifetime = get_dlg_timeout(req);
-       s.s   = _dlg_ctx.to_route_name;
-       s.len = strlen(s.s);
-       dlg_set_toroute(dlg, &s);
-       dlg->sflags |= _dlg_ctx.flags;
-
-       if (_dlg_ctx.to_bye!=0)
-               dlg->dflags |= DLG_FLAG_TOBYE;
-
     if_update_stat( dlg_enable_stats, processed_dlgs, 1);
 
 finish:
+    set_current_dialog(req, dlg);
+    _dlg_ctx.dlg = dlg;
+
+       return 0;
+
+error:
+       if (!spiral_detected)
+               unref_dlg(dlg,1);               // undo ref regarding linking
+       return -1;
+}
+
+
+/*!
+ * \brief add dlg structure to tm callbacks
+ * \param t current transaction
+ * \param req current sip request
+ * \param dlg current dialog
+ * \param smode if the sip request was spiraled
+ * \return 0 on success, -1 on failure
+ */
+int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg,
+               int smode)
+{
+       if(smode==0) {
+               if ( d_tmb.register_tmcb( req, t,
+                               TMCB_RESPONSE_READY|TMCB_RESPONSE_FWDED,
+                               dlg_onreply, (void*)dlg, unref_new_dialog)<0 ) {
+                       LM_ERR("failed to register TMCB\n");
+                       goto error;
+               }
+               // increase reference counter because of registered callback
+               ref_dlg(dlg, 1);
+       }
+
        if (t) {
                // transaction exists ==> keep ref counter large enough to
                // avoid premature cleanup and ensure proper dialog referencing
@@ -854,9 +893,7 @@ finish:
                        LM_ERR("failed to store dialog in transaction\n");
                        goto error;
                }
-       }
-       else
-       {
+       } else {
                // no transaction exists ==> postpone work until we see the
                // request being forwarded statefully
         if ( d_tmb.register_tmcb( req, NULL, TMCB_REQUEST_FWDED,
@@ -865,16 +902,10 @@ finish:
                        goto error;
         }
        }
-
-    set_current_dialog(req, dlg);
-    _dlg_ctx.dlg = dlg;
-    ref_dlg(dlg, 1);
+       dlg->dflags |= DLG_FLAG_TM;
 
        return 0;
-
 error:
-       if (!spiral_detected)
-               unref_dlg(dlg,1);               // undo ref regarding linking
        return -1;
 }
 
index ecb6526..9a8854c 100644 (file)
@@ -79,6 +79,8 @@
 /* dialog-variable flags (in addition to dialog-flags) */
 #define DLG_FLAG_DEL           (1<<8) /*!< delete this var */
 
+#define DLG_FLAG_TM            (1<<9) /*!< dialog is set in transaction */
+
 #define DLG_CALLER_LEG         0 /*!< attribute that belongs to a caller leg */
 #define DLG_CALLEE_LEG         1 /*!< attribute that belongs to a callee leg */
 
index 2e69291..bb20c57 100644 (file)
@@ -312,7 +312,12 @@ int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param )
 {
        current_dlg_msg_id = 0;
        if (current_dlg_pointer) {
-               unref_dlg( current_dlg_pointer, 1);
+               if(current_dlg_pointer->dflags & DLG_FLAG_TM) {
+                       unref_dlg( current_dlg_pointer, 1);
+               } else {
+                       /* dialog didn't make it to tm */
+                       unref_dlg( current_dlg_pointer, 2);
+               }
                current_dlg_pointer = NULL;
        }
        if (current_pending_linkers) {
index 310b674..279e719 100644 (file)
@@ -252,7 +252,7 @@ int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val)
     if(ret!= 0)
         goto done;
 
-    dlg->dflags &= DLG_FLAG_CHANGED_VARS;
+    dlg->dflags |= DLG_FLAG_CHANGED_VARS;
     dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
     if ( dlg_db_mode==DB_MODE_REALTIME )
         update_dialog_dbinfo(dlg);
@@ -348,7 +348,7 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value
        }
        /* unlock dialog */
        if (dlg) {
-               dlg->dflags &= DLG_FLAG_CHANGED_VARS;           
+               dlg->dflags |= DLG_FLAG_CHANGED_VARS;
                dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
                if ( dlg_db_mode==DB_MODE_REALTIME )
                        update_dialog_dbinfo(dlg);
index 9bcbea1..2fccc78 100644 (file)
@@ -1434,15 +1434,6 @@ dlg_refer("caller", "sip:annoucement@kamailio.org");
                <para>
                This function can be used from REQUEST_ROUTE.
                </para>
-               <para>
-               <b>IMPORTANT</b>: Users of this function should make sure that the
-               dialog created is further processed statefully. Specifically, if a
-               stateless response is sent out after dlg_manage() is called, the
-               dialog cannot be handled properly. So make sure that a transaction
-               exists or create it explicitly using the tm module.<br>This is a
-               shortcoming of the current implementation that may be resolved in a
-               future version hopefully.
-               </para>
                <example>
                <title><function>dlg_manage</function> usage</title>
                <programlisting format="linespecific">
index 4ca1d19..905c49c 100644 (file)
@@ -16,11 +16,11 @@ Carsten Bock
 
    ng-voice.com
 
-   Copyright Â© 2004 FhG FOKUS
+   Copyright © 2004 FhG FOKUS
 
-   Copyright Â© 2005 Voice Sistem
+   Copyright © 2005 Voice Sistem
 
-   Copyright Â© 2010 Daniel-Constantin Mierla (asipto.com)
+   Copyright © 2010 Daniel-Constantin Mierla (asipto.com)
      __________________________________________________________________
 
    Table of Contents
@@ -69,12 +69,10 @@ Carsten Bock
               4.2. ds_select_domain(set, alg)
               4.3. ds_next_dst()
               4.4. ds_next_domain()
-              4.5. ds_mark_dst()
-              4.6. ds_mark_dst("s")
-              4.7. ds_is_from_list()
-              4.8. ds_is_from_list("group")
-              4.9. ds_load_update()
-              4.10. ds_load_unset()
+              4.5. ds_mark_dst([state])
+              4.6. ds_is_from_list([groupid])
+              4.7. ds_load_update()
+              4.8. ds_load_unset()
 
         5. MI Commands
 
@@ -102,38 +100,40 @@ Carsten Bock
 
    List of Examples
 
-   1.1. Set the “list_file” parameter
-   1.2. Set “db_url” parameter
-   1.3. Set “table_name” parameter
-   1.4. Set “setid_col” parameter
-   1.5. Set “destination_col” parameter
-   1.6. Set “flags_col” parameter
-   1.7. Set “priority_col” parameter
-   1.8. Set the “force_dst” parameter
-   1.9. Set the “flags” parameter
-   1.10. Set the “use_default” parameter
-   1.11. Set the “dst_avp” parameter
-   1.12. Set the “grp_avp” parameter
-   1.13. Set the “cnt_avp” parameter
-   1.14. Set the “dstid_avp” parameter
-   1.15. Set the “attrs_avp” parameter
+   1.1. Set the "list_file" parameter
+   1.2. Set "db_url" parameter
+   1.3. Set "table_name" parameter
+   1.4. Set "setid_col" parameter
+   1.5. Set "destination_col" parameter
+   1.6. Set "flags_col" parameter
+   1.7. Set "priority_col" parameter
+   1.8. Set the "force_dst" parameter
+   1.9. Set the "flags" parameter
+   1.10. Set the "use_default" parameter
+   1.11. Set the "dst_avp" parameter
+   1.12. Set the "grp_avp" parameter
+   1.13. Set the "cnt_avp" parameter
+   1.14. Set the "dstid_avp" parameter
+   1.15. Set the "attrs_avp" parameter
    1.16. Use $avp(i:273) for hashing:
    1.17. Use combination of PVs for hashing:
-   1.18. Set the “setid_pvar” parameter
-   1.19. Set the “ds_ping_method” parameter
-   1.20. Set the “ds_ping_from” parameter
-   1.21. Set the “ds_ping_interval” parameter
-   1.22. Set the “ds_probing_threshhold” parameter
-   1.23. Set the “ds_ping_reply_codes” parameter
-   1.24. Set the “ds_probing_mode” parameter
-   1.25. Set the “ds_hash_size” parameter
-   1.26. Set the “ds_hash_expire” parameter
-   1.27. Set the “ds_hash_initexpire” parameter
-   1.28. Set the “ds_hash_check_interval” parameter
+   1.18. Set the "setid_pvar" parameter
+   1.19. Set the "ds_ping_method" parameter
+   1.20. Set the "ds_ping_from" parameter
+   1.21. Set the "ds_ping_interval" parameter
+   1.22. Set the "ds_probing_threshhold" parameter
+   1.23. Set the "ds_ping_reply_codes" parameter
+   1.24. Set the "ds_probing_mode" parameter
+   1.25. Set the "ds_hash_size" parameter
+   1.26. Set the "ds_hash_expire" parameter
+   1.27. Set the "ds_hash_initexpire" parameter
+   1.28. Set the "ds_hash_check_interval" parameter
    1.29. ds_select_dst usage
-   1.30. ds_load_unset usage
-   1.31. dispatcher list file
-   1.32. Kamailio config script - sample dispatcher usage
+   1.30. ds_mark_dst usage
+   1.31. ds_mark_dst usage
+   1.32. ds_load_unset usage
+   1.33. dispatcher list file
+   1.34. Kamailio config script - sample dispatcher usage
 
 Chapter 1. Admin Guide
 
@@ -181,12 +181,10 @@ Chapter 1. Admin Guide
         4.2. ds_select_domain(set, alg)
         4.3. ds_next_dst()
         4.4. ds_next_domain()
-        4.5. ds_mark_dst()
-        4.6. ds_mark_dst("s")
-        4.7. ds_is_from_list()
-        4.8. ds_is_from_list("group")
-        4.9. ds_load_update()
-        4.10. ds_load_unset()
+        4.5. ds_mark_dst([state])
+        4.6. ds_is_from_list([groupid])
+        4.7. ds_load_update()
+        4.8. ds_load_unset()
 
    5. MI Commands
 
@@ -278,10 +276,10 @@ Chapter 1. Admin Guide
 
    Path to the file with destination sets.
 
-   Default value is “/etc/kamailio/dispatcher.list” or
-   “/usr/local/etc/kamailio/dispatcher.list”.
+   Default value is "/etc/kamailio/dispatcher.list" or
+   "/usr/local/etc/kamailio/dispatcher.list".
 
-   Example 1.1. Set the “list_file” parameter
+   Example 1.1. Set the "list_file" parameter
 ...
 modparam("dispatcher", "list_file", "/var/run/kamailio/dispatcher.list")
 ...
@@ -291,9 +289,9 @@ modparam("dispatcher", "list_file", "/var/run/kamailio/dispatcher.list")
    If you want to load the sets of gateways from the database you must set
    this parameter.
 
-   Default value is “NULL” (disable DB support).
+   Default value is "NULL" (disable DB support).
 
-   Example 1.2. Set “db_url” parameter
+   Example 1.2. Set "db_url" parameter
 ...
 modparam("dispatcher", "db_url", "mysql://user:passwb@localhost/database")
 ...
@@ -303,9 +301,9 @@ modparam("dispatcher", "db_url", "mysql://user:passwb@localhost/database")
    If you want to load the sets of gateways from the database you must set
    this parameter as the database name.
 
-   Default value is “dispatcher”.
+   Default value is "dispatcher".
 
-   Example 1.3. Set “table_name” parameter
+   Example 1.3. Set "table_name" parameter
 ...
 modparam("dispatcher", "table_name", "my_dispatcher")
 ...
@@ -314,9 +312,9 @@ modparam("dispatcher", "table_name", "my_dispatcher")
 
    The column's name in the database storing the gateway's group id.
 
-   Default value is “setid”.
+   Default value is "setid".
 
-   Example 1.4. Set “setid_col” parameter
+   Example 1.4. Set "setid_col" parameter
 ...
 modparam("dispatcher", "setid_col", "groupid")
 ...
@@ -325,9 +323,9 @@ modparam("dispatcher", "setid_col", "groupid")
 
    The column's name in the database storing the destination's sip uri.
 
-   Default value is “destination”.
+   Default value is "destination".
 
-   Example 1.5. Set “destination_col” parameter
+   Example 1.5. Set "destination_col" parameter
 ...
 modparam("dispatcher", "destination_col", "uri")
 ...
@@ -337,9 +335,9 @@ modparam("dispatcher", "destination_col", "uri")
    The column's name in the database storing the flags for destination
    uri.
 
-   Default value is “flags”.
+   Default value is "flags".
 
-   Example 1.6. Set “flags_col” parameter
+   Example 1.6. Set "flags_col" parameter
 ...
 modparam("dispatcher", "flags_col", "dstflags")
 ...
@@ -349,9 +347,9 @@ modparam("dispatcher", "flags_col", "dstflags")
    The column's name in the database storing the priority for destination
    uri.
 
-   Default value is “priority”.
+   Default value is "priority".
 
-   Example 1.7. Set “priority_col” parameter
+   Example 1.7. Set "priority_col" parameter
 ...
 modparam("dispatcher", "priority_col", "dstpriority")
 ...
@@ -362,9 +360,9 @@ modparam("dispatcher", "priority_col", "dstpriority")
    when that is already set. If set to 0, will return error when the
    destination address is already set.
 
-   Default value is “1”.
+   Default value is "1".
 
-   Example 1.8. Set the “force_dst” parameter
+   Example 1.8. Set the "force_dst" parameter
 ...
 modparam("dispatcher", "force_dst", 1)
 ...
@@ -383,9 +381,9 @@ modparam("dispatcher", "force_dst", 1)
    destination set in AVP, and use these AVPs to contact next address when
    the current-tried fails.
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.9. Set the “flags” parameter
+   Example 1.9. Set the "flags" parameter
  ...
  modparam("dispatcher", "flags", 3)
  ...
@@ -397,9 +395,9 @@ modparam("dispatcher", "force_dst", 1)
    wanting to send the call to an anouncement server saying: "the gateways
    are full, try later".
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.10. Set the “use_default” parameter
+   Example 1.10. Set the "use_default" parameter
  ...
  modparam("dispatcher", "use_default", 1)
  ...
@@ -417,9 +415,9 @@ Note
 
    You must set this parameter if you want to do load balancing fail over.
 
-   Default value is “null” - don't add AVPs.
+   Default value is "null" - don't add AVPs.
 
-   Example 1.11. Set the “dst_avp” parameter
+   Example 1.11. Set the "dst_avp" parameter
  ...
  modparam("dispatcher", "dst_avp", "$avp(dsdst)")
  ...
@@ -433,9 +431,9 @@ Note
 
    You must set this parameter if you want to do load balancing fail over.
 
-   Default value is “null” - don't add AVP.
+   Default value is "null" - don't add AVP.
 
-   Example 1.12. Set the “grp_avp” parameter
+   Example 1.12. Set the "grp_avp" parameter
  ...
  modparam("dispatcher", "grp_avp", "$avp(dsgrp)")
  ...
@@ -449,9 +447,9 @@ Note
 
    You must set this parameter if you want to do load balancing fail over.
 
-   Default value is “null” - don't add AVP.
+   Default value is "null" - don't add AVP.
 
-   Example 1.13. Set the “cnt_avp” parameter
+   Example 1.13. Set the "cnt_avp" parameter
  ...
  modparam("dispatcher", "cnt_avp", "$avp(dscnt)")
  ...
@@ -466,9 +464,9 @@ Note
    You must set this parameter if you want to do load balancing on call
    load (alg 10).
 
-   Default value is “null” - don't add AVP.
+   Default value is "null" - don't add AVP.
 
-   Example 1.14. Set the “dstid_avp” parameter
+   Example 1.14. Set the "dstid_avp" parameter
  ...
  modparam("dispatcher", "dstid_avp", "$avp(dsdstid)")
  ...
@@ -479,9 +477,9 @@ Note
 
 Note
 
-   Default value is “null” - don't add AVP.
+   Default value is "null" - don't add AVP.
 
-   Example 1.15. Set the “attrs_avp” parameter
+   Example 1.15. Set the "attrs_avp" parameter
  ...
  modparam("dispatcher", "attrs_avp", "$avp(dsattrs)")
  ...
@@ -495,7 +493,7 @@ Note
    You must set this parameter if you want do hashing over custom message
    parts.
 
-   Default value is “null” - disabled.
+   Default value is "null" - disabled.
 
    Example 1.16. Use $avp(i:273) for hashing:
  ...
@@ -512,9 +510,9 @@ Note
    The name of the PV where to store the set ID (group ID) when calling
    ds_is_from_list() with no parameter.
 
-   Default value is “null” - don't set PV.
+   Default value is "null" - don't set PV.
 
-   Example 1.18. Set the “setid_pvar” parameter
+   Example 1.18. Set the "setid_pvar" parameter
  ...
  modparam("dispatcher", "setid_pvar", "$var(setid)")
  ...
@@ -525,9 +523,9 @@ Note
    the gateways. Pinging gateways feature depends on ds_ping_interval
    parameter.
 
-   Default value is “OPTIONS”.
+   Default value is "OPTIONS".
 
-   Example 1.19. Set the “ds_ping_method” parameter
+   Example 1.19. Set the "ds_ping_method" parameter
  ...
  modparam("dispatcher", "ds_ping_method", "INFO")
  ...
@@ -538,9 +536,9 @@ Note
    to the failed gateways. This method is only available, if compiled with
    the probing of failed gateways enabled.
 
-   Default value is “sip:dispatcher@localhost”.
+   Default value is "sip:dispatcher@localhost".
 
-   Example 1.20. Set the “ds_ping_from” parameter
+   Example 1.20. Set the "ds_ping_from" parameter
  ...
  modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com")
  ...
@@ -550,25 +548,27 @@ Note
    With this parameter you can define the interval for sending a request
    to a gateway marked as inactive upon a failed request routing to it.
    This parameter is only used, when the TM-Module is loaded. If set to
-   “0”, the pinging of inactive gateway is disabled.
+   "0", the pinging of inactive gateway is disabled.
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.21. Set the “ds_ping_interval” parameter
+   Example 1.21. Set the "ds_ping_interval" parameter
  ...
  modparam("dispatcher", "ds_ping_interval", 30)
  ...
 
 3.21. ds_probing_threshhold (int)
 
-   If you want to set a gateway into probing mode, you will need a
-   specific number of requests until it will change from "active" to
-   probing. The number of attempts can be set with this parameter. This
-   parameter can be modified via ser config framework.
+   If you want to set a gateway into inactive mode, there can be a
+   specific number of failed requests until it will change from "active"
+   to "inactive". It is using the state "trying", that allows selection of
+   gateway but indicates there was a failure previously with the gateway.
+   The number of attempts can be set with this parameter. This parameter
+   can be modified via ser config framework.
 
-   Default value is “3”.
+   Default value is "1" (set inactive with first failure).
 
-   Example 1.22. Set the “ds_probing_threshhold” parameter
+   Example 1.22. Set the "ds_probing_threshhold" parameter
  ...
  modparam("dispatcher", "ds_probing_threshhold", 10)
  ...
@@ -583,9 +583,9 @@ Note
    valid response). This parameter can be modified via ser config
    framework.
 
-   Default value is “” (only 200 OK is accepted).
+   Default value is "" (only 200 OK is accepted).
 
-   Example 1.23. Set the “ds_ping_reply_codes” parameter
+   Example 1.23. Set the "ds_ping_reply_codes" parameter
  ...
  modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=403;code=488;class=
 3")
@@ -594,13 +594,14 @@ Note
 3.23. ds_probing_mode (int)
 
    Controls what gateways are tested to see if they are reachable. If set
-   to 0, only the gateways with state PROBING are tested, if set to 1, all
-   gateways are tested. If set to 1 and the response is 408 (timeout), an
-   active gateway is set to PROBING state.
+   to 0, only the gateways with state PROBING are tested; if set to 1, all
+   gateways are tested; if set to 2, only gateways in inactive state with
+   probing mode set are tested. If set to 1 and there is a failure of
+   keepalive to an active gateway, then it is set to TRYING state.
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.24. Set the “ds_probing_mode” parameter
+   Example 1.24. Set the "ds_probing_mode" parameter
  ...
  modparam("dispatcher", "ds_probing_mode", 1)
  ...
@@ -612,9 +613,9 @@ Note
    a hash table with 256 slots). It must be greater than 0 to enable call
    load dispatching feature (alg 10).
 
-   Default value is “0”.
+   Default value is "0".
 
-   Example 1.25. Set the “ds_hash_size” parameter
+   Example 1.25. Set the "ds_hash_size" parameter
  ...
  modparam("dispatcher", "ds_hash_size", 9)
  ...
@@ -624,9 +625,9 @@ Note
    Expiration time in seconds to remove the load on a destination if no
    BYE was received meanwhile.
 
-   Default value is “7200”.
+   Default value is "7200".
 
-   Example 1.26. Set the “ds_hash_expire” parameter
+   Example 1.26. Set the "ds_hash_expire" parameter
  ...
  modparam("dispatcher", "ds_hash_expire", 3600)
  ...
@@ -637,9 +638,9 @@ Note
    200 for INVITE was received meanwhile and state updated with
    ds_load_update().
 
-   Default value is “7200”.
+   Default value is "7200".
 
-   Example 1.27. Set the “ds_hash_initexpire” parameter
+   Example 1.27. Set the "ds_hash_initexpire" parameter
  ...
  modparam("dispatcher", "ds_hash_initexpire", 60)
  ...
@@ -649,9 +650,9 @@ Note
    Time interval in seconds to scan internal hash table with call load
    dispatching data for expired items.
 
-   Default value is “30”.
+   Default value is "30".
 
-   Example 1.28. Set the “ds_hash_check_interval” parameter
+   Example 1.28. Set the "ds_hash_check_interval" parameter
  ...
  modparam("dispatcher", "ds_hash_check_interval", 60)
  ...
@@ -662,14 +663,12 @@ Note
    4.2. ds_select_domain(set, alg)
    4.3. ds_next_dst()
    4.4. ds_next_domain()
-   4.5. ds_mark_dst()
-   4.6. ds_mark_dst("s")
-   4.7. ds_is_from_list()
-   4.8. ds_is_from_list("group")
-   4.9. ds_load_update()
-   4.10. ds_load_unset()
+   4.5. ds_mark_dst([state])
+   4.6. ds_is_from_list([groupid])
+   4.7. ds_load_update()
+   4.8. ds_load_unset()
 
-4.1.  ds_select_dst(set, alg)
+4.1. ds_select_dst(set, alg)
 
    The method selects a destination from addresses set.
 
@@ -679,21 +678,21 @@ Note
        be an integer or a variable holding an interger.
      * alg - the algorithm used to select the destination address. The
        parameter can be an integer or a variable holding an interger.
-          + “0” - hash over callid
-          + “1” - hash over from uri.
-          + “2” - hash over to uri.
-          + “3” - hash over request-uri.
-          + “4” - round-robin (next destination).
-          + “5” - hash over authorization-username (Proxy-Authorization or
+          + "0" - hash over callid
+          + "1" - hash over from uri.
+          + "2" - hash over to uri.
+          + "3" - hash over request-uri.
+          + "4" - round-robin (next destination).
+          + "5" - hash over authorization-username (Proxy-Authorization or
             "normal" authorization). If no username is found, round robin
             is used.
-          + “6” - random (using rand()).
-          + “7” - hash over the content of PVs string. Note: This works
+          + "6" - random (using rand()).
+          + "7" - hash over the content of PVs string. Note: This works
             only when the parameter hash_pvar is set.
-          + “8” - use first destination (good for failover).
-          + “9” - use weight based load distribution. You have to set the
+          + "8" - use first destination (good for failover).
+          + "9" - use weight based load distribution. You have to set the
             attribute 'weight' per each address in destination set.
-          + “10” - use call load distribution. You have to set the
+          + "10" - use call load distribution. You have to set the
             attribute 'duid' (as an unique string id) per each address in
             destination set. Also, you must set parameters 'dstid_avp' and
             'ds_hash_size'.
@@ -708,7 +707,7 @@ Note
             on each address can change.
             This algorithm can be used only for dispatching INVITE
             requests as it is the only SIP method creating a SIP call.
-          + “X” - if the algorithm is not implemented, the first entry in
+          + "X" - if the algorithm is not implemented, the first entry in
             set is chosen.
 
    If the bit 2 in 'flags' is set, the rest of the addresses from the
@@ -726,7 +725,7 @@ $var(a) = 4;
 ds_select_dst("1", "$var(a)");
 ...
 
-4.2.  ds_select_domain(set, alg)
+4.2. ds_select_domain(set, alg)
 
    The method selects a destination from addresses set and rewrites the
    host and port from R-URI. The parameters have same meaning as for
@@ -739,65 +738,76 @@ ds_select_dst("1", "$var(a)");
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-4.3.  ds_next_dst()
+4.3. ds_next_dst()
 
    Takes the next destination address from the AVPs with id 'dst_avp_id'
    and sets the dst_uri (outbound proxy address).
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-4.4.  ds_next_domain()
+4.4. ds_next_domain()
 
    Takes the next destination address from the AVPs with id 'dst_avp_id'
    and sets the domain part of the request uri.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-4.5.  ds_mark_dst()
-
-   Mark the last used address from destination set as inactive, in order
-   to be ingnored in the future. In this way it can be implemented an
-   automatic detection of failed gateways. When an address is marked as
-   inactive, it will be ignored by 'ds_select_dst' and 'ds_select_domain'.
+4.5. ds_mark_dst([state])
+
+   Mark the last used address from destination set as inactive ("i"/"I"),
+   active ("a"/"A"), disabled ("d"/"D") or trying ("t"/"T"). Apart of
+   disabled state, a destination can be set in probing mode by adding
+   ("p"/"P") flag. With this function, an automatic detection of failed
+   gateways can be implemented. When an address is marked as inactive or
+   disabled, it will be ignored by 'ds_select_dst' and 'ds_select_domain'.
+
+   The parameter state is optional, when it is missing, then the
+   destination will be marked inactive (i.e., same as 'i').
+
+   Possible values for state parameter:
+     * "a" or "A" - the last destination should be set to active and the
+       error-counter should set to "0".
+     * "i" or "I" - the last destination should be set to inactive and
+       will be ignored in future requests.
+     * "t" or "T" - the last destination should be set to temporary trying
+       state and failure counter is incremented. When the failure counter
+       reaches the threshold, the destination will be set inactive.
+     * "p" and "P" - this has to be used in addition to one of the
+       previous flags - the last destination will be set to probing. This
+       mean the destination will be pinged with SIP OPTIONS requests from
+       time to time to detect if it is up running or down.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-4.6.  ds_mark_dst("s")
-
-   Mark the last used address from destination set as inactive
-   ("i"/"I"/"0"), active ("a"/"A"/"1") or probing ("p"/"P"/"2"). With this
-   function, an automatic detection of failed gateways can be implemented.
-   When an address is marked as inactive or probing, it will be ignored by
-   'ds_select_dst' and 'ds_select_domain'.
-
-   possible parameters:
-     * "i", "I" or "0" - the last destination should be set to inactive
-       and will be ignored in future requests.
-     * "a", "A" or "1" - the last destination should be set to active and
-       the error-counter should set to "0".
-     * "p", "P" or "2" - the last destination will be set to probing.
-       Note: You will need to call this function "threshhold"-times,
-       before it will be actually set to probing.
-
-   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
-
-4.7.  ds_is_from_list()
-
-   This function returns true, if the current request comes from a host
-   from the dispatcher-list; otherwise false.
-
-   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
-   BRANCH_ROUTE and ONREPLY_ROUTE.
+   Example 1.30. ds_mark_dst usage
+...
+failure_route[tryagain] {
+...
+   if(t_check_status("500"))
+      ds_mark_dst("ip"); # set to inactive and probing
+...
+}
+...
 
-4.8.  ds_is_from_list("group")
+4.6. ds_is_from_list([groupid])
 
    This function returns true, if the current request comes from a host in
    the given group of the dispatcher-list; otherwise false.
 
-   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
-   BRANCH_ROUTE and ONREPLY_ROUTE.
+   Parameter groupid is optional, when it is missing, then the matching
+   will be done against all addresses in all groups. Upon a match, the
+   'grp_avp' will be set to groupid of matching address.
 
-4.9.  ds_load_update()
+   This function can be used from ANY_ROUTE.
+
+   Example 1.31. ds_mark_dst usage
+...
+if(ds_is_from_list("10")) {
+    ...
+}
+...
+
+4.7. ds_load_update()
 
    Updates the load state:
      * if it is a BYE or CANCEL - remove the load from destination address
@@ -808,14 +818,14 @@ ds_select_dst("1", "$var(a)");
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE and ONREPLY_ROUTE.
 
-4.10.  ds_load_unset()
+4.8. ds_load_unset()
 
    Remove the call load for the destination that routed the call.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE and ONREPLY_ROUTE.
 
-   Example 1.30. ds_load_unset usage
+   Example 1.32. ds_load_unset usage
 ...
 route {
     ...
@@ -845,7 +855,7 @@ onreply_route {
    5.2. ds_list
    5.3. ds_reload
 
-5.1.  ds_set_state
+5.1. ds_set_state
 
    Sets the status for a destination address (can be use to mark the
    destination as active or inactive).
@@ -854,11 +864,12 @@ onreply_route {
 
    Parameters:
      * _state_ : state of the destination address
-          + “a”: active
-          + “i”: inactive
-          + “d”: disabled
-       The states “a” or “i” can be followed by “p” to set probing mode
-       (e.g. 'ap' or 'ip')
+          + "a": active
+          + "i": inactive
+          + "t": trying
+          + "d": disabled
+       The states "a", "i" or "t" can be followed by "p" to set probing
+       mode (e.g. 'ap', 'ip' or 'tp').
      * _group_: destination group id
      * _address_: address of the destination in the _group_
 
@@ -869,7 +880,7 @@ onreply_route {
                 _address_
                 _empty_line_
 
-5.2.  ds_list
+5.2. ds_list
 
    It lists the groups and included destinations.
 
@@ -881,7 +892,7 @@ onreply_route {
                 :ds_list:_reply_fifo_file_
                 _empty_line_
 
-5.3.  ds_reload
+5.3. ds_reload
 
    It reloads the groups and included destinations. The command is
    disabled for call load based dispatching (algorithm 10) since removal
@@ -901,7 +912,7 @@ onreply_route {
    6.2. dispatcher.list
    6.3. dispatcher.reload
 
-6.1.  dispatcher.set_state
+6.1. dispatcher.set_state
 
    Sets the state for a destination address (can be use to mark the
    destination as active or inactive).
@@ -910,18 +921,22 @@ onreply_route {
 
    Parameters:
      * _state_ : state of the destination address
-          + “a”: active
-          + “i”: inactive
-          + “d”: disabled
-       The states “a” or “i” can be followed by “p” to set probing mode
-       (e.g. 'ap' or 'ip')
+          + "a": active
+          + "i": inactive
+          + "t": trying
+          + "d": disabled
+       The states "a", "i" or "t" can be followed by "p" to set probing
+       mode (e.g. 'ap', 'ip' or 'tp').
      * _group_: destination group id
      * _address_: address of the destination in the _group_
 
    Example:
-                sercmd dispatcher.set_state _state_ _group_ _address_
+...
+# prototype: sercmd dispatcher.set_state _state_ _group_ _address_
+sercmd dispatcher.set_state ip 2 sip:127.0.0.1:5080
+...
 
-6.2.  dispatcher.list
+6.2. dispatcher.list
 
    It lists the groups and included destinations.
 
@@ -932,7 +947,7 @@ onreply_route {
    Example:
                 sercmd dispatcher.list
 
-6.3.  dispatcher.reload
+6.3. dispatcher.reload
 
    It reloads the groups and included destinations. The command is
    disabled for call load based dispatching (algorithm 10) since removal
@@ -979,7 +994,7 @@ setid(int) destination(sip uri) flags(int,opt) priority(int,opt) attrs(str,opt)
    For database, each element of a line resides in a different column.
    Next is a dispatcher.list file example:
 
-   Example 1.31. dispatcher list file
+   Example 1.33. dispatcher list file
 ...
 # $Id$
 # dispatcher destination sets
@@ -1004,80 +1019,289 @@ r,opt)
 
    Next picture displays a sample usage of dispatcher.
 
-   Example 1.32. Kamailio config script - sample dispatcher usage
+   Example 1.34. Kamailio config script - sample dispatcher usage
 ...
-# $Id$
+#!KAMAILIO
+#
 # sample config file for dispatcher module
+# - load balancing of VoIP calls with round robin
+# - no TPC listening
+# - don't dispatch REGISTER and presence requests
 #
+# Kamailio (OpenSER) SIP Server v3.2
+#     - web: http://www.kamailio.org
+#     - git: http://sip-router.org
+#
+# Direct your questions about this file to: sr-users@lists.sip-router.org
+#
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
+# for an explanation of possible statements, functions and parameters.
+#
+# Several features can be enabled using '#!define WITH_FEATURE' directives:
+#
+# *** To run in debug mode:
+#     - define WITH_DEBUG
+#
+
+
+####### Global Parameters #########
+
+#!ifdef WITH_DEBUG
+debug=4
+log_stderror=yes
+#!else
+debug=2
+log_stderror=no
+#!endif
+
+memdbg=5
+memlog=5
+
+log_facility=LOG_LOCAL0
+
+fork=yes
+children=4
+
+/* comment the next line to enable TCP */
+disable_tcp=yes
+
+/* uncomment the next line to disable the auto discovery of local aliases
+   based on revers DNS on IPs (default on) */
+auto_aliases=no
 
-debug=9          # debug level (cmd line: -dddddddddd)
-fork=no
-log_stderror=yes  # (cmd line: -E)
+/* add local domain aliases */
+# alias="mysipserver.com"
 
-children=2
-check_via=no      # (cmd. line: -v)
-dns=off           # (cmd. line: -r)
-rev_dns=off       # (cmd. line: -R)
 port=5060
 
-# for more info: sip_router -h
+/* uncomment and configure the following line if you want Kamailio to
+   bind on a specific interface/port/proto (default bind on all available) */
+# listen=udp:127.0.0.1:5060
 
-# ------------------ module loading ----------------------------------
-mpath="/usr/local/lib/kamailio/modules/"
-loadmodule "maxfwd.so"
+sip_warning=no
+
+####### Modules Section ########
+
+#set module path
+mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
+
+loadmodule "db_mysql.so"
+loadmodule "mi_fifo.so"
+loadmodule "kex.so"
+loadmodule "tm.so"
+loadmodule "tmx.so"
 loadmodule "sl.so"
+loadmodule "rr.so"
+loadmodule "pv.so"
+loadmodule "maxfwd.so"
+loadmodule "textops.so"
+loadmodule "siputils.so"
+loadmodule "xlog.so"
+loadmodule "sanity.so"
+loadmodule "ctl.so"
+loadmodule "mi_rpc.so"
+loadmodule "acc.so"
 loadmodule "dispatcher.so"
-loadmodule "tm.so"
+
 
 # ----------------- setting module-specific parameters ---------------
 
-# -- dispatcher params --
-modparam("dispatcher", "list_file", "/usr/local/etc/kamailio/dispatcher.list")
+
+# ----- mi_fifo params -----
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+
+# ----- rr params -----
+# add value to ;lr param to cope with most of the UAs
+modparam("rr", "enable_full_lr", 1)
+# do not append from tag to the RR (no need for this script)
+modparam("rr", "append_fromtag", 0)
+
+
+# ----- acc params -----
+modparam("acc", "log_flag", 1)
+modparam("acc", "failed_transaction_flag", 3)
+modparam("acc", "log_extra",
+        "src_user=$fU;src_domain=$fd;dst_ouser=$tU;dst_user=$rU;dst_domain=$rd;s
+rc_ip=$si")
+
+# ----- tm params -----
+modparam("tm", "fr_timer", 2000)
+modparam("tm", "fr_inv_timer", 40000)
+
+# ----- dispatcher params -----
+modparam("dispatcher", "db_url",
+        "mysql://openser:openserro@localhost/openser")
+modparam("dispatcher", "table_name", "dispatcher")
 modparam("dispatcher", "flags", 2)
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
 
-# main request routing block
+####### Routing Logic ########
+
+
+# main request routing logic
+
 route {
-        if ( !mf_process_maxfwd_header("10") )
+
+        # per request initial checks
+        route(REQINIT);
+
+        # handle requests within SIP dialogs
+        route(WITHINDLG);
+
+        ### only initial requests (no To tag)
+
+        # CANCEL processing
+        if (is_method("CANCEL"))
         {
-                sl_send_reply("483","To Many Hops");
+                if (t_check_trans())
+                        t_relay();
                 exit;
         }
 
-        # select from first dst group by round-robin
-        if(!ds_select_dst("1", "4"))
+        t_check_trans();
+
+        # record routing for dialog forming requests (in case they are routed)
+        # - remove preloaded route headers
+        remove_hf("Route");
+        if (is_method("INVITE|SUBSCRIBE"))
+                record_route();
+
+        # account only INVITEs
+        if (is_method("INVITE"))
         {
-                sl_send_reply("500", "No destination available");
-                exit;
+                setflag(1); # do accounting
         }
 
-        t_on_failure("RTF_DISPATCH");
-        if(!t_relay())
+        # handle presence related requests
+        route(PRESENCE);
+
+        # handle registrations
+        route(REGISTRAR);
+
+        if ($rU==$null)
         {
+                # request with no Username in RURI
+                sl_send_reply("484","Address Incomplete");
+                exit;
+        }
+
+        # dispatch destinations
+        route(DISPATCH);
+
+        route(RELAY);
+}
+
+
+route[RELAY] {
+        if (!t_relay()) {
                 sl_reply_error();
+        }
+        exit;
+}
+
+# Per SIP request initial checks
+route[REQINIT] {
+        if (!mf_process_maxfwd_header("10")) {
+                sl_send_reply("483","Too Many Hops");
+                exit;
+        }
+
+        if(!sanity_check("1511", "7"))
+        {
+                xlog("Malformed SIP message from $si:$sp\n");
+                exit;
+        }
+}
+
+# Handle requests within SIP dialogs
+route[WITHINDLG] {
+        if (has_totag()) {
+                # sequential request withing a dialog should
+                # take the path determined by record-routing
+                if (loose_route()) {
+                        if (is_method("BYE")) {
+                                setflag(1); # do accounting ...
+                                setflag(3); # ... even if the transaction fails
+                        }
+                        route(RELAY);
+                } else {
+                        if (is_method("SUBSCRIBE") && uri == myself) {
+                                # in-dialog subscribe requests
+                                route(PRESENCE);
+                                exit;
+                        }
+                        if ( is_method("ACK") ) {
+                                if ( t_check_trans() ) {
+                                        # non loose-route, but stateful ACK;
+                                        # must be ACK after a 487 or e.g. 404 fr
+om upstream server
+                                        t_relay();
+                                        exit;
+                                } else {
+                                        # ACK without matching transaction ... i
+gnore and discard.
+                                        exit;
+                                }
+                        }
+                        sl_send_reply("404","Not here");
+                }
+                exit;
+        }
+}
+
+# Handle SIP registrations
+route[REGISTRAR] {
+        if(!is_method("REGISTER"))
+                return;
+        sl_send_reply("404", "No registrar");
+        exit;
+}
+
+# Presence server route
+route[PRESENCE] {
+        if(!is_method("PUBLISH|SUBSCRIBE"))
+                return;
+
+        sl_send_reply("404", "Not here");
+        exit;
+}
+
+# Dispatch requests
+route[DISPATCH] {
+        # round robin dispatching on gateways group '1'
+        if(!ds_select_dst("1", "4"))
+        {
+                send_reply("404", "No destination");
                 exit;
         }
+        xlog("L_DBG", "--- SCRIPT: going to <$ru> via <$du>\n");
+        t_on_failure("RTF_DISPATCH");
+        return;
 }
 
-# dispatcher failure routing block
+# Sample failure route
 failure_route[RTF_DISPATCH] {
         if (t_is_canceled()) {
                 exit;
         }
-        # select next destination only for local timeout
-        if (t_branch_timeout() &amp;&amp; !t_branch_replied())
+        # next DST - only for 500 or local timeout
+        if (t_check_status("500")
+                        or (t_branch_timeout() and !t_branch_replied()))
         {
                 if(ds_next_dst())
                 {
                         t_on_failure("RTF_DISPATCH");
-                        t_relay();
+                        route(RELAY);
                         exit;
                 }
         }
 }
 
+
+
 ...
 
 8. Event routes
@@ -1085,7 +1309,7 @@ failure_route[RTF_DISPATCH] {
    8.1. dispatcher:dst-down
    8.2. dispatcher:dst-up
 
-8.1.  dispatcher:dst-down
+8.1. dispatcher:dst-down
 
    When defined, the module calls event_route[dispatcher:ds-down] when a
    destination goes down (becomes probing). A typical use case is to
@@ -1096,7 +1320,7 @@ event_route[dispatcher:dst-down] {
 }
 ...
 
-8.2.  dispatcher:dst-up
+8.2. dispatcher:dst-up
 
    When defined, the module calls event_route[dispatcher:ds-up] when a
    destination that was previously down (probing) comes up. A typical use
@@ -1117,46 +1341,51 @@ Chapter 2. Frequently Asked Questions
 
    2.1.
 
-       Does dispatcher provide a fair distribution?
+   Does dispatcher provide a fair distribution?
+
+   The algoritms doing hashing over parts of SIP message don't guarantee a
+   fair distribution. You should do some measurements to decide what
+   hashing algorithm fits better in your environment.
 
-       There is no guarantee of that. You should do some measurements to
-       decide what distribution algorithm fits better in your environment.
+   Other distribution algorithms such as round robin or call load
+   dispatching do a fair distribution in terms of delivered calls to
+   gateways.
 
    2.2.
 
-       Is dispatcher dialog stateful?
+   Is dispatcher dialog stateful?
 
-       No. Dispatcher is stateless, although some distribution algorithms are
-       designed to select same destination for subsequent requests of the same
-       dialog (e.g., hashing the call-id).
+   No. Dispatcher is stateless, although some distribution algorithms are
+   designed to select same destination for subsequent requests of the same
+   dialog (e.g., hashing the call-id).
 
    2.3.
 
-       Where can I find more about Kamailio?
+   Where can I find more about Kamailio?
 
-       Take a look at http://www.kamailio.org/.
+   Take a look at http://www.kamailio.org/.
 
    2.4.
 
-       Where can I post a question about this module?
+   Where can I post a question about this module?
 
-       First at all check if your question was already answered on one of our
-       mailing lists:
-         * User Mailing List -
-           http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-users
-         * Developer Mailing List -
-           http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev
+   First at all check if your question was already answered on one of our
+   mailing lists:
+     * User Mailing List -
+       http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-users
+     * Developer Mailing List -
+       http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev
 
-       E-mails regarding any stable version should be sent to
-       <sr-users@lists.sip-router.org> and e-mail regarding development
-       versions or CVS snapshots should be send to
-       <sr-dev@lists.sip-router.org>.
+   E-mails regarding any stable version should be sent to
+   <sr-users@lists.sip-router.org> and e-mail regarding development
+   versions or CVS snapshots should be send to
+   <sr-dev@lists.sip-router.org>.
 
-       If you want to keep the mail private, send it to
-       <sr-users@lists.sip-router.org>.
+   If you want to keep the mail private, send it to
+   <sr-users@lists.sip-router.org>.
 
    2.5.
 
-       How can I report a bug?
+   How can I report a bug?
 
-       Please follow the guidelines provided at: http://sip-router.org/tracker
+   Please follow the guidelines provided at: http://sip-router.org/tracker
index 9bb942a..7aa33fd 100644 (file)
@@ -103,7 +103,7 @@ int *next_idx   = NULL;
 #define _ds_list       (ds_lists[*crt_idx])
 #define _ds_list_nr (*ds_list_nr)
 
-static void ds_run_route(struct sip_msg *msg, char *route);
+static void ds_run_route(struct sip_msg *msg, str *uri, char *route);
 
 void destroy_list(int);
 
@@ -142,7 +142,7 @@ int ds_print_sets(void)
 
        if(_ds_list==NULL)
                return -1;
-       
+
        /* get the index of the set */
        si = _ds_list;
        while(si)
@@ -177,7 +177,7 @@ int init_data(void)
        }
        ds_lists[0] = ds_lists[1] = 0;
 
-       
+
        p = (int*)shm_malloc(3*sizeof(int));
        if(!p)
        {
@@ -247,7 +247,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
        ds_set_t  *sp = NULL;
        ds_dest_t *dp0 = NULL;
        ds_dest_t *dp1 = NULL;
-       
+
        /* For DNS-Lookups */
        static char hn[256];
        struct hostent* he;
@@ -261,7 +261,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
                LM_ERR("bad uri [%.*s]\n", uri.len, uri.s);
                goto err;
        }
-       
+
        /* get dest set */
        sp = ds_lists[list_idx];
        while(sp)
@@ -279,7 +279,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
                        LM_ERR("no more memory.\n");
                        goto err;
                }
-               
+
                memset(sp, 0, sizeof(ds_set_t));
                sp->next = ds_lists[list_idx];
                ds_lists[list_idx] = sp;
@@ -321,7 +321,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
         * make a copy here. */
        strncpy(hn, puri.host.s, puri.host.len);
        hn[puri.host.len]='\0';
-               
+
        /* Do a DNS-Lookup for the Host-Name: */
        he=resolvehost(hn);
        if (he==0)
@@ -331,7 +331,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
        }
        /* Free the hostname */
        hostent2ip_addr(&dp->ip_address, he, 0);
-               
+
        /* Copy the Port out of the URI: */
        dp->port = puri.port_no;                
 
@@ -359,7 +359,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
        }
 
        LM_DBG("dest [%d/%d] <%.*s>\n", sp->id, sp->nr, dp->uri.len, dp->uri.s);
-       
+
        return 0;
 err:
        /* free allocated memory */
@@ -456,7 +456,7 @@ int reindex_dests(int list_idx, int setn)
 
                        dp = sp->dlist;
                        sp->dlist = dp->next;
-                       
+
                        shm_free(dp);
                        dp=NULL;
                }
@@ -496,7 +496,7 @@ int ds_load_list(char *lfile)
        {
                LM_ERR("can't open list file [%s]\n", lfile);
                return -1;
-               
+
        }
 
        id = setn = flags = priority = 0;
@@ -587,7 +587,7 @@ add_destination:
 next_line:
                p = fgets(line, 256, f);
        }
-               
+
        if(reindex_dests(*next_idx, setn)!=0){
                LM_ERR("error on reindex\n");
                goto error;
@@ -646,20 +646,20 @@ int init_ds_db(void)
                LM_ERR("invalid database name\n");
                return -1;
        }
-       
+
        /* Find a database module */
        if (db_bind_mod(&ds_db_url, &ds_dbf) < 0)
        {
                LM_ERR("Unable to bind to a database driver\n");
                return -1;
        }
-       
+
        if(ds_connect_db()!=0){
-               
+
                LM_ERR("unable to connect to the database\n");
                return -1;
        }
-       
+
        _ds_table_version = db_table_version(&ds_dbf, ds_db_handle, &ds_table_name);
        if (_ds_table_version < 0)
        {
@@ -670,9 +670,9 @@ int init_ds_db(void)
                        && _ds_table_version != DS_TABLE_VERSION3
                        && _ds_table_version != DS_TABLE_VERSION4) {
                LM_ERR("invalid table version (found %d , required %d, %d, %d or %d)\n"
-                       "(use kamdbctl reinit)\n",
-                       _ds_table_version, DS_TABLE_VERSION, DS_TABLE_VERSION2,
-                       DS_TABLE_VERSION3, DS_TABLE_VERSION4);
+                               "(use kamdbctl reinit)\n",
+                               _ds_table_version, DS_TABLE_VERSION, DS_TABLE_VERSION2,
+                               DS_TABLE_VERSION3, DS_TABLE_VERSION4);
                return -1;
        }
 
@@ -695,11 +695,11 @@ int ds_load_db(void)
        db1_res_t * res;
        db_val_t * values;
        db_row_t * rows;
-       
+
        db_key_t query_cols[5] = {&ds_set_id_col, &ds_dest_uri_col,
-                               &ds_dest_flags_col, &ds_dest_priority_col,
-                               &ds_dest_attrs_col};
-       
+               &ds_dest_flags_col, &ds_dest_priority_col,
+               &ds_dest_attrs_col};
+
        nrcols = 2;
        if(_ds_table_version == DS_TABLE_VERSION2)
                nrcols = 3;
@@ -715,8 +715,8 @@ int ds_load_db(void)
        }
 
        if(ds_db_handle == NULL){
-                       LM_ERR("invalid DB handler\n");
-                       return -1;
+               LM_ERR("invalid DB handler\n");
+               return -1;
        }
 
        if (ds_dbf.use_table(ds_db_handle, &ds_table_name) < 0)
@@ -740,7 +740,7 @@ int ds_load_db(void)
        setn = 0;
        *next_idx = (*crt_idx + 1)%2;
        destroy_list(*next_idx);
-       
+
        for(i=0; i<nr_rows; i++)
        {
                values = ROW_VALUES(rows+i);
@@ -820,16 +820,16 @@ void destroy_list(int list_id)
                for(dest = sp->dlist; dest!= NULL; dest=dest->next)
                {
                        if(dest->uri.s!=NULL)
-                       {
-                               shm_free(dest->uri.s);
-                               dest->uri.s = NULL;
-                       }
+                       {
+                               shm_free(dest->uri.s);
+                               dest->uri.s = NULL;
+                       }
                }
                if (sp->dlist != NULL)
                        shm_free(sp->dlist);
                sp = sp->next;
        }
-       
+
        ds_lists[list_id]  = NULL;
 }
 
@@ -875,7 +875,7 @@ unsigned int ds_get_hash(str *x, str *y)
                                h+=v^(v>>3);
                        }
                }
-       
+
                v=0;
                for (;p<(y->s+y->len); p++)
                {
@@ -905,10 +905,10 @@ unsigned int ds_get_hash(str *x, str *y)
  * \return: -1 on error, 0 on success
  */
 static inline int get_uri_hash_keys(str* key1, str* key2,
-                                                       str* uri, struct sip_uri* parsed_uri, int flags)
+               str* uri, struct sip_uri* parsed_uri, int flags)
 {
        struct sip_uri tmp_p_uri; /* used only if parsed_uri==0 */
-       
+
        if (parsed_uri==0)
        {
                if (parse_uri(uri->s, uri->len, &tmp_p_uri)<0)
@@ -921,11 +921,11 @@ static inline int get_uri_hash_keys(str* key1, str* key2,
        /* uri sanity checks */
        if (parsed_uri->host.s==0)
        {
-                       LM_ERR("invalid uri, no host present: %.*s\n",
-                                       uri->len, uri->len?uri->s:"");
-                       goto error;
+               LM_ERR("invalid uri, no host present: %.*s\n",
+                               uri->len, uri->len?uri->s:"");
+               goto error;
        }
-       
+
        /* we want: user@host:port if port !=5060
         *          user@host if port==5060
         *          user if the user flag is set*/
@@ -962,31 +962,31 @@ int ds_hash_fromuri(struct sip_msg *msg, unsigned int *hash)
        str from;
        str key1;
        str key2;
-       
+
        if(msg==NULL || hash == NULL)
        {
                LM_ERR("bad parameters\n");
                return -1;
        }
-       
+
        if(parse_from_header(msg)<0)
        {
                LM_ERR("cannot parse From hdr\n");
                return -1;
        }
-       
+
        if(msg->from==NULL || get_from(msg)==NULL)
        {
                LM_ERR("cannot get From uri\n");
                return -1;
        }
-       
+
        from   = get_from(msg)->uri;
        trim(&from);
        if (get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags)<0)
                return -1;
        *hash = ds_get_hash(&key1, &key2);
-       
+
        return 0;
 }
 
@@ -999,7 +999,7 @@ int ds_hash_touri(struct sip_msg *msg, unsigned int *hash)
        str to;
        str key1;
        str key2;
-       
+
        if(msg==NULL || hash == NULL)
        {
                LM_ERR("bad parameters\n");
@@ -1011,15 +1011,15 @@ int ds_hash_touri(struct sip_msg *msg, unsigned int *hash)
                LM_ERR("cannot parse To hdr\n");
                return -1;
        }
-       
-       
+
+
        to   = get_to(msg)->uri;
        trim(&to);
-       
+
        if (get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags)<0)
                return -1;
        *hash = ds_get_hash(&key1, &key2);
-       
+
        return 0;
 }
 
@@ -1035,20 +1035,20 @@ int ds_hash_callid(struct sip_msg *msg, unsigned int *hash)
                LM_ERR("bad parameters\n");
                return -1;
        }
-       
+
        if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) ||
                                (msg->callid==NULL)) )
        {
                LM_ERR("cannot parse Call-Id\n");
                return -1;
        }
-       
+
        cid.s   = msg->callid->body.s;
        cid.len = msg->callid->body.len;
        trim(&cid);
-       
+
        *hash = ds_get_hash(&cid, NULL);
-       
+
        return 0;
 }
 
@@ -1061,8 +1061,8 @@ int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
        str* uri;
        str key1;
        str key2;
-       
-       
+
+
        if(msg==NULL || hash == NULL)
        {
                LM_ERR("bad parameters\n");
@@ -1072,11 +1072,11 @@ int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
                LM_ERR("bad request uri\n");
                return -1;
        }
-       
+
        uri=GET_RURI(msg);
        if (get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags)<0)
                return -1;
-       
+
        *hash = ds_get_hash(&key1, &key2);
        return 0;
 }
@@ -1092,7 +1092,7 @@ int ds_hash_authusername(struct sip_msg *msg, unsigned int *hash)
        str username = {0, 0};
        /* The Credentials from this request */
        auth_body_t* cred;
-       
+
        if(msg==NULL || hash == NULL)
        {
                LM_ERR("bad parameters\n");
@@ -1133,14 +1133,14 @@ int ds_hash_authusername(struct sip_msg *msg, unsigned int *hash)
                LM_ERR("No Authorization-Username or Credentials!\n");
                return 1;
        }
-       
+
        username.s = cred->digest.username.user.s;
        username.len = cred->digest.username.user.len;
 
        trim(&username);
-       
+
        *hash = ds_get_hash(&username, NULL);
-       
+
        return 0;
 }
 
@@ -1152,7 +1152,7 @@ int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash)
 {
        /* The String to create the hash */
        str hash_str = {0, 0};
-       
+
        if(msg==NULL || hash == NULL || hash_param_model == NULL)
        {
                LM_ERR("bad parameters\n");
@@ -1172,7 +1172,7 @@ int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash)
        LM_DBG("Hashing %.*s!\n", hash_str.len, hash_str.s);
 
        *hash = ds_get_hash(&hash_str, NULL);
-       
+
        return 0;
 }
 
@@ -1182,10 +1182,10 @@ int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash)
 static inline int ds_get_index(int group, ds_set_t **index)
 {
        ds_set_t *si = NULL;
-       
+
        if(index==NULL || group<0 || _ds_list==NULL)
                return -1;
-       
+
        /* get the index of the set */
        si = _ds_list;
        while(si)
@@ -1221,8 +1221,7 @@ int ds_get_leastloaded(ds_set_t *dset)
        t = dset->dlist[k].dload;
        for(j=1; j<dset->nr; j++)
        {
-               if(!((dset->dlist[j].flags & DS_INACTIVE_DST)
-                               || (dset->dlist[j].flags & DS_PROBING_DST)))
+               if(!ds_skip_dst(dset->dlist[j].flags & DS_PROBING_DST))
                {
                        if(dset->dlist[j].dload<t)
                        {
@@ -1247,7 +1246,7 @@ int ds_load_add(struct sip_msg *msg, ds_set_t *dset, int setid, int dst)
        }
 
        if(ds_add_cell(_dsht_load, &msg->callid->body,
-                       &dset->dlist[dst].attrs.duid, setid)<0)
+                               &dset->dlist[dst].attrs.duid, setid)<0)
        {
                LM_ERR("cannot add load to %d (%.*s)\n", setid,
                                msg->callid->body.len, msg->callid->body.s);
@@ -1333,7 +1332,7 @@ int ds_load_replace(struct sip_msg *msg, str *duid)
        if(ds_load_add(msg, idx, set, newdst)<0)
        {
                LM_ERR("unable to replace destination load [%.*s / %.*s]\n",
-                       duid->len, duid->s, msg->callid->body.len, msg->callid->body.s);
+                               duid->len, duid->s, msg->callid->body.len, msg->callid->body.s);
                return -1;
        }
        return 0;
@@ -1455,14 +1454,14 @@ int ds_load_state(struct sip_msg *msg, int state)
  */
 int ds_load_update(struct sip_msg *msg)
 {
-    if(parse_headers(msg, HDR_CSEQ_F|HDR_CALLID_F, 0)!=0
+       if(parse_headers(msg, HDR_CSEQ_F|HDR_CALLID_F, 0)!=0
                        || msg->cseq==NULL || msg->callid==NULL)
-    {
-        LM_ERR("cannot parse cseq and callid headers\n");
-        return -1;
-    }
+       {
+               LM_ERR("cannot parse cseq and callid headers\n");
+               return -1;
+       }
        if(msg->first_line.type==SIP_REQUEST)
-    {
+       {
                if(msg->first_line.u.request.method_value==METHOD_BYE
                                || msg->first_line.u.request.method_value==METHOD_CANCEL)
                {
@@ -1489,14 +1488,14 @@ int ds_load_unset(struct sip_msg *msg)
        struct search_state st;
        struct usr_avp *prev_avp;
        int_str avp_value;
-       
+
        if(dstid_avp_name.n==0)
                return 0;
 
        /* for INVITE requests should be called after dst list is built */
        if(msg->first_line.type==SIP_REQUEST
                        &&  msg->first_line.u.request.method_value==METHOD_INVITE)
-    {
+       {
                prev_avp = search_first_avp(dstid_avp_type, dstid_avp_name,
                                &avp_value, &st);
                if(prev_avp==NULL)
@@ -1529,7 +1528,7 @@ static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
                                LM_ERR("error while setting host\n");
                                return -1;
                        }
-               break;
+                       break;
                default:
                        duri = uri;
                        if (set_dst_uri(msg, uri) < 0) {
@@ -1537,9 +1536,9 @@ static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
                                return -1;
                        }
                        /* dst_uri changes, so it makes sense to re-use the current uri for
-                               forking */
+                          forking */
                        ruri_mark_new(); /* re-use uri for serial forking */
-               break;
+                       break;
        }
        return 0;
 }
@@ -1559,7 +1558,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                LM_ERR("bad parameters\n");
                return -1;
        }
-       
+
        if(_ds_list==NULL || _ds_list_nr<=0)
        {
                LM_ERR("no destination sets\n");
@@ -1573,7 +1572,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                                msg->dst_uri.s);
                return -1;
        }
-       
+
 
        /* get the index of the set */
        if(ds_get_index(set, &idx)!=0)
@@ -1581,7 +1580,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                LM_ERR("destination set [%d] not found\n", set);
                return -1;
        }
-       
+
        LM_DBG("set [%d]\n", set);
 
        hash = 0;
@@ -1593,67 +1592,67 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                                LM_ERR("can't get callid hash\n");
                                return -1;
                        }
-               break;
+                       break;
                case 1: /* hash from-uri */
                        if(ds_hash_fromuri(msg, &hash)!=0)
                        {
                                LM_ERR("can't get From uri hash\n");
                                return -1;
                        }
-               break;
+                       break;
                case 2: /* hash to-uri */
                        if(ds_hash_touri(msg, &hash)!=0)
                        {
                                LM_ERR("can't get To uri hash\n");
                                return -1;
                        }
-               break;
+                       break;
                case 3: /* hash r-uri */
                        if (ds_hash_ruri(msg, &hash)!=0)
                        {
                                LM_ERR("can't get ruri hash\n");
                                return -1;
                        }
-               break;
+                       break;
                case DS_ALG_RROBIN: /* round robin */
                        hash = idx->last;
                        idx->last = (idx->last+1) % idx->nr;
-               break;
+                       break;
                case 5: /* hash auth username */
                        i = ds_hash_authusername(msg, &hash);
                        switch (i)
                        {
                                case 0:
                                        /* Authorization-Header found: Nothing to be done here */
-                               break;
+                                       break;
                                case 1:
                                        /* No Authorization found: Use round robin */
                                        hash = idx->last;
                                        idx->last = (idx->last+1) % idx->nr;
-                               break;
+                                       break;
                                default:
                                        LM_ERR("can't get authorization hash\n");
                                        return -1;
-                               break;
+                                       break;
                        }
-               break;
+                       break;
                case 6: /* random selection */
                        hash = rand() % idx->nr;
-               break;
+                       break;
                case 7: /* hash on PV value */
                        if (ds_hash_pvar(msg, &hash)!=0)
                        {
                                LM_ERR("can't get PV hash\n");
                                return -1;
                        }
-               break;          
+                       break;
                case 8: /* use always first entry */
                        hash = 0;
-               break;
+                       break;
                case 9: /* weight based distribution */
                        hash = idx->wlist[idx->wlast];
                        idx->wlast = (idx->wlast+1) % 100;
-               break;
+                       break;
                case DS_ALG_LOAD: /* call load based distribution */
                        /* only INVITE can start a call */
                        if(msg->first_line.u.request.method_value!=METHOD_INVITE)
@@ -1678,7 +1677,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                                        alg = 0;
                                }
                        }
-               break;
+                       break;
                default:
                        LM_WARN("algo %d not implemented - using first entry...\n", alg);
                        hash = 0;
@@ -1692,8 +1691,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
        else
                hash = hash%idx->nr;
        i=hash;
-       while ((idx->dlist[i].flags & DS_INACTIVE_DST)
-                       || (idx->dlist[i].flags & DS_PROBING_DST))
+       while (ds_skip_dst(idx->dlist[i].flags))
        {
                if(ds_use_default!=0 && idx->nr!=1)
                        i = (i+1)%(idx->nr-1);
@@ -1705,8 +1703,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                        if(ds_use_default!=0)
                        {
                                i = idx->nr-1;
-                               if((idx->dlist[i].flags & DS_INACTIVE_DST)
-                                               || (idx->dlist[i].flags & DS_PROBING_DST))
+                               if(ds_skip_dst(idx->dlist[i].flags))
                                        return -1;
                                break;
                        } else {
@@ -1725,7 +1722,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
        /* if alg is round-robin then update the shortcut to next to be used */
        if(alg==DS_ALG_RROBIN)
                idx->last = (hash+1) % idx->nr;
-       
+
        LM_DBG("selected [%d-%d/%d] <%.*s>\n", alg, set, hash,
                        idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
 
@@ -1764,12 +1761,12 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                        }
                        cnt++;
                }
-       
+
                /* add to avp */
 
                for(i=hash-1; i>=0; i--)
                {       
-                       if((idx->dlist[i].flags & DS_INACTIVE_DST)
+                       if(ds_skip_dst(idx->dlist[i].flags)
                                        || (ds_use_default!=0 && i==(idx->nr-1)))
                                continue;
                        LM_DBG("using entry [%d/%d]\n", set, i);
@@ -1803,7 +1800,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
 
                for(i=idx->nr-1; i>hash; i--)
                {       
-                       if((idx->dlist[i].flags & DS_INACTIVE_DST)
+                       if(ds_skip_dst(idx->dlist[i].flags)
                                        || (ds_use_default!=0 && i==(idx->nr-1)))
                                continue;
                        LM_DBG("using entry [%d/%d]\n", set, i);
@@ -1834,7 +1831,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                        }
                        cnt++;
                }
-       
+
                /* add to avp the first used dst */
                avp_val.s = idx->dlist[hash].uri;
                if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
@@ -1852,8 +1849,8 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                        if(idx->dlist[hash].attrs.duid.len<=0)
                        {
                                LM_ERR("no uid for destination: %d %.*s\n", set,
-                                                       idx->dlist[hash].uri.len,
-                                                       idx->dlist[hash].uri.s);
+                                               idx->dlist[hash].uri.len,
+                                               idx->dlist[hash].uri.s);
                                return -1;
                        }
                        avp_val.s = idx->dlist[hash].attrs.duid;
@@ -1879,7 +1876,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
                if(add_avp(cnt_avp_type, cnt_avp_name, avp_val)!=0)
                        return -1;
        }
-       
+
        return 1;
 }
 
@@ -1890,7 +1887,7 @@ int ds_next_dst(struct sip_msg *msg, int mode)
        struct usr_avp *prev_avp;
        int_str avp_value;
        int alg = 0;
-       
+
        if(!(ds_flags&DS_FAILOVER_ON) || dst_avp_name.n==0)
        {
                LM_WARN("failover support disabled\n");
@@ -1913,7 +1910,7 @@ int ds_next_dst(struct sip_msg *msg, int mode)
        if(attrs_avp_name.n!=0)
        {
                prev_avp = search_first_avp(attrs_avp_type,
-                                       attrs_avp_name, &avp_value, &st);
+                               attrs_avp_name, &avp_value, &st);
                if(prev_avp!=NULL)
                {
                        destroy_avp(prev_avp);
@@ -1928,7 +1925,7 @@ int ds_next_dst(struct sip_msg *msg, int mode)
        destroy_avp(prev_avp);
        if(avp==NULL || !(avp->flags&AVP_VAL_STR))
                return -1; /* no more avps or value is int */
-       
+
        if(ds_update_dst(msg, &avp_value.s, mode)!=0)
        {
                LM_ERR("cannot set dst addr\n");
@@ -1950,16 +1947,16 @@ int ds_next_dst(struct sip_msg *msg, int mode)
                        return -1;
                }
        }
-       
+
        return 1;
 }
 
-int ds_mark_dst(struct sip_msg *msg, int mode)
+int ds_mark_dst(struct sip_msg *msg, int state)
 {
        int group, ret;
        struct usr_avp *prev_avp;
        int_str avp_value;
-       
+
        if(!(ds_flags&DS_FAILOVER_ON))
        {
                LM_WARN("failover support disabled\n");
@@ -1967,41 +1964,31 @@ int ds_mark_dst(struct sip_msg *msg, int mode)
        }
 
        prev_avp = search_first_avp(grp_avp_type, grp_avp_name, &avp_value, 0);
-       
+
        if(prev_avp==NULL || prev_avp->flags&AVP_VAL_STR)
                return -1; /* grp avp deleted -- strange */
        group = avp_value.n;
-       
+
        prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, 0);
-       
+
        if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR))
                return -1; /* dst avp deleted -- strange */
-       
-       if(mode==1) {
-               ret = ds_set_state(group, &avp_value.s,
-                               DS_INACTIVE_DST|DS_PROBING_DST|DS_RESET_FAIL_DST, 0, msg);
-       } else if(mode==2) {
-               ret = ds_set_state(group, &avp_value.s, DS_PROBING_DST, 1, msg);
-               if (ret == 0) ret = ds_set_state(group, &avp_value.s,
-                               DS_INACTIVE_DST, 0, msg);
-       } else {
-               ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 1, msg);
-               if (ret == 0) ret = ds_set_state(group, &avp_value.s,
-                               DS_PROBING_DST, 0, msg);
-       }
-       
-       LM_DBG("mode [%d] grp [%d] dst [%.*s]\n", mode, group, avp_value.s.len,
+
+       ret = ds_update_state(msg, group, &avp_value.s, state);
+
+       LM_DBG("state [%d] grp [%d] dst [%.*s]\n", state, group, avp_value.s.len,
                        avp_value.s.s);
-       
+
        return (ret==0)?1:-1;
 }
 
 /**
  *
  */
-int ds_set_state(int group, str *address, int state, int type, struct sip_msg *msg)
+int ds_update_state(sip_msg_t *msg, int group, str *address, int state)
 {
        int i=0;
+       int old_state = 0;
        ds_set_t *idx = NULL;
 
        if(_ds_list==NULL || _ds_list_nr<=0)
@@ -2009,7 +1996,7 @@ int ds_set_state(int group, str *address, int state, int type, struct sip_msg *m
                LM_ERR("the list is null\n");
                return -1;
        }
-       
+
        /* get the index of the set */
        if(ds_get_index(group, &idx)!=0)
        {
@@ -2023,57 +2010,50 @@ int ds_set_state(int group, str *address, int state, int type, struct sip_msg *m
                                && strncasecmp(idx->dlist[i].uri.s, address->s,
                                        address->len)==0)
                {
-         &nbs