Browse Source

modules/dialog2: first version of dialog2

Jason Penton 12 năm trước cách đây
mục cha
commit
651dafa718

+ 18 - 0
modules/dialog2/Makefile

@@ -0,0 +1,18 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=dialog2.so
+LIBS= 
+
+ifeq ($(CFG_NAME),kamailio)
+	DEFS += -DKAMAILIO_MOD_INTERFACE
+endif
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+include ../../Makefile.modules

+ 785 - 0
modules/dialog2/dialog.c

@@ -0,0 +1,785 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+
+
+#include "../../sr_module.h"
+#include "../../lib/srdb1/db.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../ut.h"
+#include "../../pvar.h"
+#include "../../mod_fix.h"
+#include "../../script_cb.h"
+#include "../../lib/kcore/faked_msg.h"
+#include "../../lib/kcore/kstats_wrapper.h"
+#include "../../mem/mem.h"
+#include "../../lib/kmi/mi.h"
+#include "../../lvalue.h"
+#include "../../parser/parse_to.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../rpc_lookup.h"
+#include "../../modules_k/rr/api.h"
+
+#include "dlg_hash.h"
+#include "dlg_timer.h"
+#include "dlg_handlers.h"
+#include "dlg_load.h"
+#include "dlg_cb.h"
+#include "dlg_profile.h"
+#include "dlg_var.h"
+#include "dlg_req_within.h"
+
+MODULE_VERSION
+
+
+static int mod_init(void);
+static int child_init(int rank);
+static void mod_destroy(void);
+
+/* module parameter */
+static int dlg_hash_size = 4096;
+
+static char* rr_param = "did";
+static int dlg_flag = -1;
+
+
+static str timeout_spec = {NULL, 0};
+static int default_timeout = 60 * 60 * 12; /* 12 hours */
+static int seq_match_mode = SEQ_MATCH_STRICT_ID;
+static char* profiles_wv_s = NULL;
+static char* profiles_nv_s = NULL;
+int detect_spirals = 1;
+str dlg_extra_hdrs = {NULL, 0};
+int initial_cbs_inscript = 1;
+
+str dlg_bridge_controller = {"sip:[email protected]", 27};
+
+str ruri_pvar_param = {"$ru", 3};
+pv_elem_t * ruri_param_model = NULL;
+
+struct tm_binds d_tmb;
+struct rr_binds d_rrb;
+pv_spec_t timeout_avp;
+
+
+/* commands wrappers and fixups */
+static int fixup_profile(void** param, int param_no);
+static int fixup_get_profile2(void** param, int param_no);
+static int fixup_get_profile3(void** param, int param_no);
+static int fixup_dlg_terminate(void** param, int param_no);
+static int w_set_dlg_profile(struct sip_msg*, char*, char*);
+static int w_unset_dlg_profile(struct sip_msg*, char*, char*);
+static int w_is_in_profile(struct sip_msg*, char*, char*);
+static int w_get_profile_size2(struct sip_msg*, char*, char*);
+static int w_get_profile_size3(struct sip_msg*, char*, char*, char*);
+static int w_dlg_isflagset(struct sip_msg *msg, char *flag, str *s2);
+static int w_dlg_resetflag(struct sip_msg *msg, char *flag, str *s2);
+static int w_dlg_setflag(struct sip_msg *msg, char *flag, char *s2);
+static int w_dlg_terminate(struct sip_msg*, char*, char*);
+static int w_is_known_dlg(struct sip_msg *);
+
+static cmd_export_t cmds[] = {
+    {"set_dlg_profile", (cmd_function) w_set_dlg_profile, 1, fixup_profile,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"set_dlg_profile", (cmd_function) w_set_dlg_profile, 2, fixup_profile,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"unset_dlg_profile", (cmd_function) w_unset_dlg_profile, 1, fixup_profile,
+        0, FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"unset_dlg_profile", (cmd_function) w_unset_dlg_profile, 2, fixup_profile,
+        0, FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"is_in_profile", (cmd_function) w_is_in_profile, 1, fixup_profile,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"is_in_profile", (cmd_function) w_is_in_profile, 2, fixup_profile,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"get_profile_size", (cmd_function) w_get_profile_size2, 2, fixup_get_profile2,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"get_profile_size", (cmd_function) w_get_profile_size3, 3, fixup_get_profile3,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"dlg_setflag", (cmd_function) w_dlg_setflag, 1, fixup_igp_null,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"dlg_resetflag", (cmd_function) w_dlg_resetflag, 1, fixup_igp_null,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"dlg_isflagset", (cmd_function) w_dlg_isflagset, 1, fixup_igp_null,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"dlg_terminate", (cmd_function) w_dlg_terminate, 1, fixup_dlg_terminate,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"dlg_terminate", (cmd_function) w_dlg_terminate, 2, fixup_dlg_terminate,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"is_known_dlg", (cmd_function) w_is_known_dlg, 0, NULL,
+        0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE},
+    {"load_dlg", (cmd_function) load_dlg, 0, 0, 0, 0},
+    {0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t mod_params[] = {
+    { "hash_size", INT_PARAM, &dlg_hash_size},
+    { "rr_param", STR_PARAM, &rr_param},
+    { "dlg_flag", INT_PARAM, &dlg_flag},
+    { "timeout_avp", STR_PARAM, &timeout_spec.s},
+    { "default_timeout", INT_PARAM, &default_timeout},
+    { "dlg_extra_hdrs", STR_PARAM, &dlg_extra_hdrs.s},
+    //In this new dialog module we always match using DID
+    //{ "dlg_match_mode", INT_PARAM, &seq_match_mode},
+    { "detect_spirals", INT_PARAM, &detect_spirals,},
+    { "profiles_with_value", STR_PARAM, &profiles_wv_s},
+    { "profiles_no_value", STR_PARAM, &profiles_nv_s},
+    { "bridge_controller", STR_PARAM, &dlg_bridge_controller.s},
+    { "ruri_pvar", STR_PARAM, &ruri_pvar_param.s},
+
+    { 0, 0, 0}
+};
+
+static mi_export_t mi_cmds[] = {
+    { "dlg_list", mi_print_dlgs, 0, 0, 0},
+    { "dlg_terminate_dlg", mi_terminate_dlg, 0, 0, 0},
+    { 0, 0, 0, 0, 0}
+    /* TODO: restore old dialog functionality later - also expose dialoig_out cmds, possibly*/
+};
+
+static rpc_export_t rpc_methods[];
+
+struct module_exports exports = {
+    "dialog2", /* module's name */
+    DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* exported functions */
+    mod_params, /* param exports */
+    0, /* exported statistics */
+    mi_cmds, /* exported MI functions */
+    0, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0, /* reply processing function */
+    mod_destroy,
+    child_init /* per-child init function */
+};
+
+static int fixup_profile(void** param, int param_no) {
+    struct dlg_profile_table *profile;
+    pv_elem_t *model = NULL;
+    str s;
+
+    s.s = (char*) (*param);
+    s.len = strlen(s.s);
+    if (s.len == 0) {
+        LM_ERR("param %d is empty string!\n", param_no);
+        return E_CFG;
+    }
+
+    if (param_no == 1) {
+        profile = search_dlg_profile(&s);
+        if (profile == NULL) {
+            LM_CRIT("profile <%s> not definited\n", s.s);
+            return E_CFG;
+        }
+        pkg_free(*param);
+        *param = (void*) profile;
+        return 0;
+    } else if (param_no == 2) {
+        if (pv_parse_format(&s, &model) || model == NULL) {
+            LM_ERR("wrong format [%s] for value param!\n", s.s);
+            return E_CFG;
+        }
+        *param = (void*) model;
+    }
+    return 0;
+}
+
+static int fixup_get_profile2(void** param, int param_no) {
+    pv_spec_t *sp;
+    int ret;
+
+    if (param_no == 1) {
+        return fixup_profile(param, 1);
+    } else if (param_no == 2) {
+        ret = fixup_pvar_null(param, 1);
+        if (ret < 0) return ret;
+        sp = (pv_spec_t*) (*param);
+        if (sp->type != PVT_AVP && sp->type != PVT_SCRIPTVAR) {
+            LM_ERR("return must be an AVP or SCRIPT VAR!\n");
+            return E_SCRIPT;
+        }
+    }
+    return 0;
+}
+
+static int fixup_get_profile3(void** param, int param_no) {
+    if (param_no == 1) {
+        return fixup_profile(param, 1);
+    } else if (param_no == 2) {
+        return fixup_profile(param, 2);
+    } else if (param_no == 3) {
+        return fixup_get_profile2(param, 2);
+    }
+    return 0;
+}
+
+static int fixup_dlg_terminate(void** param, int param_no) {
+    char *val;
+    int n = 0;
+
+    if (param_no == 1) {
+        val = (char*) *param;
+        if (strcasecmp(val, "all") == 0) {
+            n = 2;
+        } else if (strcasecmp(val, "caller") == 0) {
+            n = 0;
+        } else if (strcasecmp(val, "callee") == 0) {
+            n = 1;
+        } else {
+            LM_ERR("invalid param \"%s\"\n", val);
+            return E_CFG;
+        }
+        pkg_free(*param);
+        *param = (void*) (long) n;
+    } else if (param_no == 2) {
+        //fixup str
+        return fixup_str_12(param, param_no);
+    } else {
+        LM_ERR("called with parameter != 1\n");
+        return E_BUG;
+    }
+    return 0;
+}
+
+int load_dlg(struct dlg_binds *dlgb) {
+
+    dlgb->register_dlgcb = register_dlgcb;
+    dlgb->register_dlgcb_nodlg = register_dlgcb_nodlg;
+    dlgb->set_dlg_var = api_set_dlg_variable;
+    dlgb->get_dlg_var = api_get_dlg_variable;
+    dlgb->terminate_dlg = w_api_terminate_dlg;
+    dlgb->get_dlg_expires = api_get_dlg_expires;
+
+    return 1;
+}
+
+static int mod_init(void) {
+    unsigned int n;
+
+    if (register_mi_mod(exports.name, mi_cmds) != 0) {
+        LM_ERR("failed to register MI commands\n");
+        return -1;
+    }
+
+    if (rpc_register_array(rpc_methods) != 0) {
+        LM_ERR("failed to register RPC commands\n");
+        return -1;
+    }
+
+
+    if (faked_msg_init() < 0)
+        return -1;
+
+    if (timeout_spec.s)
+        timeout_spec.len = strlen(timeout_spec.s);
+
+    dlg_bridge_controller.len = strlen(dlg_bridge_controller.s);
+
+
+    /* param checkings */
+    if (dlg_flag == -1) {
+        LM_ERR("no dlg flag set!!\n");
+        return -1;
+    } else if (dlg_flag > MAX_FLAG) {
+        LM_ERR("invalid dlg flag %d!!\n", dlg_flag);
+        return -1;
+    }
+
+    if (rr_param == 0 || rr_param[0] == 0) {
+        LM_ERR("empty rr_param!!\n");
+        return -1;
+    } else if (strlen(rr_param) > MAX_DLG_RR_PARAM_NAME) {
+        LM_ERR("rr_param too long (max=%d)!!\n", MAX_DLG_RR_PARAM_NAME);
+        return -1;
+    }
+
+    if (timeout_spec.s) {
+        if (pv_parse_spec(&timeout_spec, &timeout_avp) == 0
+                && (timeout_avp.type != PVT_AVP)) {
+            LM_ERR("malformed or non AVP timeout "
+                    "AVP definition in '%.*s'\n", timeout_spec.len, timeout_spec.s);
+            return -1;
+        }
+    }
+
+    if (default_timeout <= 0) {
+        LM_ERR("0 default_timeout not accepted!!\n");
+        return -1;
+    }
+
+    if (ruri_pvar_param.s == NULL || *ruri_pvar_param.s == '\0') {
+        LM_ERR("invalid r-uri PV string\n");
+        return -1;
+    }
+    ruri_pvar_param.len = strlen(ruri_pvar_param.s);
+    if (pv_parse_format(&ruri_pvar_param, &ruri_param_model) < 0
+            || ruri_param_model == NULL) {
+        LM_ERR("malformed r-uri PV string: %s\n", ruri_pvar_param.s);
+        return -1;
+    }
+
+    /* update the len of the extra headers */
+    if (dlg_extra_hdrs.s)
+        dlg_extra_hdrs.len = strlen(dlg_extra_hdrs.s);
+
+    if (seq_match_mode != SEQ_MATCH_NO_ID &&
+            seq_match_mode != SEQ_MATCH_FALLBACK &&
+            seq_match_mode != SEQ_MATCH_STRICT_ID) {
+        LM_ERR("invalid value %d for seq_match_mode param!!\n", seq_match_mode);
+        return -1;
+    }
+
+    if (detect_spirals != 0 && detect_spirals != 1) {
+        LM_ERR("invalid value %d for detect_spirals param!!\n", detect_spirals);
+        return -1;
+    }
+
+    /* create profile hashes */
+    if (add_profile_definitions(profiles_nv_s, 0) != 0) {
+        LM_ERR("failed to add profiles without value\n");
+        return -1;
+    }
+    if (add_profile_definitions(profiles_wv_s, 1) != 0) {
+        LM_ERR("failed to add profiles with value\n");
+        return -1;
+    }
+
+    /* load the TM API */
+    if (load_tm_api(&d_tmb) != 0) {
+        LM_ERR("can't load TM API\n");
+        return -1;
+    }
+
+    /* load RR API also */
+    if (load_rr_api(&d_rrb) != 0) {
+        LM_ERR("can't load RR API\n");
+        return -1;
+    }
+
+    /* register callbacks*/
+    /* listen for all incoming requests  */
+    if (d_tmb.register_tmcb(0, 0, TMCB_REQUEST_IN, dlg_onreq, 0, 0) <= 0) {
+        LM_ERR("cannot register TMCB_REQUEST_IN callback\n");
+        return -1;
+    }
+
+    /* listen for all routed requests  */
+    if (d_rrb.register_rrcb(dlg_onroute, 0) < 0) {
+        LM_ERR("cannot register RR callback\n");
+        return -1;
+    }
+
+    if (register_script_cb(profile_cleanup, POST_SCRIPT_CB | REQUEST_CB, 0) < 0) {
+        LM_ERR("cannot regsiter script callback");
+        return -1;
+    }
+    if (register_script_cb(dlg_cfg_cb,
+            PRE_SCRIPT_CB | REQUEST_CB, 0) < 0) {
+        LM_ERR("cannot regsiter pre-script ctx callback\n");
+        return -1;
+    }
+    if (register_script_cb(dlg_cfg_cb,
+            POST_SCRIPT_CB | REQUEST_CB, 0) < 0) {
+        LM_ERR("cannot regsiter post-script ctx callback\n");
+        return -1;
+    }
+
+    if (register_script_cb(spiral_detect_reset, POST_SCRIPT_CB | REQUEST_CB, 0) < 0) {
+        LM_ERR("cannot register req pre-script spiral detection reset callback\n");
+        return -1;
+    }
+
+    if (register_timer(dlg_timer_routine, 0, 1) < 0) {
+        LM_ERR("failed to register timer \n");
+        return -1;
+    }
+
+    /*for testing only!!!! setup timer to call print all dlg every 10 seconds!*/
+    if (register_timer(print_all_dlgs, 0, 10) < 0) {
+        LM_ERR("failed to register timer \n");
+        return -1;
+    }
+
+    /* init handlers */
+    init_dlg_handlers(rr_param, dlg_flag,
+            timeout_spec.s ? &timeout_avp : 0, default_timeout, seq_match_mode);
+
+    /* init timer */
+    if (init_dlg_timer(dlg_ontimeout) != 0) {
+        LM_ERR("cannot init timer list\n");
+        return -1;
+    }
+
+    /* sanitize dlg_hash_zie */
+    if (dlg_hash_size < 1) {
+        LM_WARN("hash_size is smaller "
+                "then 1  -> rounding from %d to 1\n",
+                dlg_hash_size);
+        dlg_hash_size = 1;
+    }
+
+    /* initialized the hash table */
+    for (n = 0; n < (8 * sizeof (n)); n++) {
+        if (dlg_hash_size == (1 << n))
+            break;
+        if (n && dlg_hash_size < (1 << n)) {
+            LM_WARN("hash_size is not a power "
+                    "of 2 as it should be -> rounding from %d to %d\n",
+                    dlg_hash_size, 1 << (n - 1));
+            dlg_hash_size = 1 << (n - 1);
+        }
+    }
+
+    if (init_dlg_table(dlg_hash_size) < 0) {
+        LM_ERR("failed to create hash table\n");
+        return -1;
+    }
+
+    destroy_dlg_callbacks(DLGCB_LOADED);
+
+    return 0;
+}
+
+static int child_init(int rank) {
+    return 0;
+}
+
+static void mod_destroy(void) {
+    destroy_dlg_table();
+    destroy_dlg_timer();
+    destroy_dlg_callbacks(DLGCB_CREATED | DLGCB_LOADED);
+    destroy_dlg_handlers();
+    destroy_dlg_profiles();
+}
+
+static int w_set_dlg_profile(struct sip_msg *msg, char *profile, char *value) {
+    pv_elem_t *pve;
+    str val_s;
+
+    pve = (pv_elem_t *) value;
+
+    if (((struct dlg_profile_table*) profile)->has_value) {
+        if (pve == NULL || pv_printf_s(msg, pve, &val_s) != 0 ||
+                val_s.len == 0 || val_s.s == NULL) {
+            LM_WARN("cannot get string for value\n");
+            return -1;
+        }
+        if (set_dlg_profile(msg, &val_s,
+                (struct dlg_profile_table*) profile) < 0) {
+            LM_ERR("failed to set profile");
+            return -1;
+        }
+    } else {
+        if (set_dlg_profile(msg, NULL,
+                (struct dlg_profile_table*) profile) < 0) {
+            LM_ERR("failed to set profile");
+            return -1;
+        }
+    }
+    return 1;
+}
+
+static int w_unset_dlg_profile(struct sip_msg *msg, char *profile, char *value) {
+    pv_elem_t *pve;
+    str val_s;
+
+    pve = (pv_elem_t *) value;
+
+    if (((struct dlg_profile_table*) profile)->has_value) {
+        if (pve == NULL || pv_printf_s(msg, pve, &val_s) != 0 ||
+                val_s.len == 0 || val_s.s == NULL) {
+            LM_WARN("cannot get string for value\n");
+            return -1;
+        }
+        if (unset_dlg_profile(msg, &val_s,
+                (struct dlg_profile_table*) profile) < 0) {
+            LM_ERR("failed to unset profile");
+            return -1;
+        }
+    } else {
+        if (unset_dlg_profile(msg, NULL,
+                (struct dlg_profile_table*) profile) < 0) {
+            LM_ERR("failed to unset profile");
+            return -1;
+        }
+    }
+    return 1;
+}
+
+static int w_is_in_profile(struct sip_msg *msg, char *profile, char *value) {
+    pv_elem_t *pve;
+    str val_s;
+
+    pve = (pv_elem_t *) value;
+
+    if (pve != NULL && ((struct dlg_profile_table*) profile)->has_value) {
+        if (pv_printf_s(msg, pve, &val_s) != 0 ||
+                val_s.len == 0 || val_s.s == NULL) {
+            LM_WARN("cannot get string for value\n");
+            return -1;
+        }
+        return is_dlg_in_profile(msg, (struct dlg_profile_table*) profile,
+                &val_s);
+    } else {
+        return is_dlg_in_profile(msg, (struct dlg_profile_table*) profile,
+                NULL);
+    }
+}
+
+/**
+ * get dynamic name profile size
+ */
+static int w_get_profile_size3(struct sip_msg *msg, char *profile,
+        char *value, char *result) {
+    pv_elem_t *pve;
+    str val_s;
+    pv_spec_t *sp_dest;
+    unsigned int size;
+    pv_value_t val;
+
+    if (result != NULL) {
+        pve = (pv_elem_t *) value;
+        sp_dest = (pv_spec_t *) result;
+    } else {
+        pve = NULL;
+        sp_dest = (pv_spec_t *) value;
+    }
+    if (pve != NULL && ((struct dlg_profile_table*) profile)->has_value) {
+        if (pv_printf_s(msg, pve, &val_s) != 0 ||
+                val_s.len == 0 || val_s.s == NULL) {
+            LM_WARN("cannot get string for value\n");
+            return -1;
+        }
+        size = get_profile_size((struct dlg_profile_table*) profile, &val_s);
+    } else {
+        size = get_profile_size((struct dlg_profile_table*) profile, NULL);
+    }
+
+    memset(&val, 0, sizeof (pv_value_t));
+    val.flags = PV_VAL_INT | PV_TYPE_INT;
+    val.ri = (int) size;
+
+    if (sp_dest->setf(msg, &sp_dest->pvp, (int) EQ_T, &val) < 0) {
+        LM_ERR("setting profile PV failed\n");
+        return -1;
+    }
+
+    return 1;
+}
+
+/**
+ * get static name profile size
+ */
+static int w_get_profile_size2(struct sip_msg *msg, char *profile, char *result) {
+    return w_get_profile_size3(msg, profile, result, NULL);
+}
+
+static int w_dlg_setflag(struct sip_msg *msg, char *flag, char *s2) {
+    dlg_ctx_t *dctx;
+    int val;
+
+    if (fixup_get_ivalue(msg, (gparam_p) flag, &val) != 0) {
+        LM_ERR("no flag value\n");
+        return -1;
+    }
+    if (val < 0 || val > 31)
+        return -1;
+    if ((dctx = dlg_get_dlg_ctx()) == NULL)
+        return -1;
+
+    dctx->flags |= 1 << val;
+    if (dctx->dlg)
+        dctx->dlg->sflags |= 1 << val;
+    return 1;
+}
+
+static int w_dlg_resetflag(struct sip_msg *msg, char *flag, str *s2) {
+    dlg_ctx_t *dctx;
+    int val;
+
+    if (fixup_get_ivalue(msg, (gparam_p) flag, &val) != 0) {
+        LM_ERR("no flag value\n");
+        return -1;
+    }
+    if (val < 0 || val > 31)
+        return -1;
+
+    if ((dctx = dlg_get_dlg_ctx()) == NULL)
+        return -1;
+
+    dctx->flags &= ~(1 << val);
+    if (dctx->dlg)
+        dctx->dlg->sflags &= ~(1 << val);
+    return 1;
+}
+
+static int w_dlg_isflagset(struct sip_msg *msg, char *flag, str *s2) {
+    dlg_ctx_t *dctx;
+    int val;
+
+    if (fixup_get_ivalue(msg, (gparam_p) flag, &val) != 0) {
+        LM_ERR("no flag value\n");
+        return -1;
+    }
+    if (val < 0 || val > 31)
+        return -1;
+
+    if ((dctx = dlg_get_dlg_ctx()) == NULL)
+        return -1;
+
+    if (dctx->dlg)
+        return (dctx->dlg->sflags & (1 << val)) ? 1 : -1;
+    return (dctx->flags & (1 << val)) ? 1 : -1;
+}
+
+static int w_dlg_terminate(struct sip_msg *msg, char *side, char *r) {
+    struct dlg_cell *dlg;
+    str reason = {0, 0};
+
+    int n = (int) (long) side;
+
+    //check if a reason was given
+    if (r) {
+        if (get_str_fparam(&reason, msg, (fparam_t *) r) < 0) {
+            LM_ERR("failed to recover reason parameter\n");
+            return -1;
+        }
+    }
+
+    dlg = get_current_dialog(msg);
+    //dlg_get_ctx_dialog();
+    if (!dlg) {
+        LM_DBG("Unable to find dialog for terminate\n");
+        return -1;
+    }
+
+    if (!dlg_terminate(dlg, msg, &reason, n, NULL)) {
+        LM_DBG("Failed to terminate dialog\n");
+        return -1;
+    }
+
+    return 1;
+}
+
+/*
+ * Wrapper around is_known_dlg().
+ */
+
+static int w_is_known_dlg(struct sip_msg *msg) {
+    return is_known_dlg(msg);
+}
+
+/**************************** RPC functions ******************************/
+/*!
+ * \brief Helper method that outputs a dialog via the RPC interface
+ * \see rpc_print_dlg
+ * \param rpc RPC node that should be filled
+ * \param c RPC void pointer
+ * \param dlg printed dialog
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+static inline void internal_rpc_print_dlg(rpc_t *rpc, void *c, struct dlg_cell *dlg, void *dh)
+{
+	void* dlg_outs_h;
+	struct dlg_cell_out* dlg_out;
+
+	rpc->struct_add(dh, "dd", "Entry", dlg->h_entry, "Id", dlg->h_id);
+	rpc->struct_add(dh, "SSSSSSSsd{",
+			"RURI", &dlg->req_uri,
+			"From", &dlg->from_uri,
+			"Call-ID", &dlg->callid,
+			"Caller Contact", &dlg->caller_contact,
+			"Caller Route Set", &dlg->caller_route_set,
+			"Dialog-ID", &dlg->did,
+			"From Tag", &dlg->from_tag,
+			"State", state_to_char(dlg->state),
+			"Ref", dlg->ref,
+			"dlg_outs", &dlg_outs_h
+			);
+
+	lock_get(dlg->dlg_out_entries_lock);
+
+	dlg_out = dlg->dlg_entry_out.first;
+	while (dlg_out) {
+		rpc->struct_add(dlg_outs_h, "dd", "Entry", dlg_out->h_entry, "Id", dlg_out->h_id);
+		dlg_out = dlg_out->next;
+	}
+
+	lock_release(dlg->dlg_out_entries_lock);
+
+	/*now traverse dlg_outs*/
+
+}
+
+/*!
+ * \brief Helper function that outputs all dialogs via the RPC interface
+ * \see rpc_print_dlgs
+ * \param rpc RPC node that should be filled
+ * \param c RPC void pointer
+ * \param with_context if 1 then the dialog context will be also printed
+ */
+static void internal_rpc_print_dlgs(rpc_t *rpc, void *c)
+{
+	struct dlg_cell *dlg;
+	unsigned int i;
+
+	void *ah;
+	void *dh;		/*beginning struct holding dialogs*/
+
+	if (rpc->add(c, "{", &ah) < 0) {
+		rpc->fault(c, 500, "Internal error creating top rpc");
+		return;
+	}
+	if (rpc->struct_add(ah, "d{", "Size", (int) d_table->size, "Dialogs", &dh) < 0) {
+		rpc->fault(c, 500, "Internal error creating inner struct");
+		return;
+	}
+
+	for( i=0 ; i<d_table->size ; i++ ) {
+		dlg_lock( d_table, &(d_table->entries[i]) );
+
+		for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) {
+			internal_rpc_print_dlg(rpc, c, dlg, dh);
+		}
+		dlg_unlock( d_table, &(d_table->entries[i]) );
+	}
+}
+
+static const char *rpc_print_dlgs_doc[2] = {
+	"Print all dialogs", 0
+};
+
+static void rpc_print_dlgs(rpc_t *rpc, void *c) {
+	internal_rpc_print_dlgs(rpc, c);
+}
+
+/*static const char *rpc_end_dlg_entry_id_doc[2] = {
+    "End a given dialog based on [h_entry] [h_id]", 0
+};
+
+
+static void rpc_end_dlg_entry_id(rpc_t *rpc, void *c) {
+    unsigned int h_entry, h_id;
+    struct dlg_cell * dlg = NULL;
+    str rpc_extra_hdrs = {NULL, 0};
+
+    if (rpc->scan(c, "ddS", &h_entry, &h_id, &rpc_extra_hdrs) < 2) return;
+
+    dlg = lookup_dlg(h_entry, h_id);
+    if (dlg) {
+        //dlg_bye_all(dlg, (rpc_extra_hdrs.len>0)?&rpc_extra_hdrs:NULL);
+        unref_dlg(dlg, 1);
+    }
+}*/
+
+
+static rpc_export_t rpc_methods[] = {
+	{"dlg2.list", rpc_print_dlgs, rpc_print_dlgs_doc, 0},
+    //{"dlg.end_dlg", rpc_end_dlg_entry_id, rpc_end_dlg_entry_id_doc, 0},
+    {0, 0, 0, 0}
+};
+

+ 282 - 0
modules/dialog2/dlg_cb.c

@@ -0,0 +1,282 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-14  initial version (bogdan)
+ * 2008-04-04  added direction reporting in dlg callbacks (bogdan)
+ * 2008-04-14  DLGCB_CREATED may be registered before the module 
+ *              initialization (bogdan)
+ * 2008-04-15  added new type of callback to be triggered when dialogs are 
+ *              loaded from DB (bogdan)
+ */
+
+
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "dlg_hash.h"
+#include "dlg_cb.h"
+
+
+static struct dlg_head_cbl* create_cbs = 0;
+
+static struct dlg_head_cbl* load_cbs = 0;
+
+static struct dlg_cb_params params = {NULL, NULL, DLG_DIR_NONE, NULL, NULL};
+
+
+#define POINTER_CLOSED_MARKER  ((void *)(-1))
+
+
+static void run_load_callback(struct dlg_callback *cb);
+
+
+
+static struct dlg_head_cbl* init_dlg_callback(void)
+{
+	struct dlg_head_cbl *new_cbs;
+
+	new_cbs = (struct dlg_head_cbl*)shm_malloc(sizeof(struct dlg_head_cbl));
+	if (new_cbs==0) {
+		LM_ERR("no more shm mem\n");
+		return 0;
+	}
+	new_cbs->first = 0;
+	new_cbs->types = 0;
+
+	return new_cbs;
+}
+
+
+void destroy_dlg_callbacks_list(struct dlg_callback *cb)
+{
+	struct dlg_callback *cb_t;
+
+	while(cb) {
+		cb_t = cb;
+		cb = cb->next;
+		if (cb_t->callback_param_free && cb_t->param) {
+			cb_t->callback_param_free(cb_t->param);
+			cb_t->param = NULL;
+		}
+		shm_free(cb_t);
+	}
+}
+
+
+void destroy_dlg_callbacks(unsigned int types)
+{
+	if (types&DLGCB_CREATED) {
+		if (create_cbs && create_cbs!=POINTER_CLOSED_MARKER) {
+			destroy_dlg_callbacks_list(create_cbs->first);
+			shm_free(create_cbs);
+			create_cbs = POINTER_CLOSED_MARKER;
+		}
+	}
+	if (types&DLGCB_LOADED) {
+		if (load_cbs && load_cbs!=POINTER_CLOSED_MARKER) {
+			destroy_dlg_callbacks_list(load_cbs->first);
+			shm_free(load_cbs);
+			load_cbs = POINTER_CLOSED_MARKER;
+		}
+	}
+}
+
+int register_dlgcb_nodlg(str *callid, str *ftag, str *ttag, 
+                        int types, dialog_cb f,
+                        void *param, param_free_cb ff )
+{
+    struct dlg_cell *dlg;
+    
+    unsigned int dir = DLG_DIR_NONE;
+    dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count!
+    
+    if (!dlg) {
+        LM_ERR("Can't find dialog to register callback\n");
+        return -1;
+    }
+    
+    int ret = register_dlgcb(dlg, types, f, param, ff);
+    unref_dlg(dlg, 1);
+    
+    return ret;
+}
+
+
+int register_dlgcb(struct dlg_cell *dlg, int types, dialog_cb f,
+										void *param, param_free_cb ff )
+{
+	struct dlg_callback *cb;
+
+	if ( types&DLGCB_LOADED ) {
+		if (types!=DLGCB_LOADED) {
+			LM_CRIT("DLGCB_LOADED type must be register alone!\n");
+			return -1;
+		}
+	} else if ( types&DLGCB_CREATED ) {
+		if (types!=DLGCB_CREATED) {
+			LM_CRIT("DLGCB_CREATED type must be register alone!\n");
+			return -1;
+		}
+	} else {
+		if (dlg==0) {
+			LM_CRIT("non-DLGCB_CREATED type "
+				"must be register to a dialog (dlg missing)!\n");
+			return -1;
+		}
+	}
+	cb = (struct dlg_callback*)shm_malloc(sizeof(struct dlg_callback));
+	if (cb==0) {
+		LM_ERR("no more shm mem\n");
+		return -1;
+	}
+
+	cb->types = types;
+	cb->callback = f;
+	cb->param = param;
+	cb->callback_param_free = ff;
+
+	if ( types==DLGCB_CREATED ) {
+		if (load_cbs==POINTER_CLOSED_MARKER) {
+			LM_CRIT("DLGCB_CREATED type registered after shutdown!?!\n");
+			goto error;
+		}
+		if (create_cbs==0) {
+			/* not initialized yet */
+			if ( (create_cbs=init_dlg_callback())==NULL ) {
+				LM_ERR("no more shm mem\n");
+				goto error;
+			}
+		}
+		cb->next = create_cbs->first;
+		create_cbs->first = cb;
+		create_cbs->types |= types;
+	} else if (types==DLGCB_LOADED) {
+		if (load_cbs==POINTER_CLOSED_MARKER) {
+			/* run the callback on the spot */
+			run_load_callback(cb);
+			destroy_dlg_callbacks_list(cb);
+			return 0;
+		}
+		if (load_cbs==0) {
+			/* not initialized yet */
+			if ( (load_cbs=init_dlg_callback())==NULL ) {
+				LM_ERR("no more shm mem\n");
+				goto error;
+			}
+		}
+		cb->next = load_cbs->first;
+		load_cbs->first = cb;
+		load_cbs->types |= types;
+	} else {
+		cb->next = dlg->cbs.first;
+		dlg->cbs.first = cb;
+		dlg->cbs.types |= types;
+	}
+
+	return 0;
+error:
+	shm_free(cb);
+	return -1;
+}
+
+
+static void run_load_callback(struct dlg_callback *cb)
+{
+	struct dlg_cell *dlg;
+	unsigned int i;
+
+	params.req = NULL;
+	params.rpl = NULL;
+	params.direction = DLG_DIR_NONE;
+	params.param = &cb->param;
+
+	for( i=0 ; i<d_table->size ; i++ ) {
+		for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next )
+			cb->callback( dlg, DLGCB_LOADED, &params );
+	}
+
+	return;
+}
+
+
+void run_load_callbacks( void )
+{
+	struct dlg_callback *cb;
+
+	if (load_cbs && load_cbs!=POINTER_CLOSED_MARKER) {
+		for ( cb=load_cbs->first; cb; cb=cb->next )
+			run_load_callback( cb );
+	}
+
+	return;
+}
+
+
+void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg)
+{
+	struct dlg_callback *cb;
+
+	if (create_cbs==NULL || create_cbs->first==NULL)
+		return;
+
+	params.req = msg;
+	params.rpl = NULL;
+	/* initial request goes DOWNSTREAM all the time */
+	params.direction = DLG_DIR_DOWNSTREAM;
+	/* avoid garbage due static structure */
+	params.param = NULL;
+	params.dlg_data = NULL;
+
+	for ( cb=create_cbs->first; cb; cb=cb->next)  {
+		LM_DBG("dialog=%p\n",dlg);
+		params.param = &cb->param;
+		cb->callback( dlg, DLGCB_CREATED, &params );
+	}
+	return;
+}
+
+
+void run_dlg_callbacks( int type ,
+						struct dlg_cell *dlg,
+						struct sip_msg *req,
+						struct sip_msg *rpl,
+						unsigned int dir, void *dlg_data)
+{
+	struct dlg_callback *cb;
+
+	params.req = req;
+	params.rpl = rpl;
+	params.direction = dir;
+	params.dlg_data = dlg_data;
+
+	if (dlg->cbs.first==0 || ((dlg->cbs.types)&type)==0 )
+		return;
+
+	for ( cb=dlg->cbs.first; cb; cb=cb->next)  {
+            if ( (cb->types)&type ) {
+			LM_DBG("dialog=%p, type=%d\n", dlg, type);
+			params.param = &cb->param;
+			cb->callback( dlg, type, &params );
+		}
+	}
+	return;
+}

+ 151 - 0
modules/dialog2/dlg_cb.h

@@ -0,0 +1,151 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Voice Sistem SRLs
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-11  initial version (bogdan)
+ * 2008-04-04  added direction reporting in dlg callbacks (bogdan)
+ * 2008-04-14  added new type of callback to be triggered when dialogs are 
+ *              loaded from DB (bogdan)
+ * 2008-04-17  added new type of callback to be triggered right before the
+ *              dialog is destroyed (deleted from memory) (bogdan)
+ */
+
+#ifndef _DIALOG_DLG_CB_H_
+#define _DIALOG_DLG_CB_H_
+
+#include "../../parser/msg_parser.h"
+
+struct dlg_cell;
+
+struct dlg_cb_params {
+	struct sip_msg* req;       /* sip request msg related to the callback event */
+        struct sip_msg* rpl;       /* sip reply msg related to the callback event */
+	unsigned int direction;    /* direction of the sip msg */
+	void *dlg_data;            /* generic paramter, specific to callback */
+	void **param;              /* parameter passed at callback registration*/
+};
+
+/* callback function prototype */
+typedef void (dialog_cb) (struct dlg_cell* dlg, int type, 
+		struct dlg_cb_params * params);
+/* function to free the callback param */
+typedef void (param_free_cb) (void *param);
+/* register callback function prototype */
+typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types,
+		dialog_cb f, void *param, param_free_cb ff);
+
+typedef int (*register_dlgcb_nodlg_f)(str *callid, str *ftag, str *ttag, int cb_types,
+		dialog_cb f, void *param, param_free_cb ff);
+
+/* method to set a variable within a dialog */
+//typedef int (*set_dlg_variable_f)( struct dlg_cell* dlg,
+//                                   str* key,
+//                                   str* val);
+typedef int (*set_dlg_variable_f)( str* callid, str* ftag, str* ttag,
+                                   str* key,
+                                   str* val);
+/* method to get a variable from a dialog */
+//typedef str* (*get_dlg_variable_f)( struct dlg_cell* dlg,
+//                                    str* key);
+
+typedef str* (*get_dlg_variable_f)( str *callid, str *ftag, str *ttag,
+                                    str* key);
+
+typedef struct dlg_cell* (*get_current_dlg_f)( struct sip_msg* msg);
+
+#define DLGCB_LOADED          (1<<0)
+#define DLGCB_CREATED         (1<<1)
+#define DLGCB_FAILED          (1<<2)
+#define DLGCB_CONFIRMED_NA    (1<<3)
+#define DLGCB_CONFIRMED       (1<<4)
+#define DLGCB_REQ_WITHIN      (1<<5)
+#define DLGCB_TERMINATED      (1<<6)
+#define DLGCB_EXPIRED         (1<<7)
+#define DLGCB_EARLY           (1<<8)
+#define DLGCB_RESPONSE_FWDED  (1<<9)
+#define DLGCB_RESPONSE_WITHIN (1<<10)
+#define DLGCB_MI_CONTEXT      (1<<11)
+#define DLGCB_RPC_CONTEXT     (1<<12)
+#define DLGCB_DESTROY         (1<<13)
+#define DLGCB_SPIRALED        (1<<14)
+#define DLGCB_TERMINATED_CONFIRMED (1<<14)
+
+struct dlg_callback {
+	int types;
+	dialog_cb* callback;
+	void *param;
+	param_free_cb* callback_param_free;
+	struct dlg_callback* next;
+};
+
+
+struct dlg_head_cbl {
+	struct dlg_callback *first;
+	int types;
+};
+
+
+void destroy_dlg_callbacks(unsigned int type);
+
+void destroy_dlg_callbacks_list(struct dlg_callback *cb);
+
+int register_dlgcb_nodlg(str *callid, str *ftag, str *ttag, int types, dialog_cb f, void *param, param_free_cb ff );
+int register_dlgcb( struct dlg_cell* dlg, int types, dialog_cb f, void *param, param_free_cb ff);
+
+void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg);
+
+void run_dlg_callbacks( int type ,
+                        struct dlg_cell *dlg,
+                        struct sip_msg *req,
+                        struct sip_msg *rpl,
+                        unsigned int dir,
+                        void *dlg_data);
+
+void run_load_callbacks( void );
+
+
+/*!
+ * \brief Function that returns valid SIP message from given dialog callback parameter struct
+ * \param cb_params dialog callback parameter struct
+ * \return pointer to valid SIP message if existent, NULL otherwise
+ */
+static inline struct sip_msg *dlg_get_valid_msg(struct dlg_cb_params *cb_params)
+{
+	struct sip_msg *msg;
+
+	if (cb_params == NULL) {
+		LM_ERR("no dialog parameters given\n");
+		return NULL;
+	}
+
+	msg = cb_params->req;
+	if (msg == NULL) {
+		msg = cb_params->rpl;
+		if (msg == NULL || msg == FAKED_REPLY) {
+			return NULL;
+		}
+	}
+
+	return msg;
+};
+
+#endif

+ 1425 - 0
modules/dialog2/dlg_handlers.c

@@ -0,0 +1,1425 @@
+/*!
+ * \file
+ * \brief Functions related to dialog handling
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#include <string.h>
+#include <time.h>
+
+#include "../../trim.h"
+#include "../../pvar.h"
+#include "../../timer.h"
+#include "../../lib/kcore/statistics.h"
+#include "../../action.h"
+#include "../../script_cb.h"
+#include "../../lib/kcore/faked_msg.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_cseq.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_rr.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../modules_k/rr/api.h"
+#include "dlg_hash.h"
+#include "dlg_timer.h"
+#include "dlg_cb.h"
+#include "dlg_handlers.h"
+#include "dlg_req_within.h"
+#include "dlg_profile.h"
+#include "dlg_var.h"
+
+static str rr_param; /*!< record-route parameter for matching */
+static int dlg_flag; /*!< flag for dialog tracking */
+static pv_spec_t *timeout_avp; /*!< AVP for timeout setting */
+static int default_timeout; /*!< default dialog timeout */
+static int seq_match_mode; /*!< dlg_match mode */
+static int shutdown_done = 0; /*!< 1 when destroy_dlg_handlers was called */
+extern int detect_spirals;
+extern int initial_cbs_inscript;
+int spiral_detected = -1;
+
+extern struct rr_binds d_rrb; /*!< binding to record-routing module */
+extern struct tm_binds d_tmb;
+
+
+/* statistic variables */
+extern stat_var *early_dlgs; /*!< number of early dialogs */
+extern stat_var *processed_dlgs; /*!< number of processed dialogs */
+extern stat_var *expired_dlgs; /*!< number of expired dialogs */
+extern stat_var *failed_dlgs; /*!< number of failed dialogs */
+
+extern pv_elem_t *ruri_param_model; /*!< pv-string to get r-uri */
+
+static unsigned int CURR_DLG_LIFETIME = 0; /*!< current dialog lifetime */
+static unsigned int CURR_DLG_STATUS = 0; /*!< current dialog state */
+static unsigned int CURR_DLG_ID = 0xffffffff; /*!< current dialog id */
+
+
+/*! size of the dialog record-route parameter */
+#define RR_DLG_PARAM_SIZE  (2*2*sizeof(int)+3+MAX_DLG_RR_PARAM_NAME)
+/*! separator inside the record-route paramter */
+#define DLG_SEPARATOR      '.'
+
+/*!
+ * \brief Initialize the dialog handlers
+ * \param rr_param_p added record-route parameter
+ * \param dlg_flag_p dialog flag
+ * \param timeout_avp_p AVP for timeout setting
+ * \param default_timeout_p default timeout
+ * \param seq_match_mode_p matching mode
+ */
+void init_dlg_handlers(char *rr_param_p, int dlg_flag_p, pv_spec_t *timeout_avp_p, int default_timeout_p, int seq_match_mode_p) {
+    rr_param.s = rr_param_p;
+    rr_param.len = strlen(rr_param.s);
+
+    dlg_flag = 1 << dlg_flag_p;
+
+    timeout_avp = timeout_avp_p;
+    default_timeout = default_timeout_p;
+    seq_match_mode = seq_match_mode_p;
+}
+
+/*!
+ * \brief Shutdown operation of the module
+ */
+void destroy_dlg_handlers(void) {
+    shutdown_done = 1;
+}
+
+/*!
+ * \brief Add record-route parameter for dialog tracking
+ * \param req SIP request
+ * \param entry dialog hash entry
+ * \param id dialog hash id
+ * \return 0 on success, -1 on failure
+ */
+static inline int add_dlg_rr_param(struct dlg_cell *dlg, struct sip_msg *req, unsigned int entry, unsigned int id) {
+    static char buf[RR_DLG_PARAM_SIZE];
+    str s;
+    int n;
+    char *p;
+
+    s.s = p = buf;
+
+    *(p++) = ';';
+    memcpy(p, rr_param.s, rr_param.len);
+    p += rr_param.len;
+    *(p++) = '=';
+
+    char *did = p;
+
+    n = RR_DLG_PARAM_SIZE - (p - buf);
+    if (int2reverse_hex(&p, &n, entry) == -1)
+        return -1;
+
+    *(p++) = DLG_SEPARATOR;
+
+    n = RR_DLG_PARAM_SIZE - (p - buf);
+    if (int2reverse_hex(&p, &n, id) == -1)
+        return -1;
+
+    s.len = p - buf;
+
+    if (d_rrb.add_rr_param(req, &s) < 0) {
+        LM_ERR("failed to add rr param\n");
+        return -1;
+    }
+
+
+
+    //add the did into the dlg structure
+    int did_len = p - did;
+
+    if (dlg->did.s) {
+        if (dlg->did.len < did_len) {
+            shm_free(dlg->did.s);
+            dlg->did.s = (char*) shm_malloc(did_len);
+            if (dlg->did.s == NULL) {
+                LM_ERR("failed to add did to dlg_cell struct\n");
+                return -1;
+            }
+        }
+    } else {
+        dlg->did.s = (char*) shm_malloc(did_len);
+        if (dlg->did.s == NULL) {
+            LM_ERR("failed to add did to dlg_cell struct\n");
+            return -1;
+        }
+    }
+    memcpy(dlg->did.s, did, did_len);
+    dlg->did.len = did_len;
+
+
+
+    return 0;
+}
+
+/*!
+ * \brief Parse SIP message and populate leg informations
+ *
+ * Parse SIP message and populate leg informations.
+ * \param dlg the dialog to add cseq, contact & record_route
+ * \param msg sip message
+ * \param t transaction
+ * \param leg type of the call leg
+ * \param tag SIP To tag
+ * \return 0 on success, -1 on failure
+ * \note for a request: get record route in normal order, for a reply get
+ * in reverse order, skipping the ones from the request and the proxies' own
+ */
+int populate_leg_info(struct dlg_cell *dlg, struct sip_msg *msg,
+        struct cell* t, unsigned int leg, str *tag) {
+    unsigned int skip_recs;
+    str cseq;
+    str contact;
+    str rr_set;
+    struct socket_info* callee_bind_address = NULL;
+
+    if (leg == DLG_CALLER_LEG)
+        dlg->caller_bind_addr = msg->rcv.bind_address;
+    else
+        callee_bind_address = msg->rcv.bind_address;
+
+
+
+    /* extract the cseq number as string from the request or response*/
+    //TO DO - can pair the cseqs here to make sure that the response and request are in sync
+
+    if ((!msg->cseq && (parse_headers(msg, HDR_CSEQ_F, 0) < 0 || !msg->cseq))
+            || !msg->cseq->parsed) {
+        LM_ERR("bad sip message or missing CSeq hdr :-/\n");
+        goto error0;
+    }
+    cseq = (get_cseq(msg))->number;
+
+
+    /* extract the contact address */
+    if (!msg->contact && (parse_headers(msg, HDR_CONTACT_F, 0) < 0 || !msg->contact)) {
+        LM_ERR("bad sip message or missing Contact hdr\n");
+        goto error0;
+    }
+    if (parse_contact(msg->contact) < 0 ||
+            ((contact_body_t *) msg->contact->parsed)->contacts == NULL ||
+            ((contact_body_t *) msg->contact->parsed)->contacts->next != NULL) {
+        LM_ERR("bad Contact HDR\n");
+        goto error0;
+    }
+    contact = ((contact_body_t *) msg->contact->parsed)->contacts->uri;
+
+    /* extract the RR parts */
+    if (!msg->record_route && (parse_headers(msg, HDR_EOH_F, 0) < 0)) {
+        LM_ERR("failed to parse record route header\n");
+        goto error0;
+    }
+
+    if (leg == DLG_CALLER_LEG) {
+        skip_recs = 0;
+    } else {
+        skip_recs = 0;
+        /* was the 200 OK received or local generated */
+        /*skip_recs = dlg->from_rr_nb +
+                ((t->relayed_reply_branch >= 0) ?
+                ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_R2) ? 2 :
+                ((t->uac[t->relayed_reply_branch].flags & TM_UAC_FLAG_RR) ? 1 : 0))
+                : 0);
+         * */
+    }
+
+    if (msg->record_route) {
+        if (print_rr_body(msg->record_route, &rr_set, leg,
+                &skip_recs) != 0) {
+            LM_ERR("failed to print route records \n");
+            goto error0;
+        }
+    } else {
+        rr_set.s = 0;
+        rr_set.len = 0;
+    }
+
+    if (leg == DLG_CALLER_LEG)
+        dlg->from_rr_nb = skip_recs;
+
+    LM_DBG("route_set %.*s, contact %.*s, cseq %.*s and bind_addr %.*s\n",
+            rr_set.len, rr_set.s, contact.len, contact.s,
+            cseq.len, cseq.s,
+            msg->rcv.bind_address->sock_str.len,
+            msg->rcv.bind_address->sock_str.s);
+
+    if (dlg_set_leg_info(dlg, tag, &rr_set, &contact, &cseq, callee_bind_address, leg) != 0) {
+        LM_ERR("dlg_set_leg_info failed\n");
+        if (rr_set.s) pkg_free(rr_set.s);
+        goto error0;
+    }
+
+    if (rr_set.s) pkg_free(rr_set.s);
+
+    return 0;
+error0:
+    return -1;
+}
+
+/*!
+ * \brief Function that executes BYE reply callbacks
+ * \param t transaction, unused
+ * \param type type of the callback, should be TMCB_RESPONSE_FWDED
+ * \param param saved dialog structure inside the callback
+ */
+static void dlg_terminated_confirmed(struct cell* t,
+        int type,
+        struct tmcb_params* params) {
+    if (!params || !params->req || !params->param) {
+        LM_ERR("invalid parameters!\n");
+        return;
+    }
+
+    struct dlg_cell* dlg = (struct dlg_cell*) *params->param;
+
+    if (!dlg) {
+        LM_ERR("failed to get dialog from params!\n");
+        return;
+    }
+    /* dialog termination confirmed (BYE reply) */
+    run_dlg_callbacks(DLGCB_TERMINATED_CONFIRMED,
+            dlg,
+            params->req,
+            params->rpl,
+            DLG_DIR_UPSTREAM,
+            0);
+}
+
+/*!
+ * \brief Execute callback for the BYE request and register callback for the BYE reply
+ * \param req request message
+ * \param dlg corresponding dialog
+ * \param dir message direction
+ */
+static void dlg_terminated(struct sip_msg* req,
+        struct dlg_cell* dlg,
+        unsigned int dir) {
+    if (!req) {
+        LM_ERR("request is empty!");
+        return;
+    }
+
+    if (!dlg) {
+        LM_ERR("dialog is empty!");
+        return;
+    }
+
+    /* dialog terminated (BYE) */
+    run_dlg_callbacks(DLGCB_TERMINATED, dlg, req, NULL, dir, 0);
+
+    /* register callback for the coresponding reply */
+    LM_DBG("Registering tmcb1\n");
+    if (d_tmb.register_tmcb(req,
+            0,
+            TMCB_RESPONSE_OUT,
+            dlg_terminated_confirmed,
+            (void*) dlg,
+            0) <= 0) {
+        LM_ERR("cannot register response callback for BYE request\n");
+        return;
+    }
+}
+
+static void unlink_dlgouts_from_cb(struct cell* t, int type, struct tmcb_params *param) {
+    struct dlg_cell *dlg = (struct dlg_cell *) (*param->param);
+
+    if (!dlg)
+        return;
+
+    if (t && t->fwded_totags && t->fwded_totags->tag.len > 0) {
+        LM_DBG("unlink_dlgouts_from_cb: transaction [%.*s] can now be removed IFF it has been marked for deletion\n", t->fwded_totags->tag.len, t->fwded_totags->tag.s);
+        dlg_remove_dlg_out_tag(dlg, &t->fwded_totags->tag);
+    }
+}
+
+/*!
+ * \brief Function that is registered as TM callback and called on replies
+ *
+ * Function that is registered as TM callback and called on replies. It
+ * parses the reply and set the appropriate event. This is then used to
+ * update the dialog state, run eventual dialog callbacks and save or
+ * update the necessary informations about the dialog.
+ * \see next_state_dlg
+ * \param t transaction, unused
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) {
+    struct dlg_cell *dlg;
+    struct dlg_cell_out *dlg_out = 0;
+
+    int new_state, old_state, unref, event;
+    str to_tag, to_uri;
+    struct sip_msg *req = param->req;
+    struct sip_msg *rpl = param->rpl;
+    struct dlg_entry_out* dlg_entry_out = 0;
+
+    if (t && t->fwded_totags)
+        LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] and TO is [%.*s]\n", type, t->fwded_totags->tag.len, t->fwded_totags->tag.s);
+    else
+        LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i]\n", type);
+
+    dlg = (struct dlg_cell *) (*param->param);
+    if (shutdown_done || dlg == 0)
+        return;
+
+    if (t) {
+        dlg->transaction = t;
+    }
+
+    LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id);
+
+    if (type == TMCB_RESPONSE_FWDED) {
+        // The state does not change, but the msg is mutable in this callback
+        LM_DBG("TMCB_RESPONSE_FWDED from TM received");
+        run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
+        return;
+    }
+
+    if (type == TMCB_RESPONSE_READY) {
+        LM_DBG("TMCB_RESPONSE_READY\n");
+        return;
+    }
+
+    if (type == TMCB_RESPONSE_OUT) {
+        if (rpl == FAKED_REPLY) {
+            LM_ERR("Faked reply\n");
+            return;
+        }
+
+        // get to tag
+        LM_DBG("Extracting to-tag from reply");
+        if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) {
+            LM_ERR("bad reply or missing TO hdr :-/\n");
+            to_tag.s = 0;
+            to_tag.len = 0;
+        } else {
+            //populate to uri for this branch.
+            to_uri = get_to(rpl)->uri;
+
+            to_tag = get_to(rpl)->tag_value;
+            if (to_tag.s == 0 || to_tag.len == 0) {
+                LM_ERR("missing TAG param in TO hdr :-/\n");
+                to_tag.s = 0;
+                to_tag.len = 0;
+                //Here we assume that the transaction module timer will remove any early dialogs
+                return;
+            }
+        }
+
+        LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s);
+    }
+
+    if (type == TMCB_DESTROY)
+        event = DLG_EVENT_TDEL;
+    else if (param->code < 200)
+        event = DLG_EVENT_RPL1xx;
+    else if (param->code < 300)
+        event = DLG_EVENT_RPL2xx;
+    else
+        event = DLG_EVENT_RPL3xx;
+
+    LM_DBG("Calling next_state_dlg and event is %i\n", event);
+    next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag);
+
+    if (type == TMCB_RESPONSE_OUT) {
+        LM_DBG("Checking if there is an existing dialog_out entry with same to-tag");
+
+        dlg_entry_out = &dlg->dlg_entry_out;
+
+        lock_get(dlg->dlg_out_entries_lock);
+        dlg_out = dlg_entry_out->first;
+
+        LM_DBG("Scanning dlg_entry_out list for dlg_out");
+        while (dlg_out) {
+            //Check if there is an already dialog_out entry with same To-tag
+            if (dlg_out->to_tag.len == to_tag.len &&
+                    memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) {
+                //Found a dialog_out entry with same to_tag!
+                LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
+                break;
+            }
+            dlg_out = dlg_out->next;
+        }
+        lock_release(dlg->dlg_out_entries_lock);
+
+        if (!dlg_out) {
+            LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg);
+            dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag);
+
+            link_dlg_out(dlg, dlg_out, 0);
+
+            /* save callee's cseq, caller cseq, callee contact and callee record route*/
+            if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) {
+                LM_ERR("could not add further info to the dlg out\n");
+            }
+
+            if (!dlg_out) {
+                LM_ERR("failed to create new dialog out structure\n");
+                //TODO do something on this error!
+
+            }
+        } else {
+            //This dlg_out already exists, update cseq and contact if present
+
+            LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s);
+
+            if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq ||
+                    !rpl->cseq->parsed) {
+                LM_ERR("bad sip message or missing CSeq hdr :-/\n");
+            }
+            dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag));
+
+
+            /* extract the contact address to update if present*/
+            if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) {
+                LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n");
+            }
+            else if (parse_contact(rpl->contact) < 0 ||
+                    ((contact_body_t *) rpl->contact->parsed)->contacts == NULL ||
+                    ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) {
+                LM_ERR("Can not update callee contact: bad Contact HDR\n");
+            }
+            else
+            {
+                str contact;
+                contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri;
+                dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag));
+            }
+
+        }
+    }
+
+    if (new_state == DLG_STATE_EARLY) {
+        run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
+        return;
+    }
+
+    LM_DBG("new state is %i and old state is %i\n", new_state, old_state);
+
+    if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) {
+        LM_DBG("dialog %p confirmed \n", dlg);
+        //Remove all the other entries in dialog_out for the same dialog after TM expires the transaction
+        //(not before in order to absorb late in-early-dialog requests).
+
+        //remove all other dlg_out objects
+        if (dlg_out) {
+            if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, unlink_dlgouts_from_cb, (void*) dlg, NULL) < 0) {
+                LM_ERR("failed to register deletion delay function\n");
+                LM_DBG("Removing all other DLGs");
+                dlg_remove_dlg_out(dlg_out, dlg, 0);
+            } else {
+                //mark the outs for deletion
+                dlg_remove_dlg_out(dlg_out, dlg, 1);
+
+            }
+        } else {
+            LM_ERR("There is no dlg_out structure - this is bad\n");
+            //TODO: add error handling here
+        }
+
+        /* set start time */
+        dlg->start_ts = (unsigned int) (time(0));
+
+        /* save the settings to the database,
+         * if realtime saving mode configured- save dialog now
+         * else: the next time the timer will fire the update*/
+        dlg->dflags |= DLG_FLAG_NEW;
+
+        if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) {
+            LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] "
+                    "with clid '%.*s' and tags '%.*s' \n",
+                    dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state,
+                    dlg->callid.len, dlg->callid.s,
+                    dlg->from_tag.len, dlg->from_tag.s);
+
+        } else {
+            ref_dlg(dlg, 1);
+        }
+
+        run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
+
+        if (unref) unref_dlg(dlg, unref);
+        return;
+    }
+
+    if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) {
+        //This is a concurrently confirmed call
+        LM_DBG("This is a concurrently confirmed call.");
+        //Create a new Dialog ID token “X”
+        //Not sure how to do this so just going to use existing Did and add an X character to it
+        str new_did;
+        create_concurrent_did(dlg, &new_did);
+
+        //assign new did to the created or updated dialog_out entry.
+        update_dlg_out_did(dlg_out, &new_did);
+
+        //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did
+        //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires
+
+        struct dlg_cell *new_dlg = 0;
+        new_dlg = build_new_dlg(&(dlg->callid) /*callid*/,
+                &(dlg->from_uri) /*from uri*/,
+                &(dlg->from_tag)/*from_tag*/,
+                &(dlg->req_uri) /*r-uri*/);
+
+        //assign new did to dlg_in
+        update_dlg_did(new_dlg, &new_did);
+
+        if (new_dlg == 0) {
+            LM_ERR("failed to create new dialog\n");
+            return;
+        }
+
+        //link the new_dlg with dlg_out object
+        link_dlg_out(new_dlg, dlg_out, 0);
+
+    }
+
+    if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) {
+        LM_DBG("dialog %p failed (negative reply)\n", dlg);
+        /* dialog setup not completed (3456XX) */
+        run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
+        /* do unref */
+        if (unref)
+            unref_dlg(dlg, unref);
+
+        return;
+    }
+
+    if (unref) unref_dlg(dlg, unref);
+
+    return;
+}
+
+/*!
+ * \brief Helper function that run dialog callbacks on forwarded requests
+ * \see dlg_seq_up_onreply
+ * \see dlg_seq_down_onreply
+ * \param t transaction, unused
+ * \param type type of the callback, should be TMCB_RESPONSE_FWDED
+ * \param param saved dialog structure inside the callback
+ * \param direction direction of the request
+ */
+static void dlg_seq_onreply_helper(struct cell* t, int type,
+        struct tmcb_params *param, const int direction) {
+    struct dlg_cell *dlg;
+
+    dlg = (struct dlg_cell *) (*param->param);
+    if (shutdown_done || dlg == 0)
+        return;
+
+    if (type == TMCB_RESPONSE_FWDED) {
+        run_dlg_callbacks(DLGCB_RESPONSE_WITHIN,
+                dlg,
+                param->req,
+                param->rpl,
+                direction,
+                0);
+        return;
+    }
+
+    return;
+}
+
+/*!
+ * \brief Run dialog callbacks on forwarded requests in upstream direction
+ * \see dlg_seq_onreply_helper
+ * \param t transaction, unused
+ * \param type type of the callback, should be TMCB_RESPONSE_FWDED
+ * \param param saved dialog structure inside the callback
+ */
+static void dlg_seq_up_onreply(struct cell* t, int type, struct tmcb_params *param) {
+    return dlg_seq_onreply_helper(t, type, param, DLG_DIR_UPSTREAM);
+}
+
+/*!
+ * \brief Run dialog callbacks on forwarded requests in downstream direction
+ * \see dlg_seq_onreply_helper
+ * \param t transaction, unused
+ * \param type type of the callback, should be TMCB_RESPONSE_FWDED
+ * \param param saved dialog structure inside the callback
+ */
+static void dlg_seq_down_onreply(struct cell* t, int type, struct tmcb_params *param) {
+    return dlg_seq_onreply_helper(t, type, param, DLG_DIR_DOWNSTREAM);
+}
+
+/*!
+ * \brief Return the timeout for a dialog
+ * \param req SIP message
+ * \return value from timeout AVP if present or default timeout
+ */
+inline static int get_dlg_timeout(struct sip_msg *req) {
+    pv_value_t pv_val;
+
+    if (timeout_avp) {
+        if (pv_get_spec_value(req, timeout_avp, &pv_val) == 0 &&
+                pv_val.flags & PV_VAL_INT && pv_val.ri > 0) {
+            return pv_val.ri;
+        }
+        LM_DBG("invalid AVP value, using default timeout\n");
+    }
+    return default_timeout;
+}
+
+/*!
+ * \brief Helper function to get the necessary content from SIP message
+ * \param req SIP request
+ * \param callid found callid
+ * \param ftag found from tag
+ * \param ttag found to tag
+ * \param with_ttag flag set if to tag must be found for success
+ * \return 0 on success, -1 on failure
+ */
+static inline int pre_match_parse(struct sip_msg *req, str *callid,
+        str *ftag, str *ttag, int with_ttag) {
+    if (parse_headers(req, HDR_CALLID_F | HDR_TO_F, 0) < 0 || !req->callid ||
+            !req->to) {
+        LM_ERR("bad request or missing CALLID/TO hdr :-/\n");
+        return -1;
+    }
+
+    if (get_to(req)->tag_value.len == 0) {
+        if (with_ttag == 1) {
+            /* out of dialog request with preloaded Route headers; ignore. */
+            return -1;
+        } else {
+            ttag->s = NULL;
+            ttag->len = 0;
+        }
+    } else {
+        *ttag = get_to(req)->tag_value;
+    }
+
+    if (parse_from_header(req) < 0 || get_from(req)->tag_value.len == 0) {
+        LM_ERR("failed to get From header\n");
+        return -1;
+    }
+
+    /* callid */
+    *callid = req->callid->body;
+    trim(callid);
+    /* from tag */
+    *ftag = get_from(req)->tag_value;
+    return 0;
+}
+
+/*!
+ * \brief Function that is registered as TM callback and called on requests
+ * \see dlg_new_dialog
+ * \param t transaction, used to created the dialog
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+void dlg_onreq(struct cell* t, int type, struct tmcb_params *param) {
+    struct sip_msg *req = param->req;
+
+    if ((req->flags & dlg_flag) != dlg_flag)
+        return;
+    if (current_dlg_pointer != NULL)
+        return;
+    dlg_new_dialog(req, t, 1);
+}
+
+/*!
+ * \brief Unreference a new dialog, helper function for dlg_onreq
+ * \see dlg_onreq
+ * \param dialog unreferenced dialog
+ */
+static void unref_new_dialog(void *dialog) {
+    struct tmcb_params p;
+
+    memset(&p, 0, sizeof (struct tmcb_params));
+    p.param = (void*) &dialog;
+    dlg_onreply(0, TMCB_DESTROY, &p);
+}
+
+/*!
+ * \brief Unreference a dialog (small wrapper to take care of shutdown)
+ * \see unref_dlg
+ * \param dialog unreferenced dialog
+ */
+static void unreference_dialog(void *dialog) {
+    // if the dialog table is gone, it means the system is shutting down.
+    if (!dialog || !d_table)
+        return;
+    unref_dlg((struct dlg_cell*) dialog, 1);
+}
+
+/*!
+ * \brief Dummy callback just to keep the compiler happy
+ * \param t unused
+ * \param type unused
+ * \param param unused
+ */
+void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param) {
+    return;
+}
+
+/*!
+ * \brief Register a transaction on a dialog
+ * \param t transaction
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static int store_dlg_in_tm(struct sip_msg* msg,
+        struct cell* t,
+        struct dlg_cell *dlg) {
+    if (!msg || msg == FAKED_REPLY || !t || !dlg) {
+        LM_ERR("invalid parameter msg(%p), t(%p), dlg(%p)\n", msg, t, dlg);
+        return -1;
+    }
+
+    if (get_dialog_from_tm(t)) {
+        LM_NOTICE("dialog %p is already set for this transaction!\n", dlg);
+        return 1;
+    }
+
+    // facilitate referencing of dialog through TMCB_MAX
+    if (d_tmb.register_tmcb(msg,
+            t,
+            TMCB_MAX,
+            dlg_tmcb_dummy,
+            (void*) dlg, unreference_dialog) < 0) {
+        LM_ERR("failed cache in T the shortcut to dlg %p\n", dlg);
+        return -3;
+    }
+
+    // registering succeeded, we must increase the reference counter
+    ref_dlg(dlg, 1);
+
+    return 0;
+}
+
+/*!
+ * \brief Callback to register a transaction on a dialog
+ * \param t transaction, unused
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static void store_dlg_in_tm_cb(struct cell* t,
+        int type,
+        struct tmcb_params *param) {
+    struct dlg_cell *dlg = (struct dlg_cell *) (*param->param);
+
+    struct sip_msg* msg = param->rpl;
+    if (msg == NULL || msg == FAKED_REPLY) {
+        msg = param->req;
+    }
+
+    store_dlg_in_tm(msg, t, dlg);
+}
+
+/*!
+ * \brief Create a new dialog from a sip message
+ *
+ * Create a new dialog from a SIP message, register a callback
+ * to keep track of the dialog with help of the tm module.
+ * This function is either called from the request callback, or
+ * from the dlg_manage function in the configuration script.
+ * \see dlg_onreq
+ * \see w_dlg_manage
+ * \param msg SIP message
+ * \param t transaction
+ * \return 0 on success, -1 on failure
+ */
+int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs) {
+    struct dlg_cell *dlg;
+    str s;
+    str callid;
+    str ftag;
+    str ttag;
+    str req_uri;
+    unsigned int dir;
+
+    LM_DBG("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s);
+
+    if (current_dlg_pointer != NULL)
+        return -1;
+
+    if (req->first_line.u.request.method_value == METHOD_CANCEL)
+        return -1;
+
+    if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) {
+        LM_WARN("pre-matching failed\n");
+        return -1;
+    }
+
+    if (ttag.s != 0 && ttag.len != 0)
+        return -1;
+
+    if (pv_printf_s(req, ruri_param_model, &req_uri) < 0) {
+        LM_ERR("error - cannot print the r-uri format\n");
+        return -1;
+    }
+    trim(&req_uri);
+
+    if (detect_spirals) {
+        if (spiral_detected == 1)
+            return 0;
+
+        dir = DLG_DIR_NONE;
+
+        dlg = get_dlg(&callid, &ftag, &ttag, &dir);
+        if (dlg) {
+            LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
+                    callid.len, callid.s);
+            spiral_detected = 1;
+
+            if (run_initial_cbs)
+                run_dlg_callbacks(DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0);
+
+            //Add did to rr header for all spiralled requested INVITEs
+            if (req->first_line.u.request.method_value == METHOD_INVITE) {
+                if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) {
+                    LM_ERR("failed to add RR param\n");
+
+                }
+            }
+
+            // get_dlg has incremented the ref count by 1
+            unref_dlg(dlg, 1);
+            goto finish;
+        }
+    }
+    spiral_detected = 0;
+
+
+    LM_DBG("Building new Dialog for call-id %.*s\n", callid.len, callid.s);
+    LM_DBG("SIP Method: %.*s  \n", req->first_line.u.request.method.len, req->first_line.u.request.method.s);
+    dlg = build_new_dlg(&callid /*callid*/,
+            &(get_from(req)->uri) /*from uri*/,
+            &ftag/*from_tag*/,
+            &req_uri /*r-uri*/);
+
+    if (dlg == 0) {
+        LM_ERR("failed to create new dialog\n");
+        return -1;
+    }
+
+    /* save caller's tag, cseq, contact and record route*/
+    if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
+            &(get_from(req)->tag_value)) != 0) {
+        LM_ERR("could not add further info to the dialog\n");
+        shm_free(dlg);
+        lock_destroy(dlg->dlg_out_entries_lock);
+        lock_dealloc(dlg->dlg_out_entries_lock);
+        return -1;
+    }
+
+    dlg->transaction = t;
+
+    /* Populate initial varlist: */
+    dlg->vars = get_local_varlist_pointer(req, 1);
+
+    link_dlg(dlg, 0);
+    if (run_initial_cbs) run_create_callbacks(dlg, req);
+
+    //Dialog will *always* use DID cookie so no need for match mode anymore
+    if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) {
+        LM_ERR("failed to add RR param\n");
+        goto error;
+    }
+
+    if (d_tmb.register_tmcb(req, t,
+            TMCB_RESPONSE_READY | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_OUT,
+            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);
+
+    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;
+
+finish:
+    if (t) {
+        // transaction exists ==> keep ref counter large enough to
+        // avoid premature cleanup and ensure proper dialog referencing
+        if (store_dlg_in_tm(req, t, dlg) < 0) {
+            LM_ERR("failed to store dialog in transaction\n");
+            goto error;
+        }
+    } else {
+        // no transaction exists ==> postpone work until we see the
+        // request being forwarded statefully
+        if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED,
+                store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) {
+            LM_ERR("failed to register callback for storing dialog in transaction\n");
+            goto error;
+        }
+    }
+
+    LM_DBG("Setting current dialog\n");
+    set_current_dialog(req, dlg);
+    _dlg_ctx.dlg = dlg;
+    ref_dlg(dlg, 1);
+    return 0;
+
+error:
+    LM_DBG("Error in build_new_dlg");
+    if (!spiral_detected)
+        unref_dlg(dlg, 1); // undo ref regarding linking
+    return -1;
+}
+
+/*!
+ * \brief Parse the record-route parameter, to get dialog information back
+ * \param p start of parameter string
+ * \param end end of parameter string
+ * \param h_entry found dialog hash entry
+ * \param h_id found dialog hash id
+ * \return 0 on success, -1 on failure
+ */
+static inline int parse_dlg_rr_param(char *p, char *end, int *h_entry, int *h_id) {
+    char *s;
+
+    for (s = p; p < end && *p != DLG_SEPARATOR; p++);
+    if (*p != DLG_SEPARATOR) {
+        LM_ERR("malformed rr param '%.*s'\n", (int) (long) (end - s), s);
+        return -1;
+    }
+
+    if (reverse_hex2int(s, p - s, (unsigned int*) h_entry) < 0) {
+        LM_ERR("invalid hash entry '%.*s'\n", (int) (long) (p - s), s);
+        return -1;
+    }
+
+    if (reverse_hex2int(p + 1, end - (p + 1), (unsigned int*) h_id) < 0) {
+        LM_ERR("invalid hash id '%.*s'\n", (int) (long) (end - (p + 1)), p + 1);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*!
+ * \brief Update the saved CSEQ information in dialog from SIP message
+ * \param dlg updated dialog
+ * \param req SIP request
+ * \param dir direction of request, must DLG_DIR_UPSTREAM or DLG_DIR_DOWNSTREAM
+ * \return 0 on success, -1 on failure
+ */
+static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req,
+        unsigned int dir, str *to_tag) {
+    if ((!req->cseq && parse_headers(req, HDR_CSEQ_F, 0) < 0) || !req->cseq ||
+            !req->cseq->parsed) {
+        LM_ERR("bad sip message or missing CSeq hdr :-/\n");
+        return -1;
+    }
+
+    if (dir == DLG_DIR_UPSTREAM) {
+        return dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(req))->number), to_tag);
+    } else if (dir == DLG_DIR_DOWNSTREAM) {
+        return dlg_update_cseq(dlg, DLG_CALLER_LEG, &((get_cseq(req))->number), to_tag);
+    } else {
+        LM_CRIT("dir is not set!\n");
+        return -1;
+    }
+}
+
+/*!
+ * \brief Function that is registered as RR callback for dialog tracking
+ *
+ * Function that is registered as RR callback for dialog tracking. It
+ * sets the appropriate events after the SIP method and run the state
+ * machine to update the dialog state. It updates then the saved
+ * dialogs and also the statistics.
+ * \param req SIP request
+ * \param route_params record-route parameter
+ * \param param unused
+ */
+void dlg_onroute(struct sip_msg* req, str *route_params, void *param) {
+    struct dlg_cell *dlg;
+    str val, callid, ftag, ttag;
+    int h_entry, h_id, new_state, old_state, unref, event, timeout;
+    unsigned int dir;
+    int ret = 0;
+
+    if (current_dlg_pointer != NULL)
+        return;
+
+    /* skip initial requests - they may end up here because of the
+     * preloaded route */
+    if ((!req->to && parse_headers(req, HDR_TO_F, 0) < 0) || !req->to) {
+        LM_ERR("bad request or missing TO hdr :-/\n");
+        return;
+    }
+    if (get_to(req)->tag_value.len == 0)
+        return;
+
+    dlg = 0;
+    dir = DLG_DIR_NONE;
+
+    if (seq_match_mode != SEQ_MATCH_NO_ID) {
+        if (d_rrb.get_route_param(req, &rr_param, &val) != 0) {
+            LM_DBG("Route param '%.*s' not found\n", rr_param.len, rr_param.s);
+            if (seq_match_mode == SEQ_MATCH_STRICT_ID)
+                return;
+        } else {
+            LM_DBG("route param is '%.*s' (len=%d)\n", val.len, val.s, val.len);
+
+            if (parse_dlg_rr_param(val.s, val.s + val.len, &h_entry, &h_id) < 0)
+                return;
+
+            dlg = lookup_dlg(h_entry, h_id);
+            if (dlg == 0) {
+                LM_WARN("unable to find dialog for %.*s "
+                        "with route param '%.*s' [%u:%u]\n",
+                        req->first_line.u.request.method.len,
+                        req->first_line.u.request.method.s,
+                        val.len, val.s, h_entry, h_id);
+                if (seq_match_mode == SEQ_MATCH_STRICT_ID)
+                    return;
+            } else {
+                if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0) {
+                    // lookup_dlg has incremented the ref count by 1
+                    unref_dlg(dlg, 1);
+                    return;
+                }
+                if (match_dialog(dlg, &callid, &ftag, &ttag, &dir) == 0) {
+                    LM_WARN("tight matching failed for %.*s with callid='%.*s'/%d, "
+                            "ftag='%.*s'/%d, ttag='%.*s'/%d and direction=%d\n",
+                            req->first_line.u.request.method.len,
+                            req->first_line.u.request.method.s,
+                            callid.len, callid.s, callid.len,
+                            ftag.len, ftag.s, ftag.len,
+                            ttag.len, ttag.s, ttag.len, dir);
+                    LM_WARN("dialog identification elements are callid='%.*s'/%d, "
+                            "caller tag='%.*s'/%d\n",
+                            dlg->callid.len, dlg->callid.s, dlg->callid.len,
+                            dlg->from_tag.len, dlg->from_tag.s,
+                            dlg->from_tag.len);
+                    // lookup_dlg has incremented the ref count by 1
+                    unref_dlg(dlg, 1);
+
+                    // Reset variables in order to do a lookup based on SIP-Elements.
+                    dlg = 0;
+                    dir = DLG_DIR_NONE;
+
+                    if (seq_match_mode == SEQ_MATCH_STRICT_ID)
+                        return;
+                }
+            }
+        }
+    }
+
+    if (dlg == 0) {
+        if (pre_match_parse(req, &callid, &ftag, &ttag, 1) < 0)
+            return;
+        /* TODO - try to use the RR dir detection to speed up here the
+         * search -bogdan */
+        dlg = get_dlg(&callid, &ftag, &ttag, &dir);
+        if (!dlg) {
+            LM_DBG("Callid '%.*s' not found\n",
+                    req->callid->body.len, req->callid->body.s);
+            return;
+        }
+    }
+
+    /* set current dialog - re-use ref increment from dlg_get() above */
+    set_current_dialog(req, dlg);
+    _dlg_ctx.dlg = dlg;
+
+    if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED,
+            store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) {
+        LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n");
+    }
+
+    /* run state machine */
+    switch (req->first_line.u.request.method_value) {
+        case METHOD_PRACK:
+            event = DLG_EVENT_REQPRACK;
+            break;
+        case METHOD_ACK:
+            event = DLG_EVENT_REQACK;
+            break;
+        case METHOD_BYE:
+            event = DLG_EVENT_REQBYE;
+            break;
+        default:
+            event = DLG_EVENT_REQ;
+    }
+
+    next_state_dlg(dlg, event, &old_state, &new_state, &unref, 0);
+
+    LM_DBG("unref after next state is %i\n", unref);
+    CURR_DLG_ID = req->id;
+    CURR_DLG_LIFETIME = (unsigned int) (time(0)) - dlg->start_ts;
+    CURR_DLG_STATUS = new_state;
+
+    /* run actions for the transition */
+    if (event == DLG_EVENT_REQBYE && new_state == DLG_STATE_DELETED &&
+            old_state != DLG_STATE_DELETED) {
+        LM_DBG("BYE successfully processed\n");
+        /* remove from timer */
+        ret = remove_dialog_timer(&dlg->tl);
+        if (ret < 0) {
+            LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] "
+                    "with clid '%.*s' and tags '%.*s'\n",
+                    dlg, dlg->h_entry, dlg->h_id,
+                    dlg->callid.len, dlg->callid.s,
+                    dlg->from_tag.len, dlg->from_tag.s);
+
+        } else if (ret > 0) {
+            LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] "
+                    "with clid '%.*s' and tags '%.*s' \n",
+                    dlg, dlg->h_entry, dlg->h_id,
+                    dlg->callid.len, dlg->callid.s,
+                    dlg->from_tag.len, dlg->from_tag.s);
+
+        } else {
+            unref++;
+        }
+        /* dialog terminated (BYE) */
+        dlg_terminated(req, dlg, dir);
+        unref_dlg(dlg, unref);
+
+        return;
+    }
+
+    if ((event == DLG_EVENT_REQ || event == DLG_EVENT_REQACK)
+            && new_state == DLG_STATE_CONFIRMED) {
+
+        timeout = get_dlg_timeout(req);
+        if (timeout != default_timeout) {
+            dlg->lifetime = timeout;
+        }
+        if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) {
+            LM_ERR("failed to update dialog lifetime\n");
+        }
+        if (update_cseqs(dlg, req, dir, &ttag) != 0) {
+            LM_ERR("cseqs update failed\n");
+        } else {
+            dlg->dflags |= DLG_FLAG_CHANGED;
+        }
+
+        if (old_state != DLG_STATE_CONFIRMED) {
+            LM_DBG("confirming ACK successfully processed\n");
+
+            /* confirming ACK request */
+            run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, NULL, dir, 0);
+        } else {
+            LM_DBG("sequential request successfully processed\n");
+
+            /* within dialog request */
+            run_dlg_callbacks(DLGCB_REQ_WITHIN, dlg, req, NULL, dir, 0);
+
+            if ((event != DLG_EVENT_REQACK) &&
+                    (dlg->cbs.types) & DLGCB_RESPONSE_WITHIN) {
+                /* ref the dialog as registered into the transaction callback.
+                 * unref will be done when the callback will be destroyed */
+                ref_dlg(dlg, 1);
+                /* register callback for the replies of this request */
+                if (d_tmb.register_tmcb(req, 0, TMCB_RESPONSE_FWDED,
+                        (dir == DLG_DIR_UPSTREAM) ? dlg_seq_down_onreply : dlg_seq_up_onreply,
+                        (void*) dlg, unreference_dialog) < 0) {
+                    LM_ERR("failed to register TMCB (2)\n");
+                    unref_dlg(dlg, 1);
+                }
+            }
+        }
+    }
+
+    if (new_state == DLG_STATE_CONFIRMED && old_state != DLG_STATE_CONFIRMED) {
+        dlg->dflags |= DLG_FLAG_CHANGED;
+    }
+    return;
+}
+
+/*!
+ * \brief Timer function that removes expired dialogs, run timeout route
+ * \param tl dialog timer list
+ */
+void dlg_ontimeout(struct dlg_tl *tl) {
+    struct dlg_cell *dlg;
+    int new_state, old_state, unref;
+    struct sip_msg *fmsg;
+
+    /* get the dialog tl payload */
+    dlg = ((struct dlg_cell*) ((char *) (tl) -
+            (unsigned long) (&((struct dlg_cell*) 0)->tl)));
+
+    if (dlg->toroute > 0 && dlg->toroute < main_rt.entries
+            && main_rt.rlist[dlg->toroute] != NULL) {
+        fmsg = faked_msg_next();
+        if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE) > 0) {
+            dlg_set_ctx_dialog(dlg);
+            LM_DBG("executing route %d on timeout\n", dlg->toroute);
+            set_route_type(REQUEST_ROUTE);
+            run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0);
+            dlg_set_ctx_dialog(0);
+            exec_post_script_cb(fmsg, REQUEST_CB_TYPE);
+        }
+    }
+
+    if ((dlg->dflags & DLG_FLAG_TOBYE)
+            && (dlg->state == DLG_STATE_CONFIRMED)) {
+        //TODO: dlg_bye_all(dlg, NULL);
+        unref_dlg(dlg, 1);
+        return;
+    }
+
+    next_state_dlg(dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref, 0);
+
+    if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) {
+        LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s'\n",
+                dlg->callid.len, dlg->callid.s,
+                dlg->from_tag.len, dlg->from_tag.s);
+
+
+        /* dialog timeout */
+        run_dlg_callbacks(DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0);
+
+        unref_dlg(dlg, unref + 1);
+    } else {
+        unref_dlg(dlg, 1);
+    }
+
+    return;
+}
+
+/*!
+ * \brief Function that returns the dialog lifetime as pseudo-variable
+ * \param msg SIP message
+ * \param param pseudo-variable parameter
+ * \param res pseudo-variable result
+ * \return 0 on success, -1 on failure
+ */
+int pv_get_dlg_lifetime(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+    int l = 0;
+    char *ch = NULL;
+
+    if (msg == NULL || res == NULL)
+        return -1;
+
+    if (CURR_DLG_ID != msg->id)
+        return pv_get_null(msg, param, res);
+
+    res->ri = CURR_DLG_LIFETIME;
+    ch = int2str((unsigned long) res->ri, &l);
+
+    res->rs.s = ch;
+    res->rs.len = l;
+
+    res->flags = PV_VAL_STR | PV_VAL_INT | PV_TYPE_INT;
+
+    return 0;
+}
+
+/*!
+ * \brief Function that returns the dialog state as pseudo-variable
+ * \param msg SIP message
+ * \param param pseudo-variable parameter
+ * \param res pseudo-variable result
+ * \return 0 on success, -1 on failure
+ */
+int pv_get_dlg_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+    int l = 0;
+    char *ch = NULL;
+
+    if (msg == NULL || res == NULL)
+        return -1;
+
+    if (CURR_DLG_ID != msg->id)
+        return pv_get_null(msg, param, res);
+
+    res->ri = CURR_DLG_STATUS;
+    ch = int2str((unsigned long) res->ri, &l);
+
+    res->rs.s = ch;
+    res->rs.len = l;
+
+    res->flags = PV_VAL_STR | PV_VAL_INT | PV_TYPE_INT;
+
+    return 0;
+}
+
+/*!
+ * \brief Helper function that prints all the properties of a dialog including all the dlg_out's
+ * \param dlg dialog cell
+ * \return void
+ */
+
+void internal_print_all_dlg(struct dlg_cell *dlg) {
+
+    LM_DBG("Trying to get lock for printing\n");
+    lock_get(dlg->dlg_out_entries_lock);
+
+    struct dlg_cell_out *dlg_out;
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+
+    LM_DBG("----------------------------");
+    LM_DBG("Dialog call-id: %.*s\n", dlg->callid.len, dlg->callid.s);
+    LM_DBG("Dialog state: %d\n", dlg->state);
+    LM_DBG("Dialog ref counter: %d\n", dlg->ref);
+    LM_DBG("Dialog did: %.*s\n", dlg->did.len, dlg->did.s);
+    LM_DBG("Dialog from_tag: %.*s\n", dlg->from_tag.len, dlg->from_tag.s);
+    LM_DBG("Dialog from_uri: %.*s\n", dlg->from_uri.len, dlg->from_uri.s);
+    LM_DBG("Dialog caller contact: %.*s\n", dlg->caller_contact.len, dlg->caller_contact.s);
+    LM_DBG("Dialog first request cseq: %.*s\n", dlg->first_req_cseq.len, dlg->first_req_cseq.s);
+    LM_DBG("Dialog caller route set: %.*s\n", dlg->caller_route_set.len, dlg->caller_route_set.s);
+    LM_DBG("Dialog lifetime: %d\n", dlg->lifetime);
+    LM_DBG("Dialog bind_address: %.*s\n", dlg->caller_bind_addr->sock_str.len, dlg->caller_bind_addr->sock_str.s);
+
+    dlg_out = d_entry_out->first;
+
+    while (dlg_out) {
+
+        LM_DBG("----------");
+        LM_DBG("Dialog out did: %.*s\n", dlg_out->did.len, dlg_out->did.s);
+        LM_DBG("Dialog out to_tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
+        LM_DBG("Dialog out caller cseq: %.*s\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s);
+        LM_DBG("Dialog out callee cseq: %.*s\n", dlg_out->callee_cseq.len, dlg_out->callee_cseq.s);
+        LM_DBG("Dialog out callee contact: %.*s\n", dlg_out->callee_contact.len, dlg_out->callee_contact.s);
+        LM_DBG("Dialog out callee route set: %.*s\n", dlg_out->callee_route_set.len, dlg_out->callee_route_set.s);
+
+        LM_DBG("----------");
+        dlg_out = dlg_out->next;
+    }
+
+    LM_DBG("Releasing lock for dlgout\n");
+    lock_release(dlg->dlg_out_entries_lock);
+
+    LM_DBG("----------------------------");
+
+}
+
+/*!
+ * \brief Helper function that prints information for all dialogs
+ * \return void
+ */
+
+void print_all_dlgs() {
+    //print all dialog information  - this is just for testing and is set to happen every 10 seconds
+
+    struct dlg_cell *dlg;
+    unsigned int i;
+
+
+    LM_DBG("********************");
+    LM_DBG("printing %i dialogs\n", d_table->size);
+
+    for (i = 0; i < d_table->size; i++) {
+        dlg_lock(d_table, &(d_table->entries[i]));
+
+        for (dlg = d_table->entries[i].first; dlg; dlg = dlg->next) {
+            internal_print_all_dlg(dlg);
+        }
+        dlg_unlock(d_table, &(d_table->entries[i]));
+    }
+    LM_DBG("********************");
+
+}
+

+ 185 - 0
modules/dialog2/dlg_handlers.h

@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2006 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-14  initial version (bogdan)
+ * 2007-03-06  syncronized state machine added for dialog state. New tranzition
+ *             design based on events; removed num_1xx and num_2xx (bogdan)
+ * 2011-10     added support for forked calls (richard and jason)
+ */
+
+
+/*!
+ * \file
+ * \brief Functions related to dialog handling
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#ifndef _DIALOG_DLG_HANDLERS_H_
+#define _DIALOG_DLG_HANDLERS_H_
+
+#include "../../parser/msg_parser.h"
+#include "../../str.h"
+#include "../../pvar.h"
+#include "../../modules/tm/t_hooks.h"
+#include "dlg_timer.h"
+
+#define MI_DIALOG_NOT_FOUND 		"Requested Dialog not found"
+#define MI_DIALOG_NOT_FOUND_LEN 	(sizeof(MI_DIALOG_NOT_FOUND)-1)
+#define MI_DLG_OPERATION_ERR		"Operation failed"
+#define MI_DLG_OPERATION_ERR_LEN	(sizeof(MI_DLG_OPERATION_ERR)-1)
+
+#define MAX_DLG_RR_PARAM_NAME 32
+
+/* values for the sequential match mode */
+#define SEQ_MATCH_STRICT_ID  0
+#define SEQ_MATCH_FALLBACK   1
+#define SEQ_MATCH_NO_ID      2
+
+
+/*!
+ * \brief Initialize the dialog handlers
+ * \param rr_param_p added record-route parameter
+ * \param dlg_flag_p dialog flag
+ * \param timeout_avp_p AVP for timeout setting
+ * \param default_timeout_p default timeout
+ * \param seq_match_mode_p matching mode
+ */
+void init_dlg_handlers(char *rr_param, int dlg_flag,
+		pv_spec_t *timeout_avp, int default_timeout,
+		int seq_match_mode);
+
+
+/*!
+ * \brief Shutdown operation of the module
+ */
+void destroy_dlg_handlers(void);
+
+
+/*!
+ * \brief Parse SIP message and populate leg informations
+ *
+ * Parse SIP message and populate leg informations. 
+ * \param dlg the dialog to add cseq, contact & record_route
+ * \param msg sip message
+ * \param t transaction
+ * \param leg type of the call leg
+ * \param tag SIP To tag
+ * \return 0 on success, -1 on failure
+ * \note for a request: get record route in normal order, for a reply get
+ * in reverse order, skipping the ones from the request and the proxies' own
+ */
+int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg,
+	struct cell* t, unsigned int leg, str *tag);
+
+
+/*!
+ * \brief Function that is registered as TM callback and called on requests
+ * \param t transaction, used to created the dialog
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+void dlg_onreq(struct cell* t, int type, struct tmcb_params *param);
+
+
+/*!
+ * \brief Function that is registered as RR callback for dialog tracking
+ * 
+ * Function that is registered as RR callback for dialog tracking. It
+ * sets the appropriate events after the SIP method and run the state
+ * machine to update the dialog state. It updates then the saved
+ * dialogs and also the statistics.
+ * \param req SIP request
+ * \param route_params record-route parameter
+ * \param param unused
+ */
+void dlg_onroute(struct sip_msg* req, str *rr_param, void *param);
+
+
+/*!
+ * \brief Timer function that removes expired dialogs, run timeout route
+ * \param tl dialog timer list
+ */
+void dlg_ontimeout( struct dlg_tl *tl);
+
+
+/*!
+ * \brief Create a new dialog from a sip message
+ *
+ * Create a new dialog from a SIP message, register a callback
+ * to keep track of the dialog with help of the tm module.
+ * This function is either called from the request callback, or
+ * from the dlg_manage function in the configuration script.
+ * \see dlg_onreq
+ * \see w_dlg_manage
+ * \param msg SIP message
+ * \param t transaction
+ * \return 0 on success, -1 on failure
+ */ 
+int dlg_new_dialog(struct sip_msg *msg, struct cell *t, const int run_initial_cbs);
+
+
+/*!
+ * \brief Function that returns the dialog lifetime as pseudo-variable
+ * \param msg SIP message
+ * \param param pseudo-variable parameter
+ * \param res pseudo-variable result
+ * \return 0 on success, -1 on failure
+ */
+int pv_get_dlg_lifetime(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+
+
+/*!
+ * \brief Function that returns the dialog state as pseudo-variable
+ * \param msg SIP message
+ * \param param pseudo-variable parameter
+ * \param res pseudo-variable result
+ * \return 0 on success, -1 on failure
+ */
+int pv_get_dlg_status(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+
+
+/*!
+ * \brief Dummy callback just to keep the compiler happy
+ * \param t unused
+ * \param type unused
+ * \param param unused
+ */
+void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param);
+
+/*!
+ * \brief Helper function that prints information for all dialogs
+ * \return void
+ */
+
+void print_all_dlgs();
+
+/*!
+ * \brief Helper function that prints all the properties of a dialog including all the dlg_out's
+ * \param dlg dialog cell
+ * \return void
+ */
+
+void internal_print_all_dlg(struct dlg_cell *dlg);
+
+#endif

+ 1751 - 0
modules/dialog2/dlg_hash.c

@@ -0,0 +1,1751 @@
+/*!
+ * \file
+ * \brief Functions related to dialog creation and searching
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../lib/kmi/mi.h"
+#include "dlg_timer.h"
+#include "dlg_var.h"
+#include "dlg_hash.h"
+#include "dlg_profile.h"
+#include "dlg_handlers.h"
+
+#define MAX_LDG_LOCKS  2048
+#define MIN_LDG_LOCKS  2
+
+
+/*! global dialog table */
+struct dlg_table *d_table = 0;
+/*! global dialog out table */
+static int dlg_hash_size_out = 4096;
+
+
+/*!
+ * \brief Reference a dialog without locking
+ * \param _dlg dialog
+ * \param _cnt increment for the reference counter
+ */
+#define ref_dlg_unsafe(_dlg,_cnt)     \
+	do { \
+		(_dlg)->ref += (_cnt); \
+		LM_DBG("ref dlg %p with %d -> %d\n", \
+			(_dlg),(_cnt),(_dlg)->ref); \
+	}while(0)
+
+
+/*!
+ * \brief Unreference a dialog without locking
+ * \param _dlg dialog
+ * \param _cnt decrement for the reference counter
+ * \param _d_entry dialog entry
+ */
+#define unref_dlg_unsafe(_dlg,_cnt,_d_entry)   \
+	do { \
+		(_dlg)->ref -= (_cnt); \
+		LM_DBG("unref dlg %p with %d -> %d\n",\
+			(_dlg),(_cnt),(_dlg)->ref);\
+		if ((_dlg)->ref<0) {\
+			LM_CRIT("bogus ref %d with cnt %d for dlg %p [%u:%u] "\
+				"with clid '%.*s' and tags '%.*s'\n",\
+				(_dlg)->ref, _cnt, _dlg,\
+				(_dlg)->h_entry, (_dlg)->h_id,\
+				(_dlg)->callid.len, (_dlg)->callid.s,\
+				(_dlg)->from_tag.len,\
+				(_dlg)->from_tag.s);\
+		}\
+		if ((_dlg)->ref<=0) { \
+			unlink_unsafe_dlg( _d_entry, _dlg);\
+			LM_DBG("ref <=0 for dialog %p\n",_dlg);\
+			destroy_dlg(_dlg);\
+		}\
+	}while(0)
+
+/*!
+ * \brief Initialize the global dialog table
+ * \param size size of the table
+ * \return 0 on success, -1 on failure
+ */
+int init_dlg_table(unsigned int size) {
+    unsigned int n;
+    unsigned int i;
+
+    d_table = (struct dlg_table*) shm_malloc
+            (sizeof (struct dlg_table) +size * sizeof (struct dlg_entry));
+    if (d_table == 0) {
+        LM_ERR("no more shm mem (1)\n");
+        goto error0;
+    }
+
+    memset(d_table, 0, sizeof (struct dlg_table));
+    d_table->size = size;
+    d_table->entries = (struct dlg_entry*) (d_table + 1);
+
+    n = (size < MAX_LDG_LOCKS) ? size : MAX_LDG_LOCKS;
+    for (; n >= MIN_LDG_LOCKS; n--) {
+        d_table->locks = lock_set_alloc(n);
+        if (d_table->locks == 0)
+            continue;
+        if (lock_set_init(d_table->locks) == 0) {
+            lock_set_dealloc(d_table->locks);
+            d_table->locks = 0;
+            continue;
+        }
+        d_table->locks_no = n;
+        break;
+    }
+
+    if (d_table->locks == 0) {
+        LM_ERR("unable to allocted at least %d locks for the hash table\n",
+                MIN_LDG_LOCKS);
+        goto error1;
+    }
+
+    for (i = 0; i < size; i++) {
+        memset(&(d_table->entries[i]), 0, sizeof (struct dlg_entry));
+        d_table->entries[i].next_id = rand();
+        d_table->entries[i].lock_idx = i % d_table->locks_no;
+    }
+
+    return 0;
+error1:
+    shm_free(d_table);
+error0:
+    return -1;
+}
+
+/*!
+ * \brief free all the memory in the entry_out structure
+ * \param d_entry_out structure
+ * \return void
+ */
+static void destroy_entry_out(struct dlg_entry_out *d_entry_out) {
+    struct dlg_cell_out *dlg_out;
+    struct dlg_cell_out *dlg_out_tmp;
+    dlg_out = d_entry_out->first;
+
+    LM_DBG("Destroy dialog entry out\n");
+    while (dlg_out) {
+        //clear all dlg_out memory space
+        if (dlg_out->caller_cseq.s) {
+            LM_DBG("content before freeing caller cseq is [%.*s]\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s);
+            shm_free(dlg_out->caller_cseq.s);
+        }
+
+        if (dlg_out->callee_cseq.s) {
+            LM_DBG("content before freeing callee cseq is [%.*s]\n", dlg_out->callee_cseq.len, dlg_out->callee_cseq.s);
+            shm_free(dlg_out->callee_cseq.s);
+        }
+
+        if (dlg_out->callee_contact.s) {
+            LM_DBG("content before freeing callee contact is [%.*s]\n", dlg_out->callee_contact.len, dlg_out->callee_contact.s);
+            shm_free(dlg_out->callee_contact.s);
+        }
+
+        if (dlg_out->callee_route_set.s) {
+            shm_free(dlg_out->callee_route_set.s);
+        }
+        if (dlg_out->did.s) {
+            shm_free(dlg_out->did.s);
+        }
+
+        dlg_out_tmp = dlg_out->next;
+        shm_free(dlg_out);
+
+        dlg_out = dlg_out_tmp;
+    }
+}
+
+/*!
+ * \brief Destroy a dialog, run callbacks and free memory
+ * \param dlg destroyed dialog
+ */
+inline void destroy_dlg(struct dlg_cell *dlg) {
+    int ret = 0;
+    struct dlg_var *var;
+
+    LM_DBG("destroying dialog %p\n", dlg);
+
+    ret = remove_dialog_timer(&dlg->tl);
+    if (ret < 0) {
+        LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] "
+                "with clid '%.*s' and tags '%.*s'\n",
+                dlg, dlg->h_entry, dlg->h_id,
+                dlg->callid.len, dlg->callid.s,
+                dlg->from_tag.len, dlg->from_tag.s);
+
+    } else if (ret > 0) {
+        LM_DBG("removed timer for dlg %p [%u:%u] "
+                "with clid '%.*s' and tags '%.*s' \n",
+                dlg, dlg->h_entry, dlg->h_id,
+                dlg->callid.len, dlg->callid.s,
+                dlg->from_tag.len, dlg->from_tag.s);
+
+    }
+
+    LM_DBG("About to run dlg callback for destroy\n");
+    run_dlg_callbacks(DLGCB_DESTROY, dlg, NULL, NULL, DLG_DIR_NONE, 0);
+    LM_DBG("DONE: About to run dlg callback for destroy\n");
+    if (dlg == get_current_dlg_pointer())
+        reset_current_dlg_pointer();
+
+    if (dlg->cbs.first)
+        destroy_dlg_callbacks_list(dlg->cbs.first);
+
+    if (dlg->profile_links)
+        destroy_linkers(dlg->profile_links);
+
+
+    if (dlg->from_tag.s)
+        shm_free(dlg->from_tag.s);
+
+    if (dlg->first_req_cseq.s)
+        shm_free(dlg->first_req_cseq.s);
+
+    if (dlg->toroute_name.s)
+        shm_free(dlg->toroute_name.s);
+
+    if (dlg->did.s)
+        shm_free(dlg->did.s);
+
+    if (dlg->caller_route_set.s)
+        shm_free(dlg->caller_route_set.s);
+
+    if (dlg->caller_contact.s)
+        shm_free(dlg->caller_contact.s);
+
+    while (dlg->vars) {
+
+        var = dlg->vars;
+        dlg->vars = dlg->vars->next;
+        shm_free(var->key.s);
+        shm_free(var->value.s);
+        shm_free(var);
+    }
+
+    if (&(dlg->dlg_entry_out)) {
+        lock_get(dlg->dlg_out_entries_lock);
+        destroy_entry_out(&(dlg->dlg_entry_out));
+        lock_release(dlg->dlg_out_entries_lock);
+    }
+
+    lock_destroy(dlg->dlg_out_entries_lock);
+    lock_dealloc(dlg->dlg_out_entries_lock);
+
+    shm_free(dlg);
+
+    dlg = 0;
+
+}
+
+/*!
+ * \brief Destroy the global dialog table
+ */
+void destroy_dlg_table(void) {
+    struct dlg_cell *dlg, *l_dlg;
+    unsigned int i;
+
+    if (d_table == 0)
+        return;
+
+    if (d_table->locks) {
+        lock_set_destroy(d_table->locks);
+        lock_set_dealloc(d_table->locks);
+    }
+
+    for (i = 0; i < d_table->size; i++) {
+        dlg = d_table->entries[i].first;
+        while (dlg) {
+            l_dlg = dlg;
+            dlg = dlg->next;
+            destroy_dlg(l_dlg);
+        }
+
+    }
+
+    shm_free(d_table);
+    d_table = 0;
+
+    return;
+}
+
+/*!
+ * \brief Create a new dialog structure for a SIP dialog
+ * \param callid dialog callid
+ * \param from_uri dialog from uri
+ * \param to_uri dialog to uri
+ * \param from_tag dialog from tag
+ * \param req_uri dialog r-uri
+ * \return created dialog structure on success, NULL otherwise
+ */
+struct dlg_cell* build_new_dlg(str *callid, str *from_uri, str *from_tag, str *req_uri) {
+    struct dlg_cell *dlg;
+    int len;
+    char *p;
+
+    len = sizeof (struct dlg_cell) +callid->len + from_uri->len + req_uri->len;
+    dlg = (struct dlg_cell*) shm_malloc(len);
+    if (dlg == 0) {
+        LM_ERR("no more shm mem (%d)\n", len);
+        return 0;
+    }
+
+    memset(dlg, 0, len);
+
+    dlg->dlg_out_entries_lock = lock_alloc();
+    if (dlg->dlg_out_entries_lock == NULL) {
+        LM_ERR("Cannot allocate lock for dlg out entries. Aborting...\n");
+        shm_free(dlg);
+        return 0;
+    } else {
+        if (lock_init(dlg->dlg_out_entries_lock) == NULL) {
+            LM_ERR("Cannot init the lock for dlg out entries. Aborting...\n");
+            lock_destroy(dlg->dlg_out_entries_lock);
+            lock_dealloc(dlg->dlg_out_entries_lock);
+            shm_free(dlg);
+            return 0;
+        }
+    }
+
+    dlg->state = DLG_STATE_UNCONFIRMED;
+
+    dlg->h_entry = core_hash(callid, 0, d_table->size);
+    LM_DBG("new dialog on hash %u\n", dlg->h_entry);
+
+    p = (char*) (dlg + 1);
+
+    dlg->callid.s = p;
+    dlg->callid.len = callid->len;
+    memcpy(p, callid->s, callid->len);
+    p += callid->len;
+
+    dlg->from_uri.s = p;
+    dlg->from_uri.len = from_uri->len;
+    memcpy(p, from_uri->s, from_uri->len);
+    p += from_uri->len;
+
+    dlg->req_uri.s = p;
+    dlg->req_uri.len = req_uri->len;
+    memcpy(p, req_uri->s, req_uri->len);
+    p += req_uri->len;
+
+    if (p != (((char*) dlg) + len)) {
+        LM_CRIT("buffer overflow\n");
+        shm_free(dlg);
+        return 0;
+    }
+
+    return dlg;
+}
+
+/*!
+ * \brief Set the leg information for an existing dialog
+ * \param dlg dialog
+ * \param tag from tag or to tag
+ * \param rr record-routing information
+ * \param contact caller or callee contact
+ * \param cseq CSEQ of caller or callee
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \return 0 on success, -1 on failure
+ */
+int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
+        str *cseq, struct socket_info *bind_addr, unsigned int leg) {
+
+    if (!dlg) {
+        return -1;
+    }
+
+    //create new dlg_out entry
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+
+    if (leg == DLG_CALLER_LEG) {
+        if (tag->len > 0) {
+            dlg->from_tag.s = (char*) shm_malloc(tag->len);
+            if (!dlg->from_tag.s) {
+                LM_ERR("no more shm_mem\n");
+                return -1;
+            }
+            memcpy(dlg->from_tag.s, tag->s, tag->len);
+            dlg->from_tag.len = tag->len;
+        }
+        if (contact->len > 0) {
+            dlg->caller_contact.s = (char*) shm_malloc(contact->len);
+            if (!dlg->caller_contact.s) {
+                LM_ERR("no more shm_mem\n");
+                return -1;
+            }
+            memcpy(dlg->caller_contact.s, contact->s, contact->len);
+            dlg->caller_contact.len = contact->len;
+        }
+        if (rr->len > 0) {
+            dlg->caller_route_set.s = (char*) shm_malloc(rr->len);
+            if (!dlg->caller_route_set.s) {
+                LM_ERR("no more shm_mem\n");
+                return -1;
+            }
+            memcpy(dlg->caller_route_set.s, rr->s, rr->len);
+            dlg->caller_route_set.len = rr->len;
+        }
+        if (cseq->len > 0) {
+            dlg->first_req_cseq.s = (char*) shm_malloc(cseq->len);
+            if (!dlg->first_req_cseq.s) {
+                LM_ERR("no more shm_mem\n");
+                return -1;
+            }
+            memcpy(dlg->first_req_cseq.s, cseq->s, cseq->len);
+            dlg->first_req_cseq.len = cseq->len;
+        }
+    } else {
+        /* this is the callee side so we need to find the dialog_out entry with the correct to_tag
+           and assign caller and callee cseq, callee contact, callee route_set
+         */
+        if (d_entry_out) {
+            lock_get(dlg->dlg_out_entries_lock);
+            dlg_out = d_entry_out->first;
+            while (dlg_out) {
+                LM_DBG("Searching out dialog with to_tag '%.*s' (looking for %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s, tag->len, tag->s);
+
+                if (dlg_out->to_tag.len == tag->len && memcmp(dlg_out->to_tag.s, tag->s, dlg_out->to_tag.len) == 0) {
+                    LM_ERR("Found dialog_out entry with correct to_tag - updating leg info\n");
+
+                    if (contact->len > 0) {
+                        dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
+                        if (!dlg_out->callee_contact.s) {
+                            LM_ERR("no more shm mem\n");
+                            return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
+                        }
+                        dlg_out->callee_contact.len = contact->len;
+                        memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
+                    }
+                    if (rr->len > 0) {
+                        dlg_out->callee_route_set.s = (char*) shm_malloc(rr->len);
+                        if (!dlg_out->callee_route_set.s) {
+                            LM_ERR("no more shm mem\n");
+                            return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
+                        }
+                        dlg_out->callee_route_set.len = rr->len;
+                        memcpy(dlg_out->callee_route_set.s, rr->s, rr->len);
+                    }
+                    if (cseq->len > 0) {
+                        dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
+                        dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
+                        if (!dlg_out->callee_cseq.s || !dlg_out->caller_cseq.s) {
+                            LM_ERR("no more shm mem\n");
+                            return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
+                        }
+                        dlg_out->caller_cseq.len = cseq->len;
+                        memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
+                        dlg_out->callee_cseq.len = cseq->len;
+                        memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
+                    }
+                    dlg_out->callee_bind_addr = bind_addr;
+                }
+                dlg_out = dlg_out->next;
+            }
+            lock_release(dlg->dlg_out_entries_lock);
+        } else {
+            LM_ERR("This dialog has no dialog out entries\n");
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*!
+ * \brief Create a new dialog out structure for a SIP dialog
+ * \param to_tag - dialog to_tag
+ * \return created dlg_out structure on success, NULL otherwise
+ */
+struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str* to_uri, str* to_tag) {
+
+    struct dlg_cell_out *dlg_out;
+    int len;
+    char *p;
+
+    //len = sizeof (struct dlg_cell_out) +dlg->did.len + to_tag->len + to_uri->len;
+    len = sizeof (struct dlg_cell_out) +to_tag->len + to_uri->len;
+
+    dlg_out = (struct dlg_cell_out*) shm_malloc(len);
+    if (dlg_out == 0) {
+        LM_ERR("no more shm mem (%d)\n", len);
+        return 0;
+    }
+    memset(dlg_out, 0, len);
+
+    dlg_out->h_entry = core_hash(to_tag, 0, dlg_hash_size_out);
+    LM_DBG("new dialog_out on hash %u\n", dlg_out->h_entry);
+
+    p = (char*) (dlg_out + 1);
+
+    //dlg_out->did.s = p;
+    //dlg_out->did.len = dlg->did.len;
+    //memcpy(p, dlg->did.s, dlg->did.len);
+    //p += dlg->did.len;
+
+    dlg_out->to_uri.s = p;
+    dlg_out->to_uri.len = to_uri->len;
+    memcpy(p, to_uri->s, to_uri->len);
+    p += to_uri->len;
+
+    dlg_out->to_tag.s = p;
+    dlg_out->to_tag.len = to_tag->len;
+    memcpy(p, to_tag->s, to_tag->len);
+    p += to_tag->len;
+
+    if (p != (((char*) dlg_out) + len)) {
+        LM_CRIT("buffer overflow\n");
+        shm_free(dlg_out);
+
+        return 0;
+    }
+
+    //did might be updated (check update_did_dlg_out) if there is a concurrent call  -therefore this should not be done as single block of memory
+    //so Richard editted this to not have did in the single block of memory
+    if (dlg->did.len > 0) {
+        dlg_out->did.s = (char*) shm_malloc(dlg->did.len);
+        if (!dlg_out->did.s) {
+            LM_ERR("no more shm_mem\n");
+            return 0;
+        }
+        memcpy(dlg_out->did.s, dlg->did.s, dlg->did.len);
+        dlg_out->did.len = dlg->did.len;
+    }
+
+
+
+
+    return dlg_out;
+}
+
+/*!
+ * \brief Free the memory for a dlg_out cell
+ * \param dlg_out structure
+ * \return void
+ */
+void free_dlg_out_cell(struct dlg_cell_out *dlg_out) {
+    if (dlg_out->callee_contact.s)
+        shm_free(dlg_out->callee_contact.s);
+    if (dlg_out->callee_cseq.s)
+        shm_free(dlg_out->callee_cseq.s);
+    if (dlg_out->callee_route_set.s)
+        shm_free(dlg_out->callee_route_set.s);
+    if (dlg_out->caller_cseq.s)
+        shm_free(dlg_out->caller_cseq.s);
+
+    //Richard removed this - it is free-ed two lines above!!??!
+    //if (dlg_out->callee_route_set.s)
+    //    shm_free(dlg_out->callee_route_set.s);
+
+    //Richard added this as the did is now malloc-ed separately and not as a single concurrent block (as the totag etc. are)
+    if (dlg_out->did.s)
+        shm_free(dlg_out->did.s);
+
+
+    shm_free(dlg_out);
+}
+
+/*!
+ * \brief Remove dlg_out entry identified by to_tag from dlg structure
+ * \param dlg structure
+ * \param dlg_out to_tag
+ * \return void
+ */
+void dlg_remove_dlg_out_tag(struct dlg_cell *dlg, str *to_tag) {
+
+    lock_get(dlg->dlg_out_entries_lock);
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *pdlg_out = d_entry_out->first;
+    struct dlg_cell_out *tmpdlg = 0;
+
+    int only = 0;
+
+    while (pdlg_out) {
+        if (pdlg_out->deleted) {
+            LM_DBG("Found dlg_out to remove\n");
+            if (pdlg_out->prev) {
+                pdlg_out->prev->next = pdlg_out->next;
+            } else {
+                //assume that we are first
+                if (pdlg_out->next) {
+                    d_entry_out->first = pdlg_out->next;
+                    pdlg_out->next->prev = 0;
+                } else {
+                    LM_ERR("dlg out entry has prev set to null and next set to null too\n");
+                    only = 1;
+                }
+            }
+            if (pdlg_out->next) {
+                pdlg_out->next->prev = pdlg_out->prev;
+            } else {
+                //we are likely the last
+                if (pdlg_out->prev) {
+                    d_entry_out->last = pdlg_out->prev;
+                } else {
+                    LM_ERR("dlg out next is NULL and so is prev");
+                    only = 1;
+                }
+            }
+            tmpdlg = pdlg_out->next;
+            free_dlg_out_cell(pdlg_out);
+            pdlg_out = tmpdlg;
+
+            if (only) {
+                d_entry_out->last = 0;
+                d_entry_out->first = 0;
+            }
+
+        } else {
+            LM_DBG("Not deleting dlg_out as it is not set to deleted\n");
+            pdlg_out = pdlg_out->next;
+        }
+
+    }
+    lock_release(dlg->dlg_out_entries_lock);
+
+}
+
+/*!
+ * \brief Remove all dlg_out entries from dlg structure expect that identified as dlg_do_not_remove
+ * \param dlg_out cell - structure to not remove
+ * \param mark_only - 1 then only mark for deletion, if 0 then delete
+ * \return void
+ */
+
+void dlg_remove_dlg_out(struct dlg_cell_out *dlg_out_do_not_remove, struct dlg_cell *dlg, int only_mark) {
+
+    lock_get(dlg->dlg_out_entries_lock);
+    //get dlg_out_entry list from dlg
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+
+    //check if list is empty
+    if ((d_entry_out->first == d_entry_out->last) && (d_entry_out->first == 0)) {
+        LM_DBG("There are no dlg_out entries\n");
+        lock_release(dlg->dlg_out_entries_lock);
+        return;
+    }
+
+    dlg_out = d_entry_out->first;
+    LM_DBG("Scanning dlg_entry_out list for dlg_out entry with did: [%s]", dlg->did.s);
+    //run through the list and for each dlg_out_entry:
+    while (dlg_out) {
+
+        //check if it is the dlg_out that we don't want to remove (compare the to-tags)
+        if (dlg_out->to_tag.len == dlg_out_do_not_remove->to_tag.len &&
+                memcmp(dlg_out->to_tag.s, dlg_out_do_not_remove->to_tag.s, dlg_out->to_tag.len) == 0) {
+            LM_DBG("This is the dlg_out not to be removed!\n");
+        } else {
+            //check if this the last entry in the entry_table
+            if ((d_entry_out->first == d_entry_out->last)) {
+                //we shouldnt ever get here
+                LM_DBG("This is the last dlg_out_entry in the dlg_entries_out\n");
+                //this is the last then set entry_out-> first and entry_out->last to zero
+                dlg->dlg_entry_out.first = dlg->dlg_entry_out.last = 0;
+            } else {
+                if (!only_mark) {
+                    LM_DBG("Deleteing dlg out structure\n");
+                    if (dlg_out->prev) {
+                        dlg_out->prev->next = dlg_out->next;
+                    }
+
+                    //make the next->previous dlg_out point to the previous of this dlg_out
+                    //do not do this if this is the last entry in the list as the next struct is null
+                    if (dlg_out->next) {
+                        dlg_out->next->prev = dlg_out->prev;
+                    }
+
+                    free_dlg_out_cell(dlg_out);
+                } else {
+                    LM_DBG("Marking dlg_out structure for deletion - it should be deleted by tm callback instead to_tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
+                    dlg_out->deleted = 1;
+                }
+            }
+        }
+        dlg_out = dlg_out->next;
+    }
+    lock_release(dlg->dlg_out_entries_lock);
+}
+
+/*!
+ * \brief Update or set the CSEQ for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param cseq CSEQ of caller or callee
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_cseq(struct dlg_cell * dlg, unsigned int leg, str *cseq, str *to_tag) {
+    LM_DBG("trying to update cseq with seq [%.*s]\n", cseq->len, cseq->s);
+
+
+    //Runs through the dlg_oput entries finds the one that matches the to_tag and updates the callee or caller cseq accordingly
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+    dlg_out = d_entry_out->first;
+    if (to_tag) {
+
+        //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter  (There could be multiple)
+        while (dlg_out) {
+
+            if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
+                //this parameter matches we have found the dlg_out to update the cseq
+
+                if (leg == DLG_CALLER_LEG) {
+                    //update caller cseq
+                    if (dlg_out->caller_cseq.s) {
+                        if (dlg_out->caller_cseq.len < cseq->len) {
+                            shm_free(dlg_out->caller_cseq.s);
+                            dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
+                            if (dlg_out->caller_cseq.s == NULL)
+                                goto error;
+                            dlg_out->caller_cseq.len = cseq->len;
+                            memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
+                        }
+                    } else {
+                        dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
+                        if (dlg_out->caller_cseq.s == NULL)
+                            goto error;
+
+                        dlg_out->caller_cseq.len = cseq->len;
+                        memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
+                    }
+
+                } else if (leg == DLG_CALLEE_LEG) {
+                    //update callee cseq
+                    if (dlg_out->callee_cseq.s) {
+                        if (dlg_out->callee_cseq.len < cseq->len) {
+                            shm_free(dlg_out->callee_cseq.s);
+                            dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
+                            if (dlg_out->callee_cseq.s == NULL)
+                                goto error;
+
+                            dlg_out->callee_cseq.len = cseq->len;
+                            memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
+                        }
+                    } else {
+                        dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
+                        if (dlg_out->callee_cseq.s == NULL)
+                            goto error;
+
+                        dlg_out->callee_cseq.len = cseq->len;
+                        memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
+                    }
+
+                }
+            }
+            dlg_out = dlg_out->next;
+        }
+
+    }
+    return 0;
+error:
+    LM_ERR("not more shm mem\n");
+    return -1;
+}
+
+/*!
+ * \brief Update or set the CSEQ for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param cseq CSEQ of caller or callee
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *contact, str *to_tag) {
+    LM_DBG("trying to update contact with contact [%.*s]\n", contact->len, contact->s);
+
+    //Runs through the dlg_oput entries finds the one that matches the to_tag and updates the callee or caller contact accordingly
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+    dlg_out = d_entry_out->first;
+
+    if (leg == DLG_CALLER_LEG) {
+        //update caller contact
+        if (dlg->caller_contact.s) {
+            if (dlg->caller_contact.len < contact->len) {
+                shm_free(dlg->caller_contact.s);
+                dlg->caller_contact.s = (char*) shm_malloc(contact->len);
+                if (dlg->caller_contact.s == NULL)
+                    goto error;
+                dlg->caller_contact.len = contact->len;
+                memcpy(dlg->caller_contact.s, contact->s, contact->len);
+            }
+        } else {
+            dlg->caller_contact.s = (char*) shm_malloc(contact->len);
+            if (dlg->caller_contact.s == NULL)
+                goto error;
+
+            dlg->caller_contact.len = contact->len;
+            memcpy(dlg->caller_contact.s, contact->s, contact->len);
+        }
+    }
+    if (leg == DLG_CALLEE_LEG) {
+        //update callee contact
+        if (!to_tag) {
+            LM_ERR("No to tag to identify dlg_out\n");
+            return -1;
+        }
+        //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter  (There could be multiple)
+        while (dlg_out) {
+
+            if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
+                //this parameter matches we have found the dlg_out to update the callee contact
+
+                //update callee contact
+                if (dlg_out->callee_contact.s) {
+                    if (dlg_out->callee_contact.len < contact->len) {
+                        shm_free(dlg_out->callee_contact.s);
+                        dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
+                        if (dlg_out->callee_contact.s == NULL)
+                            goto error;
+
+                        dlg_out->callee_contact.len = contact->len;
+                        memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
+                    }
+                } else {
+                    dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
+                    if (dlg_out->callee_contact.s == NULL)
+                        goto error;
+
+                    dlg_out->callee_contact.len = contact->len;
+                    memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
+                }
+            }
+            dlg_out = dlg_out->next;
+        }
+    }
+    return 0;
+error:
+    LM_ERR("not more shm mem\n");
+    return -1;
+}
+
+/*!
+ * \brief Lookup a dialog in the global list
+ *
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again iff a dialog has been found.
+ * \param h_entry number of the hash table entry
+ * \param h_id id of the hash table entry
+ * \return dialog structure on success, NULL on failure
+ */
+struct dlg_cell * lookup_dlg(unsigned int h_entry, unsigned int h_id) {
+    struct dlg_cell *dlg;
+    struct dlg_entry *d_entry;
+
+    if (h_entry >= d_table->size)
+        goto not_found;
+
+    d_entry = &(d_table->entries[h_entry]);
+
+    dlg_lock(d_table, d_entry);
+
+    for (dlg = d_entry->first; dlg; dlg = dlg->next) {
+        if (dlg->h_id == h_id) {
+            ref_dlg_unsafe(dlg, 1);
+            dlg_unlock(d_table, d_entry);
+            LM_DBG("dialog id=%u found on entry %u\n", h_id, h_entry);
+            return dlg;
+        }
+    }
+
+    dlg_unlock(d_table, d_entry);
+not_found:
+    LM_DBG("no dialog id=%u found on entry %u\n", h_id, h_entry);
+
+    return 0;
+}
+
+/*!
+ * \brief Helper function to get a dialog corresponding to a SIP message
+ * \see get_dlg
+ * \param callid callid
+ * \param ftag from tag
+ * \param ttag to tag
+ * \param dir direction
+ * \return dialog structure on success, NULL on failure
+ */
+static inline struct dlg_cell * internal_get_dlg(unsigned int h_entry,
+        str *callid, str *ftag, str *ttag, unsigned int *dir) {
+    struct dlg_cell *dlg;
+    struct dlg_entry *d_entry;
+
+    d_entry = &(d_table->entries[h_entry]);
+
+    dlg_lock(d_table, d_entry);
+
+    for (dlg = d_entry->first; dlg; dlg = dlg->next) {
+        /* Check callid / fromtag / totag */
+        if (match_dialog(dlg, callid, ftag, ttag, dir) == 1) {
+            ref_dlg_unsafe(dlg, 1);
+            dlg_unlock(d_table, d_entry);
+            return dlg;
+        }
+    }
+
+    dlg_unlock(d_table, d_entry);
+
+    return 0;
+}
+
+/*!
+ * \brief Get dialog that correspond to CallId, From Tag and To Tag
+ *
+ * Get dialog that correspond to CallId, From Tag and To Tag.
+ * See RFC 3261, paragraph 4. Overview of Operation:
+ * "The combination of the To tag, From tag, and Call-ID completely
+ * defines a peer-to-peer SIP relationship between [two UAs] and is
+ * referred to as a dialog."
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again iff a dialog has been found.
+ * \param callid callid
+ * \param ftag from tag
+ * \param ttag to tag
+ * \param dir direction
+ * \return dialog structure on success, NULL on failure
+ */
+struct dlg_cell * get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir) {
+    struct dlg_cell *dlg;
+
+    if ((dlg = internal_get_dlg(core_hash(callid, 0,
+            d_table->size), callid, ftag, ttag, dir)) == 0 &&
+            (dlg = internal_get_dlg(core_hash(callid, ttag->len
+            ? ttag : 0, d_table->size), callid, ftag, ttag, dir)) == 0) {
+        LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
+
+        return 0;
+    }
+    return dlg;
+}
+
+/*!
+ * \brief Link a dialog structure
+ * \param dlg dialog
+ * \param n extra increments for the reference counter
+ */
+void link_dlg_out(struct dlg_cell *dlg, struct dlg_cell_out *dlg_out, int n) {
+    LM_DBG("Start: link_dlg_out\n");
+
+    lock_get(dlg->dlg_out_entries_lock);
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+
+    if ((d_entry_out->first == d_entry_out->last) && (d_entry_out->first == 0)) {
+        //adding first out dialog
+        LM_DBG("Adding first dlg_out structure\n");
+        d_entry_out->first = dlg_out;
+        d_entry_out->last = dlg_out;
+    } else {
+        LM_DBG("Adding new dlg_out structure\n");
+        dlg_out->prev = d_entry_out->last;
+        dlg_out->next = 0;
+
+        d_entry_out->last->next = dlg_out;
+        d_entry_out->last = dlg_out;
+    }
+
+    lock_release(dlg->dlg_out_entries_lock);
+
+    LM_DBG("Done: link_dlg_out\n");
+    return;
+}
+
+/*!
+ * \brief Link a dialog structure
+ * \param dlg dialog
+ * \param n extra increments for the reference counter
+ */
+void link_dlg(struct dlg_cell *dlg, int n) {
+    struct dlg_entry *d_entry;
+
+    d_entry = &(d_table->entries[dlg->h_entry]);
+
+    dlg_lock(d_table, d_entry);
+
+    dlg->h_id = d_entry->next_id++;
+    if (d_entry->first == 0) {
+        d_entry->first = d_entry->last = dlg;
+    } else {
+        d_entry->last->next = dlg;
+        dlg->prev = d_entry->last;
+        d_entry->last = dlg;
+    }
+
+    ref_dlg_unsafe(dlg, 1 + n);
+
+    dlg_unlock(d_table, d_entry);
+
+    return;
+}
+
+/*!
+ * \brief Refefence a dialog with locking
+ * \see ref_dlg_unsafe
+ * \param dlg dialog
+ * \param cnt increment for the reference counter
+ */
+void ref_dlg(struct dlg_cell *dlg, unsigned int cnt) {
+
+    struct dlg_entry *d_entry;
+
+    d_entry = &(d_table->entries[dlg->h_entry]);
+
+    dlg_lock(d_table, d_entry);
+    ref_dlg_unsafe(dlg, cnt);
+    dlg_unlock(d_table, d_entry);
+}
+
+/*!
+ * \brief Unreference a dialog with locking
+ * \see unref_dlg_unsafe
+ * \param dlg dialog
+ * \param cnt decrement for the reference counter
+ */
+void unref_dlg(struct dlg_cell *dlg, unsigned int cnt) {
+
+    struct dlg_entry *d_entry;
+
+    d_entry = &(d_table->entries[dlg->h_entry]);
+
+    dlg_lock(d_table, d_entry);
+    unref_dlg_unsafe(dlg, cnt, d_entry);
+    dlg_unlock(d_table, d_entry);
+}
+
+/*!
+ * Small logging helper functions for next_state_dlg.
+ * \param event logged event
+ * \param dlg dialog data
+ * \see next_state_dlg
+ */
+static inline void log_next_state_dlg(const int event, const struct dlg_cell * dlg) {
+
+    LM_CRIT("bogus event %d in state %d for dlg %p [%u:%u] with clid '%.*s' and tags "
+            "'%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
+            dlg->callid.len, dlg->callid.s,
+            dlg->from_tag.len, dlg->from_tag.s);
+}
+
+/*!
+ * \brief Update a dialog state according a event and the old state
+ *
+ * This functions implement the main state machine that update a dialog
+ * state according a processed event and the current state. If necessary
+ * it will delete the processed dialog. The old and new state are also
+ * saved for reference.
+ * \param dlg updated dialog
+ * \param event current event
+ * \param old_state old dialog state
+ * \param new_state new dialog state
+ * \param unref set to 1 when the dialog was deleted, 0 otherwise
+ */
+void next_state_dlg(struct dlg_cell *dlg, int event,
+        int *old_state, int *new_state, int *unref, str * to_tag) {
+    struct dlg_entry *d_entry;
+
+
+
+    d_entry = &(d_table->entries[dlg->h_entry]);
+
+    *unref = 0;
+
+    dlg_lock(d_table, d_entry);
+
+    *old_state = dlg->state;
+
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+    dlg_out = d_entry_out->first;
+    int found = -1;
+
+    switch (event) {
+        case DLG_EVENT_TDEL:
+            switch (dlg->state) {
+                case DLG_STATE_UNCONFIRMED:
+                case DLG_STATE_EARLY:
+                    dlg->state = DLG_STATE_DELETED;
+                    unref_dlg_unsafe(dlg, 1, d_entry);
+                    *unref = 1;
+                    break;
+                case DLG_STATE_CONFIRMED:
+                    unref_dlg_unsafe(dlg, 1, d_entry);
+                    break;
+                case DLG_STATE_DELETED:
+                    *unref = 1;
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_RPL1xx:
+            switch (dlg->state) {
+                case DLG_STATE_UNCONFIRMED:
+                case DLG_STATE_EARLY:
+                    dlg->state = DLG_STATE_EARLY;
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_RPL3xx:
+            switch (dlg->state) {
+                case DLG_STATE_UNCONFIRMED:
+                case DLG_STATE_EARLY:
+                    dlg->state = DLG_STATE_DELETED;
+                    *unref = 1;
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_RPL2xx:
+            switch (dlg->state) {
+                case DLG_STATE_DELETED:
+                    if (dlg->dflags & DLG_FLAG_HASBYE) {
+                        LM_CRIT("bogus event %d in state %d (with BYE) "
+                                "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' \n",
+                                event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
+                                dlg->callid.len, dlg->callid.s,
+                                dlg->from_tag.len, dlg->from_tag.s);
+                        break;
+                    }
+                    ref_dlg_unsafe(dlg, 1);
+                case DLG_STATE_UNCONFIRMED:
+                case DLG_STATE_EARLY:
+                    dlg->state = DLG_STATE_CONFIRMED;
+                    //TODO: check that the callbacks for confirmed are run
+                    break;
+                case DLG_STATE_CONFIRMED:
+                    //check the to_tag passed parameter exists
+                    if (to_tag) {
+                        //compare the to_tag passed parameter to the dlg_out to_tag entry of the dlg parameter  (There should be only 1 dlg_out entry)
+
+                        if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
+                            //this parameter matches the existing dlg_out and is therefore a retransmission, so break
+                            break;
+                        } else {
+
+                            LM_ERR("It looks like this is a concurrently confirmed call!!\n");
+                            LM_ERR("Error checking now so not putting into DLG_STATE_CONCURRENTLY_CONFIRMED\n");
+
+                            LM_ERR("This is event DLG_EVENT_RPL2XX and the current dlg state is DLG_STATE_CONFIRMED\n");
+                            LM_ERR("There should only be one dlg out here as the state is CONFIRMED but we are checking anyway!\n");
+                            LM_ERR("To tag passed in the 2XX: [%.*s]", to_tag->len, to_tag->s);
+                            LM_ERR("Now printing dlgouts totags - there should be only one!\n");
+                            while (dlg_out) {
+                                LM_ERR("dlg_out to_tag: [%.*s]\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
+                                dlg_out = dlg_out->next;
+                            }
+
+                            //The parameter does not match so this is a concurrently confirmed call
+                            //dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED;
+                        }
+                    } else {
+                        //to_tag parameter does not exist so break
+                        break;
+                    }
+                case DLG_STATE_CONCURRENTLY_CONFIRMED:
+
+                    //check the to_tag passed parameter exists
+
+                    if (to_tag) {
+
+                        //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter  (There could be multiple)
+                        while (dlg_out) {
+
+                            if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
+                                //this parameter matches the existing dlg_out and is therefore a retransmission
+                                found = 1;
+                            }
+                            dlg_out = dlg_out->next;
+                        }
+                        if (found == -1) {
+                            //The parameter does not match so this is another concurrently confirmed call (we would have breaked by now if it matched)
+                            dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED;
+                        }
+
+                    } else {
+                        //to_tag parameter does not exist so break
+                        break;
+                    }
+
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_REQACK:
+            switch (dlg->state) {
+                case DLG_STATE_CONFIRMED:
+                    break;
+                case DLG_STATE_DELETED:
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_REQBYE:
+            switch (dlg->state) {
+                case DLG_STATE_CONFIRMED:
+                    dlg->dflags |= DLG_FLAG_HASBYE;
+                    dlg->state = DLG_STATE_DELETED;
+                    *unref = 1;
+                    break;
+                case DLG_STATE_EARLY:
+                case DLG_STATE_DELETED:
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_REQPRACK:
+            switch (dlg->state) {
+                case DLG_STATE_EARLY:
+                    //Richard added this - think it is necessary as can received PRACK in early state!
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        case DLG_EVENT_REQ:
+            switch (dlg->state) {
+
+                case DLG_STATE_EARLY:
+                case DLG_STATE_CONFIRMED:
+                    break;
+                default:
+                    log_next_state_dlg(event, dlg);
+            }
+            break;
+        default:
+            LM_CRIT("unknown event %d in state %d "
+                    "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s'\n",
+                    event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
+                    dlg->callid.len, dlg->callid.s,
+                    dlg->from_tag.len, dlg->from_tag.s);
+    }
+    *new_state = dlg->state;
+
+    dlg_unlock(d_table, d_entry);
+
+    LM_DBG("dialog %p changed from state %d to "
+            "state %d, due event %d\n", dlg, *old_state, *new_state, event);
+}
+
+/**
+ *
+ */
+int dlg_set_toroute(struct dlg_cell *dlg, str * route) {
+    if (dlg == NULL || route == NULL || route->len <= 0)
+        return 0;
+    if (dlg->toroute_name.s != NULL) {
+        shm_free(dlg->toroute_name.s);
+        dlg->toroute_name.s = NULL;
+        dlg->toroute_name.len = 0;
+    }
+    dlg->toroute_name.s = (char*) shm_malloc((route->len + 1) * sizeof (char));
+    if (dlg->toroute_name.s == NULL) {
+        LM_ERR("no more shared memory\n");
+        return -1;
+    }
+    memcpy(dlg->toroute_name.s, route->s, route->len);
+    dlg->toroute_name.len = route->len;
+    dlg->toroute_name.s[dlg->toroute_name.len] = '\0';
+    dlg->toroute = route_lookup(&main_rt, dlg->toroute_name.s);
+
+    return 0;
+}
+
+/*!
+ * \brief Takes the did of the dialog and appends an "x" to it to make a different did for concurrent calls
+ * \param dlg_cell - dlg_cell whose did we use
+ * \param new_did - empty container for new_did
+ * \return void
+ */
+void create_concurrent_did(struct dlg_cell *dlg, str * new_did) {
+    int len  = dlg->did.len + 1;
+    new_did = shm_malloc(len);
+    if (new_did == 0) {
+        LM_ERR("no more shm mem (%d)\n", len);
+    }
+    memcpy(new_did->s, dlg->did.s, dlg->did.len);
+    new_did->s[dlg->did.len+1]= 'x';
+    new_did->len = len;
+}
+
+/*!
+ * \brief Update the did of the dlg_out structure
+ * \param dlg_cell_out - structure to update
+ * \param new_did - new did to use
+ * \return 1 success, 0 failure
+ */
+int update_dlg_out_did(struct dlg_cell_out *dlg_out, str * new_did) {
+    //update the did of the dlg_out
+    if (dlg_out->did.s) {
+        if (dlg_out->did.len < new_did->len) {
+            shm_free(dlg_out->did.s);
+            dlg_out->did.s = (char*) shm_malloc(new_did->len);
+            if (dlg_out->did.s == NULL)
+                goto error;
+        }
+    } else {
+        dlg_out->did.s = (char*) shm_malloc(new_did->len);
+        if (dlg_out->did.s == NULL)
+            goto error;
+    }
+    memcpy(dlg_out->did.s, new_did->s, new_did->len);
+    dlg_out->did.len = new_did->len;
+
+    return 0;
+error:
+    LM_ERR("not more shm mem\n");
+    return -1;
+}
+
+/*!
+ * \brief Update the did of the dlg structure
+ * \param dlg_cell - structure to update
+ * \param new_did - new did to use
+ * \return 1 success, 0 failure
+ */
+int update_dlg_did(struct dlg_cell *dlg, str * new_did) {
+    //update the did of the dlg_out
+    if (dlg->did.s) {
+        if (dlg->did.len < new_did->len) {
+            shm_free(dlg->did.s);
+            dlg->did.s = (char*) shm_malloc(new_did->len);
+            if (dlg->did.s == NULL)
+                goto error;
+        }
+    } else {
+        dlg->did.s = (char*) shm_malloc(new_did->len);
+        if (dlg->did.s == NULL)
+            goto error;
+    }
+    memcpy(dlg->did.s, new_did->s, new_did->len);
+    dlg->did.len = new_did->len;
+
+    return 0;
+error:
+    LM_ERR("not more shm mem\n");
+    return -1;
+}
+
+
+
+/**************************** MI functions ******************************/
+
+/*!
+ * \brief Helper method that output a dialog via the MI interface
+ * \see mi_print_dlg
+ * \param rpl MI node that should be filled
+ * \param dlg printed dialog
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+static inline int internal_mi_print_dlg_out(struct mi_node *rpl,
+        struct dlg_cell_out * dlg_out) {
+    struct mi_node* node = NULL;
+    struct mi_node* node1 = NULL;
+    struct mi_attr* attr = NULL;
+
+    node = add_mi_node_child(rpl, 0, "dialog_out", 10, 0, 0);
+    if (node == 0)
+        goto error;
+
+    attr = addf_mi_attr(node, 0, "hash", 4, "%u:%u",
+            dlg_out->h_entry, dlg_out->h_id);
+    if (attr == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_tag", 6,
+            dlg_out->to_tag.s, dlg_out->to_tag.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "did", 3,
+            dlg_out->did.s, dlg_out->did.len);
+    if (node1 == 0)
+        goto error;
+
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_contact", 14,
+            dlg_out->callee_contact.s,
+            dlg_out->callee_contact.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_cseq", 11,
+            dlg_out->caller_cseq.s,
+            dlg_out->caller_cseq.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_cseq", 11,
+            dlg_out->callee_cseq.s,
+            dlg_out->callee_cseq.len);
+    if (node1 == 0)
+        goto error;
+
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_route_set", 16,
+            dlg_out->callee_route_set.s,
+            dlg_out->callee_route_set.len);
+    if (node1 == 0)
+        goto error;
+
+    if (dlg_out->callee_bind_addr) {
+        node1 = add_mi_node_child(node, 0,
+                "callee_bind_addr", 16,
+                dlg_out->callee_bind_addr->sock_str.s,
+                dlg_out->callee_bind_addr->sock_str.len);
+    } else {
+        node1 = add_mi_node_child(node, 0,
+                "callee_bind_addr", 16, 0, 0);
+    }
+
+    return 0;
+
+error:
+    LM_ERR("failed to add node\n");
+
+    return -1;
+}
+
+/*!
+ * \brief Helper method that output a dialog via the MI interface
+ * \see mi_print_dlg
+ * \param rpl MI node that should be filled
+ * \param dlg printed dialog
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+static inline int internal_mi_print_dlg(struct mi_node *rpl,
+        struct dlg_cell *dlg, int with_context) {
+    struct mi_node* node = NULL;
+    struct mi_node* node1 = NULL;
+    struct mi_attr* attr = NULL;
+    int len;
+    char* p;
+
+    node = add_mi_node_child(rpl, 0, "dialog", 6, 0, 0);
+    if (node == 0)
+        goto error;
+
+    attr = addf_mi_attr(node, 0, "hash", 4, "%u:%u",
+            dlg->h_entry, dlg->h_id);
+    if (attr == 0)
+        goto error;
+
+    p = int2str((unsigned long) dlg->state, &len);
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "state", 5, p, len);
+    if (node1 == 0)
+        goto error;
+
+    p = int2str((unsigned long) dlg->ref, &len);
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "ref_count", 9, p, len);
+    if (node1 == 0)
+        goto error;
+
+    p = int2str((unsigned long) dlg->start_ts, &len);
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "timestart", 9, p, len);
+    if (node1 == 0)
+        goto error;
+
+    p = int2str((unsigned long) dlg->tl.timeout, &len);
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "timeout", 7, p, len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "callid", 6,
+            dlg->callid.s, dlg->callid.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_uri", 8,
+            dlg->from_uri.s, dlg->from_uri.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_tag", 8,
+            dlg->from_tag.s, dlg->from_tag.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "did", 3,
+            dlg->did.s, dlg->did.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_contact", 14,
+            dlg->caller_contact.s,
+            dlg->caller_contact.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "first_req_cseq", 14,
+            dlg->first_req_cseq.s,
+            dlg->first_req_cseq.len);
+    if (node1 == 0)
+        goto error;
+
+    node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_route_set", 16,
+            dlg->caller_route_set.s,
+            dlg->caller_route_set.len);
+    if (node1 == 0)
+        goto error;
+
+    if (dlg->caller_bind_addr) {
+        node1 = add_mi_node_child(node, 0,
+                "caller_bind_addr", 16,
+                dlg->caller_bind_addr->sock_str.s,
+                dlg->caller_bind_addr->sock_str.len);
+    } else {
+        node1 = add_mi_node_child(node, 0,
+                "caller_bind_addr", 16, 0, 0);
+    }
+
+    if (with_context) {
+        node1 = add_mi_node_child(node, 0, "context", 7, 0, 0);
+        if (node1 == 0)
+            goto error;
+        run_dlg_callbacks(DLGCB_MI_CONTEXT,
+                dlg,
+                NULL,
+                NULL,
+                DLG_DIR_NONE,
+                (void *) node1);
+    }
+
+    struct dlg_cell_out *dlg_out;
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    dlg_out = d_entry_out->first;
+
+    while (dlg_out) {
+
+        if (internal_mi_print_dlg_out(rpl, dlg_out) != 0)
+            goto error;
+
+        dlg_out = dlg_out->next;
+    }
+
+    return 0;
+
+error:
+    LM_ERR("failed to add node\n");
+
+    return -1;
+}
+
+/*!
+ * \brief Output a dialog via the MI interface
+ * \param rpl MI node that should be filled
+ * \param dlg printed dialog
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context) {
+
+    return internal_mi_print_dlg(rpl, dlg, with_context);
+}
+
+/*!
+ * \brief Helper function that output all dialogs via the MI interface
+ * \see mi_print_dlgs
+ * \param rpl MI node that should be filled
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+static int internal_mi_print_dlgs(struct mi_node *rpl, int with_context) {
+    struct dlg_cell *dlg;
+    unsigned int i;
+
+    LM_DBG("printing %i dialogs\n", d_table->size);
+
+    for (i = 0; i < d_table->size; i++) {
+        dlg_lock(d_table, &(d_table->entries[i]));
+
+        for (dlg = d_table->entries[i].first; dlg; dlg = dlg->next) {
+            if (internal_mi_print_dlg(rpl, dlg, with_context) != 0)
+                goto error;
+        }
+        dlg_unlock(d_table, &(d_table->entries[i]));
+    }
+    return 0;
+
+error:
+    dlg_unlock(d_table, &(d_table->entries[i]));
+    LM_ERR("failed to print dialog\n");
+
+    return -1;
+}
+
+static inline struct mi_root * process_mi_params(struct mi_root *cmd_tree, struct dlg_cell **dlg_p) {
+    struct mi_node* node;
+    struct dlg_entry *d_entry;
+    struct dlg_cell *dlg;
+    str *callid;
+    str *from_tag;
+    unsigned int h_entry;
+
+    node = cmd_tree->node.kids;
+    if (node == NULL) {
+        /* no parameters at all */
+        *dlg_p = NULL;
+        return NULL;
+    }
+
+    /* we have params -> get callid and fromtag */
+    callid = &node->value;
+    LM_DBG("callid='%.*s'\n", callid->len, callid->s);
+
+    node = node->next;
+    if (!node || !node->value.s || !node->value.len) {
+        from_tag = NULL;
+    } else {
+        from_tag = &node->value;
+        LM_DBG("from_tag='%.*s'\n", from_tag->len, from_tag->s);
+        if (node->next != NULL)
+            return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM));
+    }
+
+    h_entry = core_hash(callid, 0, d_table->size);
+
+    d_entry = &(d_table->entries[h_entry]);
+    dlg_lock(d_table, d_entry);
+
+    for (dlg = d_entry->first; dlg; dlg = dlg->next) {
+        if (match_downstream_dialog(dlg, callid, from_tag) == 1) {
+            if (dlg->state == DLG_STATE_DELETED) {
+                *dlg_p = NULL;
+                break;
+            } else {
+                *dlg_p = dlg;
+                dlg_unlock(d_table, d_entry);
+                return 0;
+            }
+        }
+    }
+    dlg_unlock(d_table, d_entry);
+
+    return init_mi_tree(404, MI_SSTR("Nu such dialog"));
+}
+
+/*!
+ * \brief Output all dialogs via the MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return mi node with the dialog information, or NULL on failure
+ */
+
+struct mi_root * mi_print_dlgs(struct mi_root *cmd_tree, void *param) {
+    struct mi_root* rpl_tree = NULL;
+    struct mi_node* rpl = NULL;
+    struct dlg_cell* dlg = NULL;
+
+    rpl_tree = process_mi_params(cmd_tree, &dlg);
+    if (rpl_tree)
+        //param error
+        return rpl_tree;
+
+    rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK));
+    if (rpl_tree == 0)
+        return 0;
+    rpl = &rpl_tree->node;
+
+    if (dlg == NULL) {
+        if (internal_mi_print_dlgs(rpl, 0) != 0)
+            goto error;
+    } else {
+        if (internal_mi_print_dlg(rpl, dlg, 0) != 0)
+            goto error;
+    }
+
+    return rpl_tree;
+error:
+    free_mi_tree(rpl_tree);
+
+    return NULL;
+}
+
+/*!
+ * \brief Print a dialog context via the MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return mi node with the dialog information, or NULL on failure
+ */
+struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd_tree, void *param) {
+    struct mi_root* rpl_tree = NULL;
+    struct mi_node* rpl = NULL;
+    struct dlg_cell* dlg = NULL;
+
+    rpl_tree = process_mi_params(cmd_tree, &dlg);
+    if (rpl_tree)
+        /* param error */
+        return rpl_tree;
+
+    rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK));
+    if (rpl_tree == 0)
+        return 0;
+    rpl = &rpl_tree->node;
+
+    if (dlg == NULL) {
+        if (internal_mi_print_dlgs(rpl, 1) != 0)
+            goto error;
+    } else {
+        if (internal_mi_print_dlg(rpl, dlg, 1) != 0)
+            goto error;
+    }
+
+    return rpl_tree;
+error:
+    free_mi_tree(rpl_tree);
+
+    return NULL;
+}
+
+time_t api_get_dlg_expires(str *callid, str *ftag, str *ttag) {
+    struct dlg_cell *dlg;
+    time_t expires = 0;
+    time_t start;
+
+    if (!callid || !ftag || !ttag) {
+        LM_ERR("Missing callid, from tag or to tag\n");
+        return 0;
+    }
+
+    unsigned int direction = DLG_DIR_NONE;
+    dlg = get_dlg(callid, ftag, ttag, &direction);
+    if (!dlg) return 0;
+
+    if (dlg->state != DLG_STATE_CONFIRMED || !dlg->start_ts) {
+        /* Dialog not started yet so lets assume start time is now.*/
+        start = time(0);
+    } else {
+        start = dlg->start_ts;
+    }
+
+    expires = start + dlg->lifetime;
+    unref_dlg(dlg, 1);
+
+    return expires;
+}
+
+char* state_to_char(unsigned int state) {
+	switch (state) {
+	case DLG_STATE_UNCONFIRMED:
+		return "Unconfirmed";
+	case DLG_STATE_EARLY:
+		return "Early";
+	case DLG_STATE_CONFIRMED:
+		return "Confirmed";
+	case DLG_STATE_DELETED:
+		return "Deleted";
+	default:
+		return "Unknown";
+	}
+}

+ 604 - 0
modules/dialog2/dlg_hash.h

@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2006 Voice System SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-14  initial version (bogdan)
+ * 2006-11-28  Added num_100s and num_200s to dlg_cell, to aid in adding 
+ *             statistics tracking of the number of early, and active dialogs.
+ *             (Jeffrey Magder - SOMA Networks)
+ * 2007-03-06  syncronized state machine added for dialog state. New tranzition
+ *             design based on events; removed num_1xx and num_2xx (bogdan)
+ * 2007-07-06  added flags, cseq, contact, route_set and bind_addr 
+ *             to struct dlg_cell in order to store these information into db
+ *             (ancuta)
+ * 2008-04-17  added new dialog flag to avoid state tranzitions from DELETED to
+ *             CONFIRMED_NA due delayed "200 OK" (bogdan)
+ * 2010-09      Add dlg_out structure to cater for forked calls and early dialog termination (richard and jason)
+ */
+
+/*!
+ * \file
+ * \brief Functions and definitions related to dialog creation and searching
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#ifndef _DIALOG_DLG_HASH_H_
+#define _DIALOG_DLG_HASH_H_
+
+#include "../../locking.h"
+#include "../../lib/kmi/mi.h"
+#include "dlg_timer.h"
+#include "dlg_cb.h"
+#include "../../modules/tm/tm_load.h"
+
+
+/* states of a dialog */
+#define DLG_STATE_UNCONFIRMED  1 /*!< unconfirmed dialog */
+#define DLG_STATE_EARLY        2 /*!< early dialog */
+#define DLG_STATE_CONFIRMED    4 /*!< confirmed dialog */
+#define DLG_STATE_DELETED      5 /*!< deleted dialog */
+#define DLG_STATE_CONCURRENTLY_CONFIRMED      6 /*!< confirmed concurrent dailogs */
+
+/* events for dialog processing */
+#define DLG_EVENT_TDEL         1 /*!< transaction was destroyed */
+#define DLG_EVENT_RPL1xx       2 /*!< 1xx request */
+#define DLG_EVENT_RPL2xx       3 /*!< 2xx request */ 
+#define DLG_EVENT_RPL3xx       4 /*!< 3xx request */
+#define DLG_EVENT_REQPRACK     5 /*!< PRACK request */
+#define DLG_EVENT_REQACK       6 /*!< ACK request */
+#define DLG_EVENT_REQBYE       7 /*!< BYE request */
+#define DLG_EVENT_REQ          8 /*!< other requests */
+
+/* dialog flags */
+#define DLG_FLAG_NEW           (1<<0) /*!< new dialog */
+#define DLG_FLAG_CHANGED       (1<<1) /*!< dialog was changed */
+#define DLG_FLAG_HASBYE        (1<<2) /*!< bye was received */
+#define DLG_FLAG_TOBYE         (1<<3) /*!< flag from dialog context */
+#define DLG_FLAG_CALLERBYE     (1<<4) /*!< bye from caller */
+#define DLG_FLAG_CALLEEBYE     (1<<5) /*!< bye from callee */
+#define DLG_FLAG_LOCALDLG      (1<<6) /*!< local dialog, unused */
+#define DLG_FLAG_CHANGED_VARS  (1<<7) /*!< dialog-variables changed */
+
+/* dialog-variable flags (in addition to dialog-flags) */
+#define DLG_FLAG_DEL           (1<<8) /*!< delete this var */
+
+#define DLG_CALLER_LEG         0 /*!< attribute that belongs to a caller leg */
+#define DLG_CALLEE_LEG         1 /*!< attribute that belongs to a callee leg */
+
+#define DLG_DIR_NONE           0 /*!< dialog has no direction */
+#define DLG_DIR_DOWNSTREAM     1 /*!< dialog has downstream direction */
+#define DLG_DIR_UPSTREAM       2 /*!< dialog has upstream direction */
+
+/*! entries in the main dialog table */
+struct dlg_entry_out {
+    struct dlg_cell_out *first; /*!< dialog list */
+    struct dlg_cell_out *last; /*!< optimisation, end of the dialog list */
+    unsigned int count; /*! number of out entries in the linked list */
+};
+
+/*! entries in the dialog list */
+struct dlg_cell {
+    volatile int ref; /*!< reference counter */
+    struct dlg_cell *next; /*!< next entry in the list */
+    struct dlg_cell *prev; /*!< previous entry in the list */
+    unsigned int h_id; /*!< id of the hash table entry */
+    unsigned int h_entry; /*!< number of hash entry */
+    str did;
+    str callid; /*!< callid from SIP message */
+    str from_tag; /*!< from tags of caller*/
+    str from_uri; /*!< from uri from SIP message */
+    str first_req_cseq; /*!< CSEQ of caller*/
+    str req_uri; /*!< r-uri from SIP message */
+    str caller_contact; /*!< contact of caller*/
+    str caller_route_set; /*!< route set of caller*/
+    struct socket_info * caller_bind_addr; /*! binded address of caller*/
+    unsigned int state; /*!< dialog state */
+    unsigned int start_ts; /*!< start time  (absolute UNIX ts)*/
+    unsigned int lifetime; /*!< dialog lifetime */
+    unsigned int toroute; /*!< index of route that is executed on timeout */
+    str toroute_name; /*!< name of route that is executed on timeout */
+    unsigned int dflags; /*!< internal dialog flags */
+    unsigned int sflags; /*!< script dialog flags */
+    struct dlg_tl tl; /*!< dialog timer list */
+    struct dlg_head_cbl cbs; /*!< dialog callbacks */
+    struct dlg_profile_link *profile_links; /*!< dialog profiles */
+    struct dlg_var *vars; /*!< dialog variables */
+    struct dlg_entry_out dlg_entry_out; /*!< list of dialog_out entries */
+    struct cell *transaction; /*!< ptr to associated transaction for this dialog TM module cell ptr */
+    gen_lock_t *dlg_out_entries_lock; /*!< lock for dialog_out linked list */
+    unsigned int from_rr_nb; /*!< information from record routing */
+};
+
+struct dlg_cell_out {
+    struct dlg_cell_out *next; /*!< next entry in the list */
+    struct dlg_cell_out *prev; /*!< previous entry in the list */
+    unsigned int h_id; /*!< id of the hash table entry */
+    unsigned int h_entry; /*!< number of hash entry */
+    str did;
+    str to_uri; /*!< to uri */
+    str to_tag; /*!< to tags of callee*/
+    str caller_cseq; /*!< CSEQ of caller*/
+    str callee_cseq; /*!< CSEQ of callee*/
+    str callee_contact; /*!< contact of callee*/
+    str callee_route_set; /*!< route set of caller*/
+    struct socket_info * callee_bind_addr; /*! binded address of caller*/
+    unsigned int dflags; /*!< internal dialog flags */
+    unsigned int deleted;
+};
+
+/*! entries in the main dialog table */
+struct dlg_entry {
+    struct dlg_cell *first; /*!< dialog list */
+    struct dlg_cell *last; /*!< optimisation, end of the dialog list */
+    unsigned int next_id; /*!< next id */
+    unsigned int lock_idx; /*!< lock index */
+};
+
+/*! main dialog table */
+struct dlg_table {
+    unsigned int size; /*!< size of the dialog table */
+    struct dlg_entry *entries; /*!< dialog hash table */
+    unsigned int locks_no; /*!< number of locks */
+    gen_lock_set_t *locks; /*!< lock table */
+};
+
+
+/*! global dialog table */
+extern struct dlg_table *d_table;
+/*! point to the current dialog */
+extern struct dlg_cell *current_dlg_pointer;
+
+
+/*!
+ * \brief Set a dialog lock
+ * \param _table dialog table
+ * \param _entry locked entry
+ */
+#define dlg_lock(_table, _entry) \
+		lock_set_get( (_table)->locks, (_entry)->lock_idx);
+
+
+/*!
+ * \brief Release a dialog lock
+ * \param _table dialog table
+ * \param _entry locked entry
+ */
+#define dlg_unlock(_table, _entry) \
+		lock_set_release( (_table)->locks, (_entry)->lock_idx);
+
+/*!
+ * \brief Unlink a dialog from the list without locking
+ * \see unref_dlg_unsafe
+ * \param d_entry unlinked entry
+ * \param dlg unlinked dialog
+ */
+static inline void unlink_unsafe_dlg(struct dlg_entry *d_entry, struct dlg_cell *dlg) {
+    if (dlg->next)
+        dlg->next->prev = dlg->prev;
+    else
+        d_entry->last = dlg->prev;
+    if (dlg->prev)
+        dlg->prev->next = dlg->next;
+    else
+        d_entry->first = dlg->next;
+
+    dlg->next = dlg->prev = 0;
+
+    return;
+}
+
+
+/*!
+ * \brief Destroy a dialog, run callbacks and free memory
+ * \param dlg destroyed dialog
+ */
+inline void destroy_dlg(struct dlg_cell *dlg);
+
+
+/*!
+ * \brief Initialize the global dialog table
+ * \param size size of the table
+ * \return 0 on success, -1 on failure
+ */
+int init_dlg_table(unsigned int size);
+
+
+/*!
+ * \brief Destroy the global dialog table
+ */
+void destroy_dlg_table(void);
+
+
+void free_dlg_out_cell(struct dlg_cell_out *dlg_out);
+
+
+/*!
+ * \brief Create a new dialog structure for a SIP dialog
+ * \param callid dialog callid
+ * \param from_uri dialog from uri
+ * \param to_uri dialog to uri
+ * \param from_tag dialog from tag
+ * \param req_uri dialog r-uri
+ * \return created dialog structure on success, NULL otherwise
+ */
+struct dlg_cell* build_new_dlg(str *callid, str *from_uri,
+        str *from_tag, str *req_uri);
+
+
+/*!
+ * \brief Set the leg information for an existing dialog
+ * \param dlg dialog
+ * \param tag from tag or to tag
+ * \param rr record-routing information
+ * \param contact caller or callee contact
+ * \param cseq CSEQ of caller or callee
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \return 0 on success, -1 on failure
+ */
+int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
+        str *cseq, struct socket_info *bind_addr, unsigned int leg);
+
+
+
+/*!
+ * \brief Update or set the CSEQ for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param cseq CSEQ of caller or callee
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_cseq(struct dlg_cell *dlg, unsigned int leg, str *cseq, str *to_tag);
+
+/*!
+ * \brief Update or set the contact for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param contact CONTACT of caller or callee
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *contact, str *to_tag);
+
+/*!
+ * \brief Set time-out route
+ * \param dlg dialog
+ * \param route name of route
+ * \return 0 on success, -1 on failure
+ */
+int dlg_set_toroute(struct dlg_cell *dlg, str *route);
+
+
+/*!
+ * \brief Lookup a dialog in the global list
+ *
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again iff a dialog has been found.
+ * \param h_entry number of the hash table entry
+ * \param h_id id of the hash table entry
+ * \return dialog structure on success, NULL on failure
+ */
+struct dlg_cell* lookup_dlg(unsigned int h_entry, unsigned int h_id);
+
+
+/*!
+ * \brief Get dialog that correspond to CallId, From Tag and To Tag
+ *
+ * Get dialog that correspond to CallId, From Tag and To Tag.
+ * See RFC 3261, paragraph 4. Overview of Operation:                 
+ * "The combination of the To tag, From tag, and Call-ID completely  
+ * defines a peer-to-peer SIP relationship between [two UAs] and is 
+ * referred to as a dialog."
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again iff a dialog has been found.
+ * \param callid callid
+ * \param ftag from tag
+ * \param ttag to tag
+ * \param dir direction
+ * \return dialog structure on success, NULL on failure
+ */
+struct dlg_cell* get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir);
+
+
+/*!
+ * \brief Link a dialog structure
+ * \param dlg dialog
+ * \param n extra increments for the reference counter
+ */
+void link_dlg(struct dlg_cell *dlg, int n);
+
+
+void link_dlg_out(struct dlg_cell *dlg, struct dlg_cell_out *dlg_out, int n);
+
+/*!
+ * \brief Unreference a dialog with locking
+ * \see unref_dlg_unsafe
+ * \param dlg dialog
+ * \param cnt decrement for the reference counter
+ */
+void unref_dlg(struct dlg_cell *dlg, unsigned int cnt);
+
+
+/*!
+ * \brief Refefence a dialog with locking
+ * \see ref_dlg_unsafe
+ * \param dlg dialog
+ * \param cnt increment for the reference counter
+ */
+void ref_dlg(struct dlg_cell *dlg, unsigned int cnt);
+
+
+/*!
+ * \brief Update a dialog state according a event and the old state
+ *
+ * This functions implement the main state machine that update a dialog
+ * state according a processed event and the current state. If necessary
+ * it will delete the processed dialog. The old and new state are also
+ * saved for reference.
+ * \param dlg updated dialog
+ * \param event current event
+ * \param old_state old dialog state
+ * \param new_state new dialog state
+ * \param unref set to 1 when the dialog was deleted, 0 otherwise
+ */
+void next_state_dlg(struct dlg_cell *dlg, int event,
+        int *old_state, int *new_state, int *unref, str *to_tag);
+
+
+/*!
+ * \brief Output all dialogs via the MI interface
+ * \param cmd_tree MI root node
+ * \param param unused
+ * \return a mi node with the dialog information, or NULL on failure
+ */
+struct mi_root * mi_print_dlgs(struct mi_root *cmd, void *param);
+
+
+
+/*!
+ * \brief Print a dialog context via the MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return mi node with the dialog information, or NULL on failure
+ */
+struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd, void *param);
+
+/*!
+ * \brief Terminate selected dialogs via the MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return mi node with the dialog information, or NULL on failure
+ */
+struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param);
+
+/*!
+ * \brief Check if a dialog structure matches to a SIP message dialog
+ * \param dlg dialog structure
+ * \param callid SIP message Call-ID
+ * \param ftag SIP message from tag
+ * \param ttag SIP message to tag
+ * \param dir direction of the message, if DLG_DIR_NONE it will set
+ * \return 1 if dialog structure and message content matches, 0 otherwise
+ */
+static inline int match_dialog(struct dlg_cell *dlg, str *callid,
+        str *ftag, str *ttag, unsigned int *dir) {
+
+    struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
+    struct dlg_cell_out *dlg_out;
+
+    if (d_entry_out->first == 0) {
+        //there are no dialog out entries yet
+        if (*dir == DLG_DIR_DOWNSTREAM) {
+            if (dlg->callid.len == callid->len &&
+                    dlg->from_tag.len == ftag->len &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0 &&
+                    strncmp(dlg->from_tag.s, ftag->s, ftag->len) == 0) {
+                return 1;
+            }
+        } else if (*dir == DLG_DIR_UPSTREAM) {
+            if (dlg->callid.len == callid->len &&
+                    dlg->from_tag.len == ttag->len &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0 &&
+                    strncmp(dlg->from_tag.s, ttag->s, ttag->len) == 0) {
+                return 1;
+            }
+        } else {
+
+            if (dlg->callid.len != callid->len) {
+                return 0;
+            }
+            if (dlg->from_tag.len == ttag->len &&
+                    strncmp(dlg->from_tag.s, ttag->s, ttag->len) == 0 &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0) {
+
+                *dir = DLG_DIR_UPSTREAM;
+                return 1;
+            } else if (dlg->from_tag.len == ftag->len &&
+                    strncmp(dlg->from_tag.s, ftag->s, ftag->len) == 0 &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0) {
+
+                *dir = DLG_DIR_DOWNSTREAM;
+                return 1;
+            }
+            LM_DBG("No match found\n");
+        }
+    } else {
+
+        //there is a dialog out entry
+        if (*dir == DLG_DIR_DOWNSTREAM) {
+            if (dlg->callid.len == callid->len &&
+                    dlg->from_tag.len == ftag->len &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0 &&
+                    strncmp(dlg->from_tag.s, ftag->s, ftag->len) == 0) {
+                //now need to scroll thought d_out_entries to see if to_tag matches!
+                dlg_out = d_entry_out->first;
+                while (dlg_out) {
+                    if (dlg_out->to_tag.len == ttag->len &&
+                            memcmp(dlg_out->to_tag.s, ttag->s, dlg_out->to_tag.len) == 0) {
+                        return 1;
+                    }
+                    dlg_out = dlg_out->next;
+                }
+            }
+        } else if (*dir == DLG_DIR_UPSTREAM) {
+            if (dlg->callid.len == callid->len &&
+                    dlg->from_tag.len == ttag->len &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0 &&
+                    strncmp(dlg->from_tag.s, ttag->s, ttag->len) == 0) {
+                dlg_out = d_entry_out->first;
+                while (dlg_out) {
+                    if (dlg_out->to_tag.len == ftag->len &&
+                            memcmp(dlg_out->to_tag.s, ftag->s, dlg_out->to_tag.len) == 0) {
+                        return 1;
+                    }
+                    dlg_out = dlg_out->next;
+                }
+            }
+        } else {
+            if (dlg->callid.len != callid->len) {
+                return 0;
+            }
+
+            if (dlg->from_tag.len == ttag->len &&
+                    strncmp(dlg->from_tag.s, ttag->s, ttag->len) == 0 &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0) {
+                //now need to scroll thought d_out_entries to see if to_tag matches!
+                dlg_out = d_entry_out->first;
+                while (dlg_out) {
+                    if (dlg_out->to_tag.len == ftag->len &&
+                            memcmp(dlg_out->to_tag.s, ftag->s, dlg_out->to_tag.len) == 0) {
+                        *dir = DLG_DIR_UPSTREAM;
+                        return 1;
+                    }
+                    dlg_out = dlg_out->next;
+                }
+            } else if (dlg->from_tag.len == ftag->len &&
+                    strncmp(dlg->from_tag.s, ftag->s, ftag->len) == 0 &&
+                    strncmp(dlg->callid.s, callid->s, callid->len) == 0) {
+                //now need to scroll thought d_out_entries to see if to_tag matches!
+                dlg_out = d_entry_out->first;
+                while (dlg_out) {
+                    if (dlg_out->to_tag.len == ttag->len &&
+                            memcmp(dlg_out->to_tag.s, ttag->s, dlg_out->to_tag.len) == 0) {
+                        *dir = DLG_DIR_DOWNSTREAM;
+                        return 1;
+                    }
+                    dlg_out = dlg_out->next;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+/*!
+ * \brief Check if a downstream dialog structure matches a SIP message dialog
+ * \param dlg dialog structure
+ * \param callid SIP message callid
+ * \param ftag SIP message from tag
+ * \return 1 if dialog structure matches the SIP dialog, 0 otherwise
+ */
+static inline int match_downstream_dialog(struct dlg_cell *dlg, str *callid, str *ftag) {
+    if (dlg == NULL || callid == NULL)
+        return 0;
+    if (ftag == NULL) {
+        if (dlg->callid.len != callid->len ||
+                strncmp(dlg->callid.s, callid->s, callid->len) != 0)
+            return 0;
+    } else {
+        if (dlg->callid.len != callid->len ||
+                dlg->from_tag.len != ftag->len ||
+                strncmp(dlg->callid.s, callid->s, callid->len) != 0 ||
+                strncmp(dlg->from_tag.s, ftag->s, ftag->len) != 0)
+            return 0;
+    }
+    return 1;
+}
+
+
+/*!
+ * \brief Output a dialog via the MI interface
+ * \param rpl MI node that should be filled
+ * \param dlg printed dialog
+ * \param with_context if 1 then the dialog context will be also printed
+ * \return 0 on success, -1 on failure
+ */
+int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context);
+
+
+
+/*!
+ * \brief Create a new dialog out structure for a SIP dialog
+ * \param to_tag - dialog to_tag
+ * \return created dlg_out structure on success, NULL otherwise
+ */
+
+struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str *to_uri, str* to_tag);
+
+/*!
+ * \brief Remove all dlg_out entries from dlg structure expect that identified as dlg_do_not_remove
+ * \param dlg_out cell - struture to not remove
+ * \param mark_only - 1 then only mark for delection, if 0 then delete
+ * \return void
+ */
+
+void dlg_remove_dlg_out(struct dlg_cell_out *dlg_out_do_not_remove, struct dlg_cell *dlg, int only_mark);
+
+/*!
+ * \brief Remove dlg_out entry identified by to_tag from dlg structure
+ * \param dlg structure
+ * \param dlg_out to_tag
+ * \return void
+ */
+
+void dlg_remove_dlg_out_tag(struct dlg_cell *dlg, str *to_tag);
+
+/*!
+ * \brief Takes the did of the dialog and appends an "x" to it to make a different did for concurrent calls
+ * \param dlg_cell - dlg_cell whose did we use
+ * \param new_did - empty container for new_did
+ * \return void
+ */
+
+void create_concurrent_did(struct dlg_cell *dlg, str *new_did);
+
+/*!
+ * \brief Update the did of the dlg_out structure
+ * \param dlg_cell_out - structure to update
+ * \param new_did - new did to use
+ * \return 1 success, 0 failure
+ */
+
+int update_dlg_out_did(struct dlg_cell_out *dlg_out, str *new_did);
+
+/*!
+ * \brief Update the did of the dlg structure
+ * \param dlg_cell - structure to update
+ * \param new_did - new did to use
+ * \return 1 success, 0 failure
+ */
+
+int update_dlg_did(struct dlg_cell *dlg, str *new_did);
+
+time_t api_get_dlg_expires(str *callid, str *ftag, str *ttag);
+
+char* state_to_char(unsigned int state);
+
+#endif

+ 70 - 0
modules/dialog2/dlg_load.h

@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * dialog module - basic support for dialog tracking
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2006-04-14  initial version (bogdan)
+ */
+
+#ifndef _DIALOG_DLG_LOAD_H_
+#define _DIALOG_DLG_LOAD_H_
+
+#include "dlg_cb.h"
+#include "../../sr_module.h"
+
+/* terminate_dlg function prototype */
+typedef int (*terminate_dlg_f)(str *callid, str *ftag, str *ttag, str *hdrs, str *reason);
+
+/* get_dlg_lifetime function prototype */
+typedef time_t (*get_dlg_expires_f)(str *callid, str *ftag, str *ttag);
+
+struct dlg_binds {
+	register_dlgcb_f  register_dlgcb;
+        register_dlgcb_nodlg_f register_dlgcb_nodlg;
+	terminate_dlg_f terminate_dlg;
+        set_dlg_variable_f set_dlg_var;
+	get_dlg_variable_f get_dlg_var;
+        get_dlg_expires_f get_dlg_expires;      
+};
+
+
+typedef int(*load_dlg_f)( struct dlg_binds *dlgb );
+int load_dlg( struct dlg_binds *dlgb);
+
+static inline int load_dlg_api( struct dlg_binds *dlgb )
+{
+	load_dlg_f load_dlg;
+
+	/* import the DLG auto-loading function */
+	if ( !(load_dlg=(load_dlg_f)find_export("load_dlg", 0, 0)))
+		return -1;
+
+	/* let the auto-loading function load all DLG stuff */
+	if (load_dlg( dlgb )==-1)
+		return -1;
+
+	return 0;
+}
+
+
+#endif

+ 865 - 0
modules/dialog2/dlg_profile.c

@@ -0,0 +1,865 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2008 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2008-04-20  initial version (bogdan)
+ *
+ */
+
+
+/*!
+ * \file
+ * \brief Profile related functions for the dialog module
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../route.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../trim.h"
+#include "dlg_hash.h"
+#include "dlg_handlers.h"
+#include "dlg_profile.h"
+
+
+/*! size of dialog profile hash */
+#define PROFILE_HASH_SIZE 16
+
+/*! tm bindings */
+extern struct tm_binds d_tmb;
+
+/*! global dialog message id */
+static unsigned int            current_dlg_msg_id = 0 ;
+
+/*! global dialog */
+struct dlg_cell                *current_dlg_pointer = NULL ;
+
+/*! pending dialog links */
+static struct dlg_profile_link *current_pending_linkers = NULL;
+
+/*! global dialog profile list */
+static struct dlg_profile_table *profiles = NULL;
+
+
+static struct dlg_profile_table* new_dlg_profile( str *name,
+		unsigned int size, unsigned int has_value);
+
+
+struct dlg_cell *get_current_dlg_pointer(void)
+{
+	return current_dlg_pointer;
+}
+
+void reset_current_dlg_pointer(void)
+{
+	current_dlg_pointer = NULL;
+}
+
+/*!
+ * \brief Add profile definitions to the global list
+ * \see new_dlg_profile
+ * \param profiles profile name
+ * \param has_value set to 0 for a profile without value, otherwise it has a value
+ * \return 0 on success, -1 on failure
+ */
+int add_profile_definitions( char* profiles, unsigned int has_value)
+{
+	char *p;
+	char *d;
+	str name;
+	unsigned int i;
+
+	if (profiles==NULL || strlen(profiles)==0 )
+		return 0;
+
+	p = profiles;
+	do {
+		/* locate name of profile */
+		name.s = p;
+		d = strchr( p, ';');
+		if (d) {
+			name.len = d-p;
+			d++;
+		} else {
+			name.len = strlen(p);
+		}
+
+		/* we have the name -> trim it for spaces */
+		trim_spaces_lr( name );
+
+		/* check len name */
+		if (name.len==0)
+			/* ignore */
+			continue;
+
+		/* check the name format */
+		for(i=0;i<name.len;i++) {
+			if ( !isalnum(name.s[i]) ) {
+				LM_ERR("bad profile name <%.*s>, char %c - use only "
+					"alphanumerical characters\n", name.len,name.s,name.s[i]);
+				return -1;
+			}
+		}
+
+		/* name ok -> create the profile */
+		LM_DBG("creating profile <%.*s>\n",name.len,name.s);
+
+		if (new_dlg_profile( &name, PROFILE_HASH_SIZE, has_value)==NULL) {
+			LM_ERR("failed to create new profile <%.*s>\n",name.len,name.s);
+			return -1;
+		}
+
+	}while( (p=d)!=NULL );
+
+	return 0;
+}
+
+
+/*!
+ * \brief Search a dialog profile in the global list
+ * \note Linear search, this won't have the best performance for huge profile lists
+ * \param name searched dialog profile
+ * \return pointer to the profile on success, NULL otherwise
+ */
+struct dlg_profile_table* search_dlg_profile(str *name)
+{
+	struct dlg_profile_table *profile;
+
+	for( profile=profiles ; profile ; profile=profile->next ) {
+		if (name->len==profile->name.len &&
+		memcmp(name->s,profile->name.s,name->len)==0 )
+			return profile;
+	}
+	return NULL;
+}
+
+
+/*!
+ * \brief Creates a new dialog profile
+ * \see add_profile_definitions
+ * \param name profile name
+ * \param size profile size
+ * \param has_value set to 0 for a profile without value, otherwise it has a value
+ * \return pointer to the created dialog on success, NULL otherwise
+ */
+static struct dlg_profile_table* new_dlg_profile( str *name, unsigned int size,
+		unsigned int has_value)
+{
+	struct dlg_profile_table *profile;
+	struct dlg_profile_table *ptmp;
+	unsigned int len;
+	unsigned int i;
+
+	if ( name->s==NULL || name->len==0 || size==0 ) {
+		LM_ERR("invalid parameters\n");
+		return NULL;
+	}
+
+	for( len=0,i=0 ; i<8*sizeof(size) ; i++ ) {
+		if ( size & (1<<i) ) len++;
+	}
+	if (len!=1) {
+		LM_ERR(" size %u is not power of 2!\n", size);
+		return NULL;
+	}
+
+	profile = search_dlg_profile(name);
+	if (profile!=NULL) {
+		LM_ERR("duplicate dialog profile registered <%.*s>\n",
+			name->len, name->s);
+		return NULL;
+	}
+
+	len = sizeof(struct dlg_profile_table) +
+		size*sizeof(struct dlg_profile_entry) +
+		name->len + 1;
+	profile = (struct dlg_profile_table *)shm_malloc(len);
+	if (profile==NULL) {
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+
+	memset( profile , 0 , len);
+	profile->size = size;
+	profile->has_value = (has_value==0)?0:1;
+
+	/* init lock */
+	if (lock_init( &profile->lock )==NULL) {
+		LM_ERR("failed to init lock\n");
+		shm_free(profile);
+		return NULL;
+	}
+
+	/* set inner pointers */
+	profile->entries = (struct dlg_profile_entry*)(profile + 1);
+	profile->name.s = ((char*)profile->entries) +
+		size*sizeof(struct dlg_profile_entry);
+
+	/* copy the name of the profile */
+	memcpy( profile->name.s, name->s, name->len );
+	profile->name.len = name->len;
+	profile->name.s[profile->name.len] = 0;
+
+	/* link profile */
+	for( ptmp=profiles ; ptmp && ptmp->next; ptmp=ptmp->next );
+	if (ptmp==NULL)
+		profiles = profile;
+	else
+		ptmp->next = profile;
+
+	return profile;
+}
+
+
+/*!
+ * \brief Destroy a dialog profile list
+ * \param profile dialog profile
+ */
+static void destroy_dlg_profile(struct dlg_profile_table *profile)
+{
+	if (profile==NULL)
+		return;
+
+	lock_destroy( &profile->lock );
+	shm_free( profile );
+	return;
+}
+
+
+/*!
+ * \brief Destroy the global dialog profile list
+ */
+void destroy_dlg_profiles(void)
+{
+	struct dlg_profile_table *profile;
+
+	while(profiles) {
+		profile = profiles;
+		profiles = profiles->next;
+		destroy_dlg_profile( profile );
+	}
+	return;
+}
+
+
+/*!
+ * \brief Destroy dialog linkers
+ * \param linker dialog linker
+ */
+void destroy_linkers(struct dlg_profile_link *linker)
+{
+	struct dlg_profile_entry *p_entry;
+	struct dlg_profile_link *l;
+	struct dlg_profile_hash *lh;
+
+	while(linker) {
+		l = linker;
+		linker = linker->next;
+		/* unlink from profile table */
+		if (l->hash_linker.next) {
+			p_entry = &l->profile->entries[l->hash_linker.hash];
+			lock_get( &l->profile->lock );
+			lh = &l->hash_linker;
+			/* last element on the list? */
+			if (lh==lh->next) {
+				p_entry->first = NULL;
+			} else {
+				if (p_entry->first==lh)
+					p_entry->first = lh->next;
+				lh->next->prev = lh->prev;
+				lh->prev->next = lh->next;
+			}
+			lh->next = lh->prev = NULL;
+			p_entry->content --;
+			lock_release( &l->profile->lock );
+		}
+		/* free memory */
+		shm_free(l);
+	}
+}
+
+
+/*!
+ * \brief Cleanup a profile
+ * \param msg SIP message
+ * \param flags unused
+ * \param unused
+ * \return 1
+ */
+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);
+		current_dlg_pointer = NULL;
+	}
+	if (current_pending_linkers) {
+		destroy_linkers(current_pending_linkers);
+		current_pending_linkers = NULL;
+	}
+
+	/* need to return non-zero - 0 will break the exec of the request */
+	return 1;
+}
+
+
+
+struct dlg_cell* get_dialog_from_tm(struct cell *t)
+{
+    if (t==NULL || t==T_UNDEFINED)
+        return NULL;
+
+    struct tm_callback* x = (struct tm_callback*)(t->tmcb_hl.first);
+
+    while(x){
+        membar_depends();
+        if (x->types==TMCB_MAX && x->callback==dlg_tmcb_dummy){
+            return (struct dlg_cell*)(x->param);
+        }
+        x=x->next;
+    }
+
+    return NULL;
+}
+
+/*!
+ * \brief Get the current dialog for a message, if exists
+ * \param msg SIP message
+ * \return NULL if called in REQUEST_ROUTE, pointer to dialog ctx otherwise
+ */
+struct dlg_cell *get_current_dialog(struct sip_msg *msg)
+{
+
+	if (is_route_type(REQUEST_ROUTE|BRANCH_ROUTE)) {
+            LM_DBG("Get Current Dialog: Route type is REQUEST ROUTE or BRANCH ROUTE");
+            LM_DBG("Get Current Dialog: SIP Method - %.*s", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
+		/* use the per-process static holder */
+		if (msg->id==current_dlg_msg_id){
+                    LM_DBG("Message Id [%i] equals current dlg msg id [%i] - returning current dlg pointer", msg->id, current_dlg_msg_id);
+                    return current_dlg_pointer;
+                }
+                LM_DBG("Message Id [%i] not equal to current point dlg id [%i] - returning null", msg->id, current_dlg_msg_id);
+		current_dlg_pointer = NULL;
+		current_dlg_msg_id = msg->id;
+		destroy_linkers(current_pending_linkers);
+		current_pending_linkers = NULL;
+		return NULL;
+	} else {
+		/* use current transaction to get dialog */
+            LM_DBG("Route type is not REQUEST ROUTE or brancg route - getting from tm");
+	    return get_dialog_from_tm(d_tmb.t_gett());
+	}
+}
+
+
+/*!
+ * \brief Calculate the hash profile from a dialog
+ * \see core_hash
+ * \param value hash source
+ * \param dlg dialog cell
+ * \param profile dialog profile table (for hash size)
+ * \return value hash if the value has a value, hash over dialog otherwise
+ */
+inline static unsigned int calc_hash_profile(str *value, struct dlg_cell *dlg,
+		struct dlg_profile_table *profile)
+{
+	if (profile->has_value) {
+		/* do hash over the value */
+		return core_hash( value, NULL, profile->size);
+	} else {
+		/* do hash over dialog pointer */
+		return ((unsigned long)dlg) % profile->size ;
+	}
+}
+
+
+/*!
+ * \brief Link a dialog profile
+ * \param linker dialog linker
+ * \param dlg dialog cell
+ */
+static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *dlg)
+{
+	unsigned int hash;
+	struct dlg_profile_entry *p_entry;
+	struct dlg_entry *d_entry;
+
+	/* add the linker to the dialog */
+	/* FIXME zero h_id is not 100% for testing if the dialog is inserted
+	 * into the hash table -> we need circular lists  -bogdan */
+	if (dlg->h_id) {
+		d_entry = &d_table->entries[dlg->h_entry];
+		dlg_lock( d_table, d_entry);
+		linker->next = dlg->profile_links;
+		dlg->profile_links =linker;
+		linker->hash_linker.dlg = dlg;
+		dlg_unlock( d_table, d_entry);
+	} else {
+		linker->next = dlg->profile_links;
+		dlg->profile_links =linker;
+		linker->hash_linker.dlg = dlg;
+	}
+
+	/* calculate the hash position */
+	hash = calc_hash_profile(&linker->hash_linker.value, dlg, linker->profile);
+	linker->hash_linker.hash = hash;
+
+	/* insert into profile hash table */
+	p_entry = &linker->profile->entries[hash];
+	lock_get( &linker->profile->lock );
+	if (p_entry->first) {
+		linker->hash_linker.prev = p_entry->first->prev;
+		linker->hash_linker.next = p_entry->first;
+		p_entry->first->prev->next = &linker->hash_linker;
+		p_entry->first->prev = &linker->hash_linker;
+	} else {
+		p_entry->first = linker->hash_linker.next
+			= linker->hash_linker.prev = &linker->hash_linker;
+	}
+	p_entry->content ++;
+	lock_release( &linker->profile->lock );
+}
+
+
+/*!
+ * \brief Set the global variables to the current dialog
+ * \param msg SIP message
+ * \param dlg dialog cell
+ */
+void set_current_dialog(struct sip_msg *msg, struct dlg_cell *dlg)
+{
+	struct dlg_profile_link *linker;
+	struct dlg_profile_link *tlinker;
+
+	/* if linkers are not from current request, just discard them */
+	if (msg->id!=current_dlg_msg_id) {
+		current_dlg_msg_id = msg->id;
+		destroy_linkers(current_pending_linkers);
+	} else {
+		/* add the linker, one be one, to the dialog */
+		linker = current_pending_linkers;
+		while (linker) {
+			tlinker = linker;
+			linker = linker->next;
+			/* process tlinker */
+			tlinker->next = NULL;
+			link_dlg_profile( tlinker, dlg);
+		}
+	}
+	current_pending_linkers = NULL;
+	current_dlg_pointer = dlg;
+
+	/* do not increase reference counter here, let caller handle it
+	 * (yes, this is somewhat ugly) */
+}
+
+
+/*!
+ * \brief Set a dialog profile
+ * \param msg SIP message
+ * \param value value
+ * \param profile dialog profile table
+ * \return 0 on success, -1 on failure
+ */
+int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *profile)
+{
+	struct dlg_cell *dlg;
+	struct dlg_profile_link *linker;
+
+	/* get current dialog */
+	dlg = get_current_dialog(msg);
+
+	if (dlg==NULL && !is_route_type(REQUEST_ROUTE)) {
+		LM_CRIT("BUG - dialog not found in a non REQUEST route (%d)\n",
+			REQUEST_ROUTE);
+		return -1;
+	}
+
+	/* build new linker */
+	linker = (struct dlg_profile_link*)shm_malloc(
+		sizeof(struct dlg_profile_link) + (profile->has_value?value->len:0) );
+	if (linker==NULL) {
+		LM_ERR("no more shm memory\n");
+		return -1;
+	}
+	memset(linker, 0, sizeof(struct dlg_profile_link));
+
+	/* set backpointer to profile */
+	linker->profile = profile;
+
+	/* set the value */
+	if (profile->has_value) {
+		linker->hash_linker.value.s = (char*)(linker+1);
+		memcpy( linker->hash_linker.value.s, value->s, value->len);
+		linker->hash_linker.value.len = value->len;
+	}
+
+	if (dlg!=NULL) {
+		/* add linker directly to the dialog and profile */
+		link_dlg_profile( linker, dlg);
+	} else {
+		/* no dialog yet -> set linker as pending */
+		linker->next = current_pending_linkers;
+		current_pending_linkers = linker;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Unset a dialog profile
+ * \param msg SIP message
+ * \param value value
+ * \param profile dialog profile table
+ * \return 1 on success, -1 on failure
+ */
+int unset_dlg_profile(struct sip_msg *msg, str *value,
+		struct dlg_profile_table *profile)
+{
+	struct dlg_cell *dlg;
+	struct dlg_profile_link *linker;
+	struct dlg_profile_link *linker_prev;
+	struct dlg_entry *d_entry;
+
+	/* get current dialog */
+	dlg = get_current_dialog(msg);
+
+	if (dlg==NULL || is_route_type(REQUEST_ROUTE)) {
+		LM_CRIT("BUG - dialog NULL or del_profile used in request route\n");
+		return -1;
+	}
+
+	/* check the dialog linkers */
+	d_entry = &d_table->entries[dlg->h_entry];
+	dlg_lock( d_table, d_entry);
+	linker = dlg->profile_links;
+	linker_prev = NULL;
+	for( ; linker ; linker_prev=linker,linker=linker->next) {
+		if (linker->profile==profile) {
+			if (profile->has_value==0) {
+				goto found;
+			} else if (value && value->len==linker->hash_linker.value.len &&
+			memcmp(value->s,linker->hash_linker.value.s,value->len)==0){
+				goto found;
+			}
+			/* allow further search - maybe the dialog is inserted twice in
+			 * the same profile, but with different values -bogdan
+			 */
+		}
+	}
+	dlg_unlock( d_table, d_entry);
+	return -1;
+
+found:
+	/* table still locked */
+	/* remove the linker element from dialog */
+	if (linker_prev==NULL) {
+		dlg->profile_links = linker->next;
+	} else {
+		linker_prev->next = linker->next;
+	}
+	linker->next = NULL;
+	dlg_unlock( d_table, d_entry);
+	/* remove linker from profile table and free it */
+	destroy_linkers(linker);
+	return 1;
+}
+
+
+/*!
+ * \brief Check if a dialog belongs to a profile
+ * \param msg SIP message
+ * \param profile dialog profile table
+ * \param value value
+ * \return 1 on success, -1 on failure
+ */
+int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile,
+		str *value) {
+	struct dlg_cell *dlg;
+	struct dlg_profile_link *linker;
+	struct dlg_entry *d_entry;
+
+	LM_DBG("Getting current dialog");
+	/* get current dialog */
+	dlg = get_current_dialog(msg);
+
+	if (dlg == NULL) {
+		LM_DBG("Error: Current dlg is null");
+
+		return -1;
+	}
+	LM_DBG("Current dlg found");
+
+	/* check the dialog linkers */
+	d_entry = &d_table->entries[dlg->h_entry];
+	dlg_lock( d_table, d_entry);
+	for (linker = dlg->profile_links; linker; linker = linker->next) {
+		LM_DBG("Running through linkers");
+		if (linker->profile == profile) {
+			LM_DBG("Profile matches");
+			if (profile->has_value == 0) {
+				LM_DBG("Profile has value is zero returning true");
+				dlg_unlock( d_table, d_entry);
+				return 1;
+			} else if (value && value->len == linker->hash_linker.value.len
+					&& memcmp(value->s, linker->hash_linker.value.s, value->len)
+							== 0) {
+				LM_DBG("Profile has value equal to passed value returning true");
+				dlg_unlock( d_table, d_entry);
+				return 1;
+			}
+			/* allow further search - maybe the dialog is inserted twice in
+			 * the same profile, but with different values -bogdan
+			 */
+		}
+	}
+	dlg_unlock( d_table, d_entry);
+	return -1;
+}
+
+
+/*!
+ * \brief Get the size of a profile
+ * \param profile evaluated profile
+ * \param value value
+ * \return the profile size
+ */
+unsigned int get_profile_size(struct dlg_profile_table *profile, str *value)
+{
+	unsigned int n,i;
+	struct dlg_profile_hash *ph;
+
+	if (profile->has_value==0 || value==NULL) {
+		/* iterate through the hash and count all records */
+		lock_get( &profile->lock );
+		for( i=0,n=0 ; i<profile->size ; i++ )
+			n += profile->entries[i].content;
+		lock_release( &profile->lock );
+		return n;
+	} else {
+		/* iterate through the hash entry and count only matching */
+		/* calculate the hash position */
+		i = calc_hash_profile( value, NULL, profile);
+		n = 0;
+		lock_get( &profile->lock );
+		ph = profile->entries[i].first;
+		if(ph) {
+			do {
+				/* compare */
+				if ( value->len==ph->value.len &&
+				memcmp(value->s,ph->value.s,value->len)==0 ) {
+					/* found */
+					n++;
+				}
+				/* next */
+				ph=ph->next;
+			}while( ph!=profile->entries[i].first );
+		}
+		lock_release( &profile->lock );
+		return n;
+	}
+}
+
+/*
+ * Determine if message is in a dialog currently being tracked
+ */
+int	is_known_dlg(struct sip_msg *msg) {
+	if(get_current_dialog(msg) == NULL)
+		return -1;
+
+	return 1;
+}
+
+/****************************** MI commands *********************************/
+
+/*!
+ * \brief Output a profile via MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return MI root output on success, NULL on failure
+ */
+struct mi_root * mi_get_profile(struct mi_root *cmd_tree, void *param)
+{
+	struct mi_node* node;
+	struct mi_root* rpl_tree= NULL;
+	struct mi_node* rpl = NULL;
+	struct mi_attr* attr;
+	struct dlg_profile_table *profile;
+	str *value;
+	str *profile_name;
+	unsigned int size;
+	int len;
+	char *p;
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || !node->value.s || !node->value.len)
+		return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
+	profile_name = &node->value;
+
+	if (node->next) {
+		node = node->next;
+		if (!node->value.s || !node->value.len)
+			return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
+		if (node->next)
+			return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
+		value = &node->value;
+	} else {
+		value = NULL;
+	}
+
+	/* search for the profile */
+	profile = search_dlg_profile( profile_name );
+	if (profile==NULL)
+		return init_mi_tree( 404, MI_SSTR("Profile not found"));
+
+	size = get_profile_size( profile , value );
+
+	rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+	if (rpl_tree==0)
+		return 0;
+	rpl = &rpl_tree->node;
+
+	node = add_mi_node_child(rpl, MI_DUP_VALUE, "profile", 7, NULL, 0);
+	if (node==0) {
+		free_mi_tree(rpl_tree);
+		return NULL;
+	}
+
+	attr = add_mi_attr(node, MI_DUP_VALUE, "name", 4,
+		profile->name.s, profile->name.len);
+	if(attr == NULL) {
+		goto error;
+	}
+
+	if (value) {
+		attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, value->s, value->len);
+	} else {
+		attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, NULL, 0);
+	}
+	if(attr == NULL) {
+		goto error;
+	}
+
+	p= int2str((unsigned long)size, &len);
+	attr = add_mi_attr(node, MI_DUP_VALUE, "count", 5, p, len);
+	if(attr == NULL) {
+		goto error;
+	}
+
+	return rpl_tree;
+error:
+	free_mi_tree(rpl_tree);
+	return NULL;
+}
+
+
+/*!
+ * \brief List the profiles via MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return MI root output on success, NULL on failure
+ */
+struct mi_root * mi_profile_list(struct mi_root *cmd_tree, void *param )
+{
+	struct mi_node* node;
+	struct mi_root* rpl_tree= NULL;
+	struct mi_node* rpl = NULL;
+	struct dlg_profile_table *profile;
+	struct dlg_profile_hash *ph;
+	str *profile_name;
+	str *value;
+	unsigned int i;
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || !node->value.s || !node->value.len)
+		return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
+	profile_name = &node->value;
+
+	if (node->next) {
+		node = node->next;
+		if (!node->value.s || !node->value.len)
+			return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
+		if (node->next)
+			return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
+		value = &node->value;
+	} else {
+		value = NULL;
+	}
+
+	/* search for the profile */
+	profile = search_dlg_profile( profile_name );
+	if (profile==NULL)
+		return init_mi_tree( 404, MI_SSTR("Profile not found"));
+
+	rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+	if (rpl_tree==0)
+		return 0;
+	rpl = &rpl_tree->node;
+
+	/* go through the hash and print the dialogs */
+	if (profile->has_value==0 || value==NULL) {
+		/* no value */
+		lock_get( &profile->lock );
+		for ( i=0 ; i< profile->size ; i++ ) {
+			ph = profile->entries[i].first;
+			if(ph) {
+				do {
+					/* print dialog */
+					if ( mi_print_dlg( rpl, ph->dlg, 0)!=0 )
+						goto error;
+					/* next */
+					ph=ph->next;
+				}while( ph!=profile->entries[i].first );
+			}
+			lock_release( &profile->lock );
+		}
+	} else {
+		/* check for value also */
+		lock_get( &profile->lock );
+		for ( i=0 ; i< profile->size ; i++ ) {
+			ph = profile->entries[i].first;
+			if(ph) {
+				do {
+					if ( value->len==ph->value.len &&
+					memcmp(value->s,ph->value.s,value->len)==0 ) {
+						/* print dialog */
+						if ( mi_print_dlg( rpl, ph->dlg, 0)!=0 )
+							goto error;
+					}
+					/* next */
+					ph=ph->next;
+				}while( ph!=profile->entries[i].first );
+			}
+			lock_release( &profile->lock );
+		}
+	}
+
+	return rpl_tree;
+error:
+	free_mi_tree(rpl_tree);
+	return NULL;
+}

+ 206 - 0
modules/dialog2/dlg_profile.h

@@ -0,0 +1,206 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2008 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2008-04-20  initial version (bogdan)
+ *
+ */
+
+
+
+#ifndef _DIALOG_DLG_PROFILE_H_
+#define _DIALOG_DLG_PROFILE_H_
+
+#include "../../parser/msg_parser.h"
+#include "../../locking.h"
+#include "../../str.h"
+#include "../../modules/tm/h_table.h"
+
+
+
+/*!
+ * \file
+ * \brief Profile related functions for the dialog module
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+
+/*! dialog profile hash list */
+struct dlg_profile_hash {
+	str value; /*!< hash value */
+	struct dlg_cell *dlg; /*!< dialog cell */
+	struct dlg_profile_hash *next;
+	struct dlg_profile_hash *prev;
+	unsigned int hash; /*!< position in the hash table */
+};
+
+
+/*! list with links to dialog profiles */
+struct dlg_profile_link {
+	struct dlg_profile_hash hash_linker;
+	struct dlg_profile_link  *next;
+	struct dlg_profile_table *profile;
+};
+
+
+/*! dialog profile entry */
+struct dlg_profile_entry {
+	struct dlg_profile_hash *first;
+	unsigned int content; /*!< content of the entry */
+};
+
+
+/*! dialog profile table */
+struct dlg_profile_table {
+	str name; /*!< name of the dialog profile */
+	unsigned int size; /*!< size of the dialog profile */
+	unsigned int has_value; /*!< 0 for profiles without value, otherwise it has a value */
+	gen_lock_t lock; /*! lock for concurrent access */
+	struct dlg_profile_entry *entries;
+	struct dlg_profile_table *next;
+};
+
+
+struct dlg_cell *get_current_dlg_pointer(void);
+
+void reset_current_dlg_pointer(void);
+
+struct dlg_cell* get_dialog_from_tm(struct cell *t);
+
+struct dlg_cell *get_current_dialog(struct sip_msg *msg);
+
+/*!
+ * \brief Add profile definitions to the global list
+ * \see new_dlg_profile
+ * \param profiles profile name
+ * \param has_value set to 0 for a profile without value, otherwise it has a value
+ * \return 0 on success, -1 on failure
+ */
+int add_profile_definitions( char* profiles, unsigned int has_value);
+
+
+/*!
+ * \brief Destroy the global dialog profile list
+ */
+void destroy_dlg_profiles(void);
+
+
+/*!
+ * \brief Search a dialog profile in the global list
+ * \note Linear search, this won't have the best performance for huge profile lists
+ * \param name searched dialog profile
+ * \return pointer to the profile on success, NULL otherwise
+ */
+struct dlg_profile_table* search_dlg_profile(str *name);
+
+
+/*!
+ * \brief Cleanup a profile
+ * \param msg SIP message
+ * \param flags unused
+ * \param unused
+ * \return 1
+ */
+int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param );
+
+
+/*!
+ * \brief Destroy dialog linkers
+ * \param linker dialog linker
+ */ 
+void destroy_linkers(struct dlg_profile_link *linker);
+
+
+/*!
+ * \brief Set the global variables to the current dialog
+ * \param msg SIP message
+ * \param dlg dialog cell
+ */
+void set_current_dialog(struct sip_msg *msg, struct dlg_cell *dlg);
+
+
+/*!
+ * \brief Set the dialog profile
+ * \param msg SIP message
+ * \param value value
+ * \param profile dialog profile table
+ * \return 0 on success, -1 on failure
+ */
+int set_dlg_profile(struct sip_msg *msg, str *value,
+		struct dlg_profile_table *profile);
+
+
+/*!
+ * \brief Unset a dialog profile
+ * \param msg SIP message
+ * \param value value
+ * \param profile dialog profile table
+ * \return 1 on success, -1 on failure
+ */
+int unset_dlg_profile(struct sip_msg *msg, str *value,
+		struct dlg_profile_table *profile);
+
+
+/*!
+ * \brief Check if a dialog belongs to a profile
+ * \param msg SIP message
+ * \param profile dialog profile table
+ * \param value value
+ * \return 1 on success, -1 on failure
+ */
+int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile,
+		str *value);
+
+
+/*!
+ * \brief Get the size of a profile
+ * \param profile evaluated profile
+ * \param value value
+ * \return the profile size
+ */
+unsigned int get_profile_size(struct dlg_profile_table *profile, str *value);
+
+
+/*!
+ * \brief Output a profile via MI interface
+ * \param cmd_tree MI command tree
+ * \param param MI parameter
+ * \return MI root output on success, NULL on failure
+ */
+struct mi_root * mi_get_profile(struct mi_root *cmd_tree, void *param );
+
+
+/*!
+ * \brief List the profiles via MI interface
+ * \param cmd_tree MI command tree
+ * \param param unused
+ * \return MI root output on success, NULL on failure
+ */
+struct mi_root * mi_profile_list(struct mi_root *cmd_tree, void *param );
+
+/*!
+ * \brief return true if the messages belongs to a tracked dialog
+ */
+int is_known_dlg(struct sip_msg *msg);
+
+#endif

+ 521 - 0
modules/dialog2/dlg_req_within.c

@@ -0,0 +1,521 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2007-07-10  initial version (ancuta)
+ * 2008-04-04  added direction reporting in dlg callbacks (bogdan)
+ * 2011-10      added support for early dialog termination (jason)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../lib/srdb1/db.h"
+#include "../../dprint.h"
+#include "../../config.h"
+#include "../../socket_info.h"
+#include "../../modules/tm/dlg.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../lib/kmi/tree.h"
+#include "../../lib/kcore/kstats_wrapper.h"
+#include "../../locking.h"
+#include "dlg_timer.h"
+#include "dlg_hash.h"
+#include "dlg_req_within.h"
+
+#define MAX_FWD_HDR        "Max-Forwards: " MAX_FWD CRLF
+#define MAX_FWD_HDR_LEN    (sizeof(MAX_FWD_HDR) - 1)
+
+extern str dlg_extra_hdrs;
+
+int free_tm_dlg(dlg_t *td) {
+    if (td) {
+        if (td->route_set)
+            free_rr(&td->route_set);
+        pkg_free(td);
+    }
+    return 0;
+}
+
+dlg_t * build_dlg_t(struct dlg_cell * cell, int dir) {
+
+    dlg_t* td = NULL;
+    str cseq;
+    unsigned int loc_seq;
+    str route_set;
+    str contact;
+
+    struct dlg_cell_out *dlg_out = 0;
+    struct dlg_entry_out* dlg_entry_out = 0;
+
+    /* if trying to send by to callee we need to get the corresponding dlg_out cell */
+    lock_get(cell->dlg_out_entries_lock);
+    dlg_entry_out = &cell->dlg_entry_out;
+
+    dlg_out = dlg_entry_out->first;
+    //must be concurrent call - lets choose - TODO - ie. check if there is more
+
+    if (!dlg_out) {
+        LM_ERR("Trying to send BYE for dialog with no callee leg\n");
+        lock_release(cell->dlg_out_entries_lock);
+        return NULL;
+    }
+
+    td = (dlg_t*) pkg_malloc(sizeof (dlg_t));
+    if (!td) {
+
+        LM_ERR("out of pkg memory\n");
+        lock_release(cell->dlg_out_entries_lock);
+        return NULL;
+    }
+    memset(td, 0, sizeof (dlg_t));
+
+    if (dir == DLG_CALLER_LEG) {
+        cseq = cell->first_req_cseq;
+        route_set = cell->caller_route_set;
+        contact = cell->caller_contact;
+        td->rem_uri = cell->from_uri;
+        td->loc_uri = dlg_out->to_uri;
+        td->id.rem_tag = cell->from_tag;
+        td->id.loc_tag = dlg_out->to_tag;
+        td->send_sock = cell->caller_bind_addr;
+    } else {
+        cseq = dlg_out->callee_cseq;
+        route_set = dlg_out->callee_route_set;
+        contact = dlg_out->callee_contact;
+        td->rem_uri = dlg_out->to_uri;
+        td->loc_uri = cell->from_uri;
+        td->id.rem_tag = dlg_out->to_tag;
+        td->id.loc_tag = cell->from_tag;
+        td->send_sock = dlg_out->callee_bind_addr;
+    }
+
+    if (str2int(&cseq, &loc_seq) != 0) {
+        LM_ERR("invalid cseq\n");
+        goto error;
+    }
+
+    /*we don not increase here the cseq as this will be done by TM*/
+    td->loc_seq.value = loc_seq;
+    td->loc_seq.is_set = 1;
+
+    /*route set*/
+    if (route_set.s && route_set.len) {
+
+        if (parse_rr_body(route_set.s, route_set.len, &td->route_set) != 0) {
+            LM_ERR("failed to parse route set\n");
+            goto error;
+        }
+    }
+
+    if (contact.s == 0 || contact.len == 0) {
+
+        LM_ERR("no contact available\n");
+        goto error;
+    }
+
+    td->id.call_id = cell->callid;
+    td->rem_target = contact;
+    td->state = DLG_CONFIRMED;
+
+    lock_release(cell->dlg_out_entries_lock);
+    return td;
+
+error:
+    lock_release(cell->dlg_out_entries_lock);
+    free_tm_dlg(td);
+    return NULL;
+}
+
+/*callback function to handle responses to the BYE request */
+void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps) {
+
+    struct dlg_cell* dlg;
+    int event, old_state, new_state, unref, ret;
+    struct dlg_cell_out *dlg_out = 0;
+
+    if (ps->param == NULL || *ps->param == NULL) {
+        LM_ERR("invalid parameter\n");
+        return;
+    }
+
+    if (ps->code < 200) {
+        LM_DBG("receiving a provisional reply\n");
+        return;
+    }
+
+    LM_DBG("receiving a final reply %d\n", ps->code);
+
+    dlg = (struct dlg_cell *) (*(ps->param));
+    event = DLG_EVENT_REQBYE;
+
+    //get the corresponding dlg out structure for this REQ
+    struct dlg_entry_out *dlg_entry_out = &dlg->dlg_entry_out;
+    lock_get(dlg->dlg_out_entries_lock);
+    dlg_out = dlg_entry_out->first; //TODO check for concurrent call
+    if (!dlg_out)
+        return;
+
+    next_state_dlg(dlg, event, &old_state, &new_state, &unref, &dlg_out->to_tag);
+
+    lock_release(dlg->dlg_out_entries_lock);
+
+    if (new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED) {
+
+        LM_DBG("removing dialog with h_entry %u and h_id %u\n",
+                dlg->h_entry, dlg->h_id);
+
+        /* remove from timer */
+        ret = remove_dialog_timer(&dlg->tl);
+        if (ret < 0) {
+            LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] "
+                    "with clid '%.*s'\n",
+                    dlg, dlg->h_entry, dlg->h_id,
+                    dlg->callid.len, dlg->callid.s);
+        } else if (ret > 0) {
+            LM_WARN("inconsitent dlg timer data on dlg %p [%u:%u] "
+                    "with clid '%.*s'\n",
+                    dlg, dlg->h_entry, dlg->h_id,
+                    dlg->callid.len, dlg->callid.s);
+        } else {
+            unref++;
+        }
+        /* dialog terminated (BYE) */
+        run_dlg_callbacks(DLGCB_TERMINATED, dlg, ps->req, ps->rpl, DLG_DIR_NONE, 0);
+
+        /* derefering the dialog */
+        unref_dlg(dlg, unref + 1);
+    }
+
+    if (new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED) {
+        /* trash the dialog from DB and memory */
+
+        /* force delete from mem */
+        unref_dlg(dlg, 1);
+    }
+
+}
+
+static inline int build_extra_hdr(struct dlg_cell * cell, str *extra_hdrs,
+        str *str_hdr) {
+    char *p;
+
+    str_hdr->len = MAX_FWD_HDR_LEN + dlg_extra_hdrs.len;
+    if (extra_hdrs && extra_hdrs->len > 0)
+        str_hdr->len += extra_hdrs->len;
+
+    str_hdr->s = (char*) pkg_malloc(str_hdr->len * sizeof (char));
+    if (!str_hdr->s) {
+        LM_ERR("out of pkg memory\n");
+        goto error;
+    }
+
+    memcpy(str_hdr->s, MAX_FWD_HDR, MAX_FWD_HDR_LEN);
+    p = str_hdr->s + MAX_FWD_HDR_LEN;
+    if (dlg_extra_hdrs.len) {
+        memcpy(p, dlg_extra_hdrs.s, dlg_extra_hdrs.len);
+        p += dlg_extra_hdrs.len;
+    }
+    if (extra_hdrs && extra_hdrs->len > 0)
+        memcpy(p, extra_hdrs->s, extra_hdrs->len);
+
+    return 0;
+
+error:
+    return -1;
+}
+
+/* cell- pointer to a struct dlg_cell
+ * dir- direction: the request will be sent to:
+ * 		DLG_CALLER_LEG (0): caller
+ * 		DLG_CALLEE_LEG (1): callee
+ */
+static inline int send_bye(struct dlg_cell * cell, int dir, str *hdrs) {
+    uac_req_t uac_r;
+    dlg_t* dialog_info;
+    str met = {"BYE", 3};
+    int result;
+    /* do not send BYE request for non-confirmed dialogs (not supported) */
+    if (cell->state != DLG_STATE_CONFIRMED) {
+        LM_ERR("terminating only 1 side of non-confirmed dialogs not supported by this function\n");
+        return -1;
+    }
+
+    /*verify direction*/
+    if ((dialog_info = build_dlg_t(cell, dir)) == 0) {
+        LM_ERR("failed to create dlg_t\n");
+        goto err;
+    }
+
+    LM_DBG("sending BYE to %s\n", (dir == DLG_CALLER_LEG) ? "caller" : "callee");
+
+    ref_dlg(cell, 1);
+
+    memset(&uac_r, '\0', sizeof (uac_req_t));
+    set_uac_req(&uac_r, &met, hdrs, NULL, dialog_info, TMCB_LOCAL_COMPLETED,
+            bye_reply_cb, (void*) cell);
+
+    result = d_tmb.t_request_within(&uac_r);
+
+    if (result < 0) {
+        LM_ERR("failed to send the BYE request\n");
+        goto err1;
+    }
+
+    free_tm_dlg(dialog_info);
+
+    LM_DBG("BYE sent to %s\n", (dir == 0) ? "caller" : "callee");
+    return 0;
+
+err1:
+    unref_dlg(cell, 1);
+err:
+    if (dialog_info)
+        free_tm_dlg(dialog_info);
+    return -1;
+}
+
+/*static void early_transaction_destroyed(struct cell* t, int type, struct tmcb_params *param) {
+    struct dlg_cell *dlg = (struct dlg_cell *) (*param->param);
+
+    if (!dlg)
+        return;
+
+    LM_DBG("Early transaction destroyed\n");
+}*/
+
+/* side =
+ * 0: caller
+ * 1: callee
+ * 2: all
+ */
+int dlg_terminate(struct dlg_cell *dlg, struct sip_msg *msg, str *reason, int side, str *extra_hdrs) {
+
+    struct cell* t;
+    str default_reason = {"call failed", 11};
+    int cfg_cmd = 0;
+    str default_extra_headers = {0,0};
+
+    if (!dlg) {
+        LM_ERR("calling end_dialog with NULL pointer dlg\n");
+        return -1;
+    }
+
+    if (!extra_hdrs)
+        extra_hdrs = &default_extra_headers;
+
+
+    if (msg) {
+        //assume called from cfg command -> dlg_terminate, as opposed to internal API or mi interface
+        cfg_cmd = 1;
+    }
+
+    if (!reason || reason->len <= 0 || !reason->s) {
+        reason = &default_reason;
+    }
+
+    if (dlg->state != DLG_STATE_CONFIRMED) {
+        if (side != 2) {
+            LM_ERR("can't terminate only 1 side of an early dialog\n");
+            return -1;
+        }
+        if (dlg->transaction) {
+            LM_DBG("terminating early dialog with %d outbound forks\n",
+                    dlg->transaction->nr_of_outgoings);
+
+            t = dlg->transaction;
+
+            if (t && t!=(void*) -1  && t->uas.request) {
+                if (t->method.len!=6 || t->method.s[0]!='I' || t->method.s[1]!='N' || t->method.s[2]!='V')
+		{
+			//well this is the transaction of a subsequent request within the dialog
+			//and the dialog is not confirmed yet, so its a PRACK or an UPDATE
+			//could also be an options, but the important thing is how am i going to get
+			//the transaction of the invite, that is the one i have to cancel
+			LM_WARN("this is not my transaction so where am i?\n");
+                        return 1; //TODO - need to check why we got in here once before? this crashed on t_reply as t seemed invalid
+		}
+
+                //TODO: here we are assuming none of the CALLEE's have sent a 200, in
+                //which case we would have to send an ACK, BYE
+                //so right now - we are sending 488 to caller and CANCEL's to all CALLEEs
+
+                LM_DBG("tearing down dialog in EARLY state - no clients responded > 199\n");
+                if (cfg_cmd) {
+                        d_tmb.t_reply(msg,488,reason->s);
+                        d_tmb.t_release(msg);
+                } else {
+                        d_tmb.t_reply(t->uas.request,488,reason->s);
+                        d_tmb.t_release(t->uas.request);
+                }
+            }
+        } else {
+            LM_WARN("can't terminate early dialog without a transaction\n");
+            return -1;
+        }
+    } else {
+        LM_DBG("terminating confirmed dialog\n");
+        if (side == DLG_CALLER_LEG /* 0 */ || side == DLG_CALLEE_LEG /* 1 */) {
+            if (dlg_bye(dlg, (extra_hdrs->len > 0) ? extra_hdrs : NULL, side) < 0)
+                return -1;
+
+        } else {
+            if (dlg_bye_all(dlg, (extra_hdrs->len > 0) ? extra_hdrs : NULL) < 0)
+                return -1;
+        }
+    }
+    return 1;
+}
+
+/*parameters from MI: callid, from tag, to tag*/
+/* TODO: add reason parameter to mi interface */
+struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param) {
+
+    struct mi_node* node;
+    struct dlg_cell * dlg = NULL;
+    str mi_extra_hdrs = {NULL, 0};
+    int status, msg_len;
+    char *msg;
+
+    str callid = {NULL, 0};
+    str ftag = {NULL, 0};
+    str ttag = {NULL, 0};
+
+    if (d_table == NULL)
+        goto end;
+
+    node = cmd_tree->node.kids;
+
+    if (node == NULL || node->next == NULL  || node->next->next == NULL)
+        return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+    if (!node->value.s || !node->value.len) {
+        goto error;
+    } else {
+        callid = node->value;
+    }
+    node = node->next;
+    if (!node->value.s || !node->value.len) {
+        goto error;
+    } else {
+        ftag = node->value;
+    }
+    node = node->next;
+    if (!node->value.s || !node->value.len) {
+        goto error;
+    } else {
+        ttag = node->value;
+    }
+
+    if (node->next) {
+        node = node->next;
+        if (node->value.len && node->value.s)
+            mi_extra_hdrs = node->value;
+    }
+
+    unsigned int dir = DLG_DIR_NONE;
+    LM_DBG("Looking for callid [%.*s]\n", callid.len, callid.s);
+    dlg = get_dlg(&callid, &ftag, &ttag, &dir); //increments ref count!
+
+    if (dlg) {
+        LM_DBG("Found dialog to terminate and it is in state [%i]\n", dlg->state);
+
+        if (dlg_terminate(dlg, 0, NULL/*reson*/, /* all sides of a dialog*/ 2, &mi_extra_hdrs) < 0) {
+            status = 500;
+            msg = MI_DLG_OPERATION_ERR;
+            msg_len = MI_DLG_OPERATION_ERR_LEN;
+        } else {
+            status = 200;
+            msg = MI_OK_S;
+            msg_len = MI_OK_LEN;
+        }
+        unref_dlg(dlg, 1);
+
+        return init_mi_tree(status, msg, msg_len);
+    }
+end:
+    return init_mi_tree(404, MI_DIALOG_NOT_FOUND, MI_DIALOG_NOT_FOUND_LEN);
+
+error:
+    return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+
+}
+
+int dlg_bye(struct dlg_cell *dlg, str *hdrs, int side) {
+    str all_hdrs = {0, 0};
+    int ret;
+
+    if (side == DLG_CALLER_LEG) {
+        if (dlg->dflags & DLG_FLAG_CALLERBYE)
+            return -1;
+        dlg->dflags |= DLG_FLAG_CALLERBYE;
+    } else {
+        if (dlg->dflags & DLG_FLAG_CALLEEBYE)
+            return -1;
+        dlg->dflags |= DLG_FLAG_CALLEEBYE;
+    }
+    if ((build_extra_hdr(dlg, hdrs, &all_hdrs)) != 0) {
+        LM_ERR("failed to build dlg headers\n");
+        return -1;
+    }
+    ret = send_bye(dlg, side, &all_hdrs);
+    pkg_free(all_hdrs.s);
+    return ret;
+}
+
+/* Wrapper for terminating dialog from API - from other modules */
+int w_api_terminate_dlg(str *callid, str *ftag, str *ttag, str *hdrs, str* reason) {
+    struct dlg_cell *dlg;
+
+    unsigned int dir = DLG_DIR_NONE;
+    dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count!
+
+    if (!dlg) {
+        LM_ERR("Asked to tear down non existent dialog\n");
+        return -1;
+    }
+
+    unref_dlg(dlg, 1);
+
+    return dlg_terminate(dlg, NULL, NULL/*reason*/, 2, hdrs);
+
+}
+
+int dlg_bye_all(struct dlg_cell *dlg, str *hdrs) {
+    str all_hdrs = {0, 0};
+    int ret;
+
+    if ((build_extra_hdr(dlg, hdrs, &all_hdrs)) != 0) {
+        LM_ERR("failed to build dlg headers\n");
+        return -1;
+    }
+
+    ret = send_bye(dlg, DLG_CALLER_LEG, &all_hdrs);
+    ret |= send_bye(dlg, DLG_CALLEE_LEG, &all_hdrs);
+
+    pkg_free(all_hdrs.s);
+    return ret;
+
+}
+

+ 58 - 0
modules/dialog2/dlg_req_within.h

@@ -0,0 +1,58 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2007-07-10  initial version (ancuta)
+*/
+
+
+
+
+#ifndef DLG_REQUEST_WITHIN_H
+#define DLG_REQUEST_WITHIN_H
+
+#include "dlg_hash.h"
+#include "../../modules/tm/tm_load.h"
+
+#define MAX_FWD			"70"
+#define MAX_SIZE		256
+#define RCV_BYE_REPLY	1
+
+#define MI_DIALOG_NOT_FOUND 		"Requested Dialog not found"
+#define MI_DIALOG_NOT_FOUND_LEN 	(sizeof(MI_DIALOG_NOT_FOUND)-1)
+#define MI_DLG_OPERATION_ERR		"Operation failed"
+#define MI_DLG_OPERATION_ERR_LEN	(sizeof(MI_DLG_OPERATION_ERR)-1)
+
+extern struct tm_binds d_tmb;
+//extern int dlg_enable_stats;
+//extern stat_var * active_dlgs;
+
+struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param );
+
+dlg_t* build_dlg_t(struct dlg_cell * cell, int dir);
+int free_tm_dlg(dlg_t *td);
+int dlg_bye(struct dlg_cell *dlg, str *hdrs, int side);
+int dlg_bye_all(struct dlg_cell *dlg, str *hdrs);
+int w_api_terminate_dlg(str *call_id, str *from_tag, str *to_tag, str *hdrs, str* reason);
+int dlg_terminate(struct dlg_cell *dlg, struct sip_msg* msg, str *reason, int side, str *extra_headers);
+
+#endif

+ 287 - 0
modules/dialog2/dlg_timer.c

@@ -0,0 +1,287 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-14  initial version (bogdan)
+ * 2007-03-06  to avoid races, tests on timer links are done under locks
+ *             (bogdan)
+ */
+
+/*!
+ * \file
+ * \brief Timer related functions for the dialog module
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#include "../../mem/shm_mem.h"
+#include "../../timer.h"
+#include "dlg_timer.h"
+
+/*! global dialog timer */
+struct dlg_timer *d_timer = 0;
+/*! global dialog timer handler */
+dlg_timer_handler timer_hdl = 0;
+
+
+/*!
+ * \brief Initialize the dialog timer handler
+ * Initialize the dialog timer handler, allocate the lock and a global
+ * timer in shared memory. The global timer handler will be set on success.
+ * \param hdl dialog timer handler
+ * \return 0 on success, -1 on failure
+ */
+int init_dlg_timer(dlg_timer_handler hdl)
+{
+	d_timer = (struct dlg_timer*)shm_malloc(sizeof(struct dlg_timer));
+	if (d_timer==0) {
+		LM_ERR("no more shm mem\n");
+		return -1;
+	}
+	memset( d_timer, 0, sizeof(struct dlg_timer) );
+
+	d_timer->first.next = d_timer->first.prev = &(d_timer->first);
+
+	d_timer->lock = lock_alloc();
+	if (d_timer->lock==0) {
+		LM_ERR("failed to alloc lock\n");
+		goto error0;
+	}
+
+	if (lock_init(d_timer->lock)==0) {
+		LM_ERR("failed to init lock\n");
+		goto error1;
+	}
+
+	timer_hdl = hdl;
+	return 0;
+error1:
+	lock_dealloc(d_timer->lock);
+error0:
+	shm_free(d_timer);
+	d_timer = 0;
+	return -1;
+}
+
+
+/*!
+ * \brief Destroy global dialog timer
+ */
+void destroy_dlg_timer(void)
+{
+	if (d_timer==0)
+		return;
+
+	lock_destroy(d_timer->lock);
+	lock_dealloc(d_timer->lock);
+
+	shm_free(d_timer);
+	d_timer = 0;
+}
+
+
+/*!
+ * \brief Helper function for insert_dialog_timer
+ * \see insert_dialog_timer
+ * \param tl dialog timer list
+ */
+static inline void insert_dialog_timer_unsafe(struct dlg_tl *tl)
+{
+	struct dlg_tl* ptr;
+
+	/* insert in sorted order */
+	for(ptr = d_timer->first.prev; ptr != &d_timer->first ; ptr = ptr->prev) {
+		if ( ptr->timeout <= tl->timeout )
+			break;
+	}
+
+	LM_DBG("inserting %p for %d\n", tl,tl->timeout);
+	tl->prev = ptr;
+	tl->next = ptr->next;
+	tl->prev->next = tl;
+	tl->next->prev = tl;
+}
+
+
+/*!
+ * \brief Insert a dialog timer to the list
+ * \param tl dialog timer list
+ * \param interval timeout value in seconds
+ * \return 0 on success, -1 when the input timer list is invalid
+ */
+int insert_dlg_timer(struct dlg_tl *tl, int interval)
+{
+	lock_get( d_timer->lock);
+
+	if (tl->next!=0 || tl->prev!=0) {
+		LM_CRIT("Trying to insert a bogus dlg tl=%p tl->next=%p tl->prev=%p\n",
+			tl, tl->next, tl->prev);
+		lock_release( d_timer->lock);
+		return -1;
+	}
+	tl->timeout = get_ticks()+interval;
+	insert_dialog_timer_unsafe( tl );
+
+	lock_release( d_timer->lock);
+
+	return 0;
+}
+
+
+/*!
+ * \brief Helper function for remove_dialog_timer
+ * \param tl dialog timer list
+ * \see remove_dialog_timer
+ */
+static inline void remove_dialog_timer_unsafe(struct dlg_tl *tl)
+{
+	tl->prev->next = tl->next;
+	tl->next->prev = tl->prev;
+}
+
+
+/*!
+ * \brief Remove a dialog timer from the list
+ * \param tl dialog timer that should be removed
+ * \return 1 when the input timer is empty, 0 when the timer was removed,
+ * -1 when the input timer list is invalid
+ */
+int remove_dialog_timer(struct dlg_tl *tl)
+{
+	lock_get( d_timer->lock);
+
+	if (tl->prev==NULL && tl->timeout==0) {
+		lock_release( d_timer->lock);
+		return 1;
+	}
+
+	if (tl->prev==NULL || tl->next==NULL) {
+		LM_CRIT("bogus tl=%p tl->prev=%p tl->next=%p\n",
+			tl, tl->prev, tl->next);
+		lock_release( d_timer->lock);
+		return -1;
+	}
+
+	remove_dialog_timer_unsafe(tl);
+	tl->next = NULL;
+	tl->prev = NULL;
+	tl->timeout = 0;
+
+	lock_release( d_timer->lock);
+	return 0;
+}
+
+
+/*!
+ * \brief Update a dialog timer on the list
+ * \param tl dialog timer
+ * \param timeout new timeout value in seconds
+ * \return 0 on success, -1 when the input list is invalid
+ * \note the update is implemented as a remove, insert
+ */
+int update_dlg_timer(struct dlg_tl *tl, int timeout)
+{
+	lock_get( d_timer->lock);
+
+	if (tl->next==0 || tl->prev==0) {
+		LM_CRIT("Trying to update a bogus dlg tl=%p tl->next=%p tl->prev=%p\n",
+			tl, tl->next, tl->prev);
+		lock_release( d_timer->lock);
+		return -1;
+	}
+	remove_dialog_timer_unsafe( tl );
+	tl->timeout = get_ticks()+timeout;
+	insert_dialog_timer_unsafe( tl );
+
+	lock_release( d_timer->lock);
+	return 0;
+}
+
+
+/*!
+ * \brief Helper function for dlg_timer_routine
+ * \param time time for expiration check
+ * \return list of expired dialogs on success, 0 on failure
+ */
+static inline struct dlg_tl* get_expired_dlgs(unsigned int time)
+{
+	struct dlg_tl *tl , *end, *ret;
+
+	lock_get( d_timer->lock);
+
+	if (d_timer->first.next==&(d_timer->first)
+	|| d_timer->first.next->timeout > time ) {
+		lock_release( d_timer->lock);
+		return 0;
+	}
+
+	end = &d_timer->first;
+	tl = d_timer->first.next;
+	LM_DBG("start with tl=%p tl->prev=%p tl->next=%p (%d) at %d "
+		"and end with end=%p end->prev=%p end->next=%p\n",
+		tl,tl->prev,tl->next,tl->timeout,time,
+		end,end->prev,end->next);
+	while( tl!=end && tl->timeout <= time) {
+		LM_DBG("getting tl=%p tl->prev=%p tl->next=%p with %d\n",
+			tl,tl->prev,tl->next,tl->timeout);
+		tl->prev = 0;
+		tl->timeout = 0;
+		tl=tl->next;
+	}
+	LM_DBG("end with tl=%p tl->prev=%p tl->next=%p and d_timer->first.next->prev=%p\n",
+		tl,tl->prev,tl->next,d_timer->first.next->prev);
+
+	if (tl==end && d_timer->first.next->prev) {
+		ret = 0;
+	} else {
+		ret = d_timer->first.next;
+		tl->prev->next = 0;
+		d_timer->first.next = tl;
+		tl->prev = &d_timer->first;
+	}
+
+	lock_release( d_timer->lock);
+
+	return ret;
+}
+
+
+/*!
+ * \brief Timer routine for expiration of dialogs
+ * Timer handler for expiration of dialogs, runs the global timer handler on them.
+ * \param time for expiration checks
+ * \param attr unused
+ */
+void dlg_timer_routine(unsigned int ticks , void * attr)
+{
+	struct dlg_tl *tl, *ctl;
+
+	tl = get_expired_dlgs( ticks );
+
+	while (tl) {
+		ctl = tl;
+		tl = tl->next;
+		ctl->next = NULL;
+		LM_DBG("tl=%p next=%p\n", ctl, tl);
+		timer_hdl( ctl );
+	}
+}

+ 114 - 0
modules/dialog2/dlg_timer.h

@@ -0,0 +1,114 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Voice System SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2006-04-14  initial version (bogdan)
+ */
+
+/*!
+ * \file
+ * \brief Timer related functions for the dialog module
+ * \ingroup dialog
+ * Module: \ref dialog
+ */
+
+#ifndef _DIALOG_DLG_TIMER_H_
+#define _DIALOG_DLG_TIMER_H_
+
+
+#include "../../locking.h"
+
+
+/*! dialog timeout list */
+struct dlg_tl
+{
+	struct dlg_tl     *next;
+	struct dlg_tl     *prev;
+	volatile unsigned int  timeout; /*!< timeout in seconds */
+};
+
+
+/*! dialog timer */
+struct dlg_timer
+{
+	struct dlg_tl   first; /*!< dialog timeout list */
+	gen_lock_t      *lock; /*!< lock for the list */
+};
+
+
+/*! dialog timer handler */
+typedef void (*dlg_timer_handler)(struct dlg_tl *);
+
+
+/*!
+ * \brief Initialize the dialog timer handler
+ * Initialize the dialog timer handler, allocate the lock and a global
+ * timer in shared memory. The global timer handler will be set on success.
+ * \param hdl dialog timer handler
+ * \return 0 on success, -1 on failure
+ */
+int init_dlg_timer(dlg_timer_handler);
+
+
+/*!
+ * \brief Destroy global dialog timer
+ */
+void destroy_dlg_timer(void);
+
+
+/*!
+ * \brief Insert a dialog timer to the list
+ * \param tl dialog timer list
+ * \param interval timeout value in seconds
+ * \return 0 on success, -1 when the input timer list is invalid
+ */
+int insert_dlg_timer(struct dlg_tl *tl, int interval);
+
+
+/*!
+ * \brief Remove a dialog timer from the list
+ * \param tl dialog timer that should be removed
+ * \return 1 when the input timer is empty, 0 when the timer was removed,
+ * -1 when the input timer list is invalid
+ */
+int remove_dialog_timer(struct dlg_tl *tl);
+
+
+/*!
+ * \brief Update a dialog timer on the list
+ * \param tl dialog timer
+ * \param timeout new timeout value in seconds
+ * \return 0 on success, -1 when the input list is invalid
+ * \note the update is implemented as a remove, insert
+ */
+int update_dlg_timer(struct dlg_tl *tl, int timeout);
+
+
+/*!
+ * \brief Timer routine for expiration of dialogs
+ * Timer handler for expiration of dialogs, runs the global timer handler on them.
+ * \param time for expiration checks
+ * \param attr unused
+ */
+void dlg_timer_routine(unsigned int ticks , void * attr);
+
+#endif

+ 766 - 0
modules/dialog2/dlg_var.c

@@ -0,0 +1,766 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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
+ */
+		       
+#include "../../route.h"
+
+#include "dlg_var.h"
+#include "dlg_hash.h"
+#include "dlg_profile.h"
+
+dlg_ctx_t _dlg_ctx;
+extern int spiral_detected;
+
+/*! global variable table, in case the dialog does not exist yet */
+struct dlg_var * var_table = 0;
+/*! ID of the current message */
+int msg_id;
+
+int dlg_cfg_cb(struct sip_msg *foo, unsigned int flags, void *bar)
+{
+        memset(&_dlg_ctx, 0, sizeof(dlg_ctx_t));
+	return 1;
+}
+
+
+static inline struct dlg_var *new_dlg_var(str *key, str *val)
+{
+	struct dlg_var *var;
+
+	var =(struct dlg_var*)shm_malloc(sizeof(struct dlg_var));
+	if (var==NULL) {
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	var->next = NULL;
+	var->vflags = DLG_FLAG_NEW;
+	/* set key */
+	var->key.len = key->len;
+	var->key.s = (char*)shm_malloc(var->key.len);
+	if (var->key.s==NULL) {
+		shm_free(var);			
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	memcpy(var->key.s, key->s, key->len);
+	/* set value */
+	var->value.len = val->len;
+	var->value.s = (char*)shm_malloc(var->value.len);
+	if (var->value.s==NULL) {
+		shm_free(var->key.s);			
+		shm_free(var);			
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	memcpy(var->value.s, val->s, val->len);
+	return var;
+}
+
+/*! Delete the current var-list */
+void free_local_varlist() {
+	struct dlg_var *var;
+	while (var_table) {
+		var = var_table;
+		var_table = var_table->next;
+		shm_free(var->key.s);
+		shm_free(var->value.s);
+		shm_free(var);
+	}
+}
+
+/*! Retrieve the local var-list pointer */
+struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointer) {
+	struct dlg_var *var;
+	/* New list, delete the old one */
+	if (msg->id != msg_id) {
+		free_local_varlist();
+		msg_id = msg->id;
+	}
+	var = var_table;
+	if (clear_pointer)
+		var_table = NULL;
+	return var;
+}
+
+/* Adds, updates and deletes dialog variables */
+int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new)
+{
+	struct dlg_var * var = NULL;
+	struct dlg_var * it;
+	struct dlg_var * it_prev;
+	struct dlg_var ** var_list;
+	
+	if (dlg) 
+		var_list = &dlg->vars;
+	else 
+		var_list = &var_table;
+
+	if ( val && (var=new_dlg_var(key, val))==NULL) {
+		LM_ERR("failed to create new dialog variable\n");
+		return -1;
+	}
+
+	/* iterate the list */
+	for( it_prev=NULL, it=*var_list ; it ; it_prev=it,it=it->next) {
+		if (key->len==it->key.len && memcmp(key->s,it->key.s,key->len)==0
+			&& (it->vflags & DLG_FLAG_DEL) == 0) {
+			/* found -> replace or delete it */
+			if (val==NULL) {
+				/* delete it */
+				if (it_prev) it_prev->next = it->next;
+				else *var_list = it->next;
+				/* Set the delete-flag for the current var: */
+				it->vflags &= DLG_FLAG_DEL;
+			} else {
+				/* replace the current it with var and free the it */
+				var->next = it->next;
+				/* Take the previous vflags: */
+				var->vflags = it->vflags & DLG_FLAG_CHANGED;
+				if (it_prev) it_prev->next = var;
+				else *var_list = var;				  
+			}
+
+			/* Free this var: */
+			shm_free(it->key.s);
+			shm_free(it->value.s);
+			shm_free(it);
+			return 0;
+		}
+	}
+
+	/* not found: */
+
+	if (!var) {
+		LM_ERR("dialog variable <%.*s> does not exist in variable list\n", key->len, key->s);
+		return -1;
+	}
+
+	/* insert a new one at the beginning of the list */
+	var->next = *var_list;
+	*var_list = var;
+
+	return 0;
+}
+
+str * get_dlg_variable_unsafe(struct dlg_cell *dlg, str *key)
+{
+	struct dlg_var *var, *var_list;
+
+	if (dlg) 
+		var_list = dlg->vars;
+	else
+		var_list = var_table;
+
+	/* iterate the list */
+	for(var=var_list ; var ; var=var->next) {
+		if (key->len==var->key.len && memcmp(key->s,var->key.s,key->len)==0
+		&& (var->vflags & DLG_FLAG_DEL) == 0) {
+			return &var->value;
+		}
+	}
+
+	return NULL;
+}
+
+int pv_parse_dialog_var_name(pv_spec_p sp, str *in)
+{
+	if(in==NULL || in->s==NULL || sp==NULL)
+		return -1;
+
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = AVP_NAME_STR;
+	sp->pvp.pvn.u.isname.name.s = *in;
+
+	return 0;
+}
+
+/*! Internal debugging function: Prints the list of dialogs */
+void print_lists(struct dlg_cell *dlg) {
+	struct dlg_var *varlist;
+	varlist = var_table;
+	LM_DBG("Internal var-list (%p):\n", varlist);
+	while (varlist) {
+		LM_DBG("%.*s=%.*s (flags %i)\n",
+			varlist->key.len, varlist->key.s,
+			varlist->value.len, varlist->value.s,
+			varlist->vflags);
+		varlist = varlist->next;
+	}
+	if (dlg) {
+		varlist = dlg->vars;
+		LM_DBG("Dialog var-list (%p):\n", varlist);
+		while (varlist) {
+			LM_DBG("%.*s=%.*s (flags %i)\n",
+				varlist->key.len, varlist->key.s,
+				varlist->value.len, varlist->value.s,
+				varlist->vflags);
+			varlist = varlist->next;
+		}
+	}
+}
+
+str * api_get_dlg_variable(str *callid, str *ftag, str *ttag, str *key) {
+    struct dlg_cell *dlg;
+    
+    unsigned int dir = DLG_DIR_NONE;
+    dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count!
+    
+    if (!dlg) {
+        LM_ERR("Asked to tear down non existent dialog\n");
+        return NULL;
+    }
+    
+    unref_dlg(dlg, 1);
+    
+    return get_dlg_variable(dlg, key);
+}
+
+str * get_dlg_variable(struct dlg_cell *dlg, str *key)
+{
+    str* var = NULL;
+
+    if( !dlg || !key || key->len > strlen(key->s))
+    {
+        LM_ERR("BUG - bad parameters\n");
+
+        return NULL;
+    }
+
+    dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+    var = get_dlg_variable_unsafe( dlg, key);
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+    return var;
+}
+
+int api_set_dlg_variable(str *callid, str *ftag, str *ttag, str *key, str *val) {
+    struct dlg_cell *dlg;
+    
+    unsigned int dir = DLG_DIR_NONE;
+    dlg = get_dlg(callid, ftag, ttag, &dir); //increments ref count!
+    
+    if (!dlg) {
+        LM_ERR("Asked to tear down non existent dialog\n");
+        return -1;
+    }
+    
+    unref_dlg(dlg, 1);
+     
+    return set_dlg_variable(dlg, key, val);
+    
+   
+}
+
+int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val)
+{
+    if( !dlg || !key || key->len > strlen(key->s) || (val && val->len > strlen(val->s)))
+    {
+        LM_ERR("BUG - bad parameters\n");
+        return -1;
+    }
+
+    dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+
+    if( !val)
+    {
+        if (set_dlg_variable_unsafe(dlg, key, NULL, 1)!=0) {
+            LM_ERR("failed to delete dialog variable <%.*s>\n", key->len,key->s);
+            goto error;
+        }
+    } else {
+        if (set_dlg_variable_unsafe(dlg, key, val, 1)!=0) {
+            LM_ERR("failed to store dialog values <%.*s>\n",key->len,key->s);
+            goto error;
+        }
+    }
+
+    dlg->dflags &= DLG_FLAG_CHANGED_VARS;
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+    print_lists(dlg);
+
+    return 0;
+
+error:
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+    return -1;
+}
+
+int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
+{
+	struct dlg_cell *dlg;
+	str * value;
+
+	if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL) {
+		LM_CRIT("BUG - bad parameters\n");
+		return -1;
+	}
+
+	/* Retrieve the current dialog */
+	dlg=get_current_dialog( msg);
+
+	if (dlg) {
+		/* Lock the dialog */
+		dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+	} else {
+		/* Verify the local list */
+		get_local_varlist_pointer(msg, 0);
+	}
+
+	value = get_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s);
+
+	print_lists(dlg);
+
+	/* unlock dialog */
+	if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+	if (value)
+		return pv_get_strval(msg, param, res, value);
+
+
+	return 0;
+}
+
+int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
+{
+	struct dlg_cell *dlg;
+
+	/* Retrieve the current dialog */
+	dlg=get_current_dialog( msg);
+	
+	if (dlg) {
+		/* Lock the dialog */
+		dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+	} else {
+		/* Verify the local list */
+		get_local_varlist_pointer(msg, 0);
+	}
+
+	if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) {
+		LM_CRIT("BUG - bad parameters\n");
+		return -1;
+	}
+
+	if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) {
+		/* if NULL, remove the value */
+		if (set_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s, NULL, 1)!=0) {
+			LM_ERR("failed to delete dialog variable <%.*s>\n", param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s);
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+	} else {
+		/* if value, must be string */
+		if ( !(val->flags&PV_VAL_STR)) {
+			LM_ERR("non-string values are not supported\n");
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+
+		if (set_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s, &val->rs, 1)!=0) {
+			LM_ERR("failed to store dialog values <%.*s>\n",param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s);
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+	}
+	/* unlock dialog */
+	if (dlg) {
+		dlg->dflags &= DLG_FLAG_CHANGED_VARS;		
+		dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+	}
+	print_lists(dlg);
+
+	return 0;
+}
+
+int pv_get_dlg_ctx(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res)
+{
+	if(param==NULL)
+		return -1;
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 1:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.flags);
+		case 2:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.timeout);
+		case 3:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.to_bye);
+		case 4:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.to_route);
+		case 5:
+			_dlg_ctx.set = (_dlg_ctx.dlg==NULL)?0:1;
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.set);
+		case 6:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dir);
+		default:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.on);
+	}
+	return 0;
+}
+
+int pv_set_dlg_ctx(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val)
+{
+	int n;
+	char *rtp;
+
+	if(param==NULL)
+		return -1;
+
+	if(val==NULL)
+		n = 0;
+	else
+		n = val->ri;
+
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 1:
+			_dlg_ctx.flags = n;
+		break;
+		case 2:
+			_dlg_ctx.timeout = n;
+		break;
+		case 3:
+			_dlg_ctx.to_bye = n;
+		break;
+		case 4:
+			if(val->flags&PV_VAL_STR) {
+				if(val->rs.s[val->rs.len]=='\0'
+						&& val->rs.len<DLG_TOROUTE_SIZE) {
+					_dlg_ctx.to_route = route_lookup(&main_rt, val->rs.s);
+					strcpy(_dlg_ctx.to_route_name, val->rs.s);
+				} else _dlg_ctx.to_route = 0;
+			} else {
+				if(n!=0) {
+					rtp = int2str(n, NULL);
+					_dlg_ctx.to_route = route_lookup(&main_rt, rtp);
+					strcpy(_dlg_ctx.to_route_name, rtp);
+				} else _dlg_ctx.to_route = 0;
+			}
+			if(_dlg_ctx.to_route <0) _dlg_ctx.to_route = 0;
+		break;
+		default:
+			_dlg_ctx.on = n;
+		break;
+	}
+	return 0;
+}
+
+int pv_parse_dlg_ctx_name(pv_spec_p sp, str *in)
+{
+	if(sp==NULL || in==NULL || in->len<=0)
+		return -1;
+
+	switch(in->len)
+	{
+		case 2: 
+			if(strncmp(in->s, "on", 2)==0)
+				sp->pvp.pvn.u.isname.name.n = 0;
+			else goto error;
+		break;
+		case 3: 
+			if(strncmp(in->s, "set", 3)==0)
+				sp->pvp.pvn.u.isname.name.n = 5;
+			else if(strncmp(in->s, "dir", 3)==0)
+				sp->pvp.pvn.u.isname.name.n = 6;
+			else goto error;
+		break;
+		case 5: 
+			if(strncmp(in->s, "flags", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 1;
+			else goto error;
+		break;
+		case 7: 
+			if(strncmp(in->s, "timeout", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 2;
+			else goto error;
+		break;
+		case 11: 
+			if(strncmp(in->s, "timeout_bye", 11)==0)
+				sp->pvp.pvn.u.isname.name.n = 3;
+			else goto error;
+		break;
+		case 13:
+			if(strncmp(in->s, "timeout_route", 13)==0)
+				sp->pvp.pvn.u.isname.name.n = 4;
+			else goto error;
+		break;
+		default:
+			goto error;
+	}
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = 0;
+
+	return 0;
+
+error:
+	LM_ERR("unknown PV name %.*s\n", in->len, in->s);
+	return -1;
+}
+
+int pv_get_dlg(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res)
+{
+	if(param==NULL)
+		return -1;
+	if(_dlg_ctx.dlg == NULL)
+		return pv_get_null(msg, param, res);
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 1:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->h_id);
+		case 2:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->state);
+/*
+		case 3:
+			if(_dlg_ctx.dlg->route_set[DLG_CALLEE_LEG].s==NULL
+					|| _dlg_ctx.dlg->route_set[DLG_CALLEE_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->route_set[DLG_CALLEE_LEG]);
+		case 4:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->dflags);
+		case 5:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->sflags);
+		case 6:
+			if(_dlg_ctx.dlg->callid.s==NULL
+					|| _dlg_ctx.dlg->callid.len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->callid);
+		case 7:
+			if(_dlg_ctx.dlg->to_uri.s==NULL
+					|| _dlg_ctx.dlg->to_uri.len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->to_uri);
+		case 8:
+			if(_dlg_ctx.dlg->tag[DLG_CALLEE_LEG].s==NULL
+					|| _dlg_ctx.dlg->tag[DLG_CALLEE_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->tag[DLG_CALLEE_LEG]);
+		case 9:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->toroute);
+		case 10:
+			if(_dlg_ctx.dlg->cseq[DLG_CALLEE_LEG].s==NULL
+					|| _dlg_ctx.dlg->cseq[DLG_CALLEE_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->cseq[DLG_CALLEE_LEG]);
+		case 11:
+			if(_dlg_ctx.dlg->route_set[DLG_CALLER_LEG].s==NULL
+					|| _dlg_ctx.dlg->route_set[DLG_CALLER_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->route_set[DLG_CALLER_LEG]);
+		case 12:
+			if(_dlg_ctx.dlg->from_uri.s==NULL
+					|| _dlg_ctx.dlg->from_uri.len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->from_uri);
+		case 13:
+			if(_dlg_ctx.dlg->tag[DLG_CALLER_LEG].s==NULL
+					|| _dlg_ctx.dlg->tag[DLG_CALLER_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->tag[DLG_CALLER_LEG]);
+		case 14:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->lifetime);
+		case 15:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->start_ts);
+		case 16:
+			if(_dlg_ctx.dlg->cseq[DLG_CALLER_LEG].s==NULL
+					|| _dlg_ctx.dlg->cseq[DLG_CALLER_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->cseq[DLG_CALLER_LEG]);
+		case 17:
+			if(_dlg_ctx.dlg->contact[DLG_CALLEE_LEG].s==NULL
+					|| _dlg_ctx.dlg->contact[DLG_CALLEE_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->contact[DLG_CALLEE_LEG]);
+		case 18:
+			if(_dlg_ctx.dlg->bind_addr[DLG_CALLEE_LEG]==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->bind_addr[DLG_CALLEE_LEG]->sock_str);
+		case 19:
+			if(_dlg_ctx.dlg->contact[DLG_CALLER_LEG].s==NULL
+					|| _dlg_ctx.dlg->contact[DLG_CALLER_LEG].len<=0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->contact[DLG_CALLER_LEG]);
+		case 20:
+			if(_dlg_ctx.dlg->bind_addr[DLG_CALLER_LEG]==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res,
+					&_dlg_ctx.dlg->bind_addr[DLG_CALLER_LEG]->sock_str);
+		case 21:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->h_entry);
+*/
+		default:
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)_dlg_ctx.dlg->ref);
+	}
+	return 0;
+}
+
+int pv_parse_dlg_name(pv_spec_p sp, str *in)
+{
+	if(sp==NULL || in==NULL || in->len<=0)
+		return -1;
+
+	switch(in->len)
+	{
+		case 3: 
+			if(strncmp(in->s, "ref", 3)==0)
+				sp->pvp.pvn.u.isname.name.n = 0;
+			else goto error;
+		break;
+		case 4: 
+			if(strncmp(in->s, "h_id", 4)==0)
+				sp->pvp.pvn.u.isname.name.n = 1;
+			else goto error;
+		break;
+		case 5: 
+			if(strncmp(in->s, "state", 5)==0)
+				sp->pvp.pvn.u.isname.name.n = 2;
+			else if(strncmp(in->s, "to_rs", 5)==0)
+				sp->pvp.pvn.u.isname.name.n = 3;
+			else goto error;
+		break;
+		case 6: 
+			if(strncmp(in->s, "dflags", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 4;
+			else if(strncmp(in->s, "sflags", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 5;
+			else if(strncmp(in->s, "callid", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 6;
+			else if(strncmp(in->s, "to_uri", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 7;
+			else if(strncmp(in->s, "to_tag", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 8;
+			else goto error;
+		break;
+		case 7: 
+			if(strncmp(in->s, "toroute", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 9;
+			else if(strncmp(in->s, "to_cseq", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 10;
+			else if(strncmp(in->s, "from_rs", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 11;
+			else if(strncmp(in->s, "h_entry", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 21;
+			else goto error;
+		break;
+		case 8: 
+			if(strncmp(in->s, "from_uri", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 12;
+			else if(strncmp(in->s, "from_tag", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 13;
+			else if(strncmp(in->s, "lifetime", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 14;
+			else if(strncmp(in->s, "start_ts", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 15;
+			else goto error;
+		break;
+		case 9: 
+			if(strncmp(in->s, "from_cseq", 9)==0)
+				sp->pvp.pvn.u.isname.name.n = 16;
+			else goto error;
+		break;
+		case 10: 
+			if(strncmp(in->s, "to_contact", 10)==0)
+				sp->pvp.pvn.u.isname.name.n = 17;
+			else goto error;
+		break;
+		case 11: 
+			if(strncmp(in->s, "to_bindaddr", 11)==0)
+				sp->pvp.pvn.u.isname.name.n = 18;
+			else goto error;
+		break;
+		case 12: 
+			if(strncmp(in->s, "from_contact", 12)==0)
+				sp->pvp.pvn.u.isname.name.n = 19;
+			else goto error;
+		break;
+		case 13: 
+			if(strncmp(in->s, "from_bindaddr", 20)==0)
+				sp->pvp.pvn.u.isname.name.n = 2;
+			else goto error;
+		break;
+		default:
+			goto error;
+	}
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = 0;
+
+	return 0;
+
+error:
+	LM_ERR("unknown PV name %.*s\n", in->len, in->s);
+	return -1;
+}
+
+void dlg_set_ctx_dialog(struct dlg_cell *dlg)
+{
+	_dlg_ctx.dlg = dlg;
+}
+
+struct dlg_cell* dlg_get_ctx_dialog(void)
+{
+	return _dlg_ctx.dlg;
+}
+
+dlg_ctx_t* dlg_get_dlg_ctx(void)
+{
+	return &_dlg_ctx;
+}
+
+int spiral_detect_reset(struct sip_msg *foo, unsigned int flags, void *bar)
+{
+	spiral_detected = -1;
+
+	return 0;
+}

+ 91 - 0
modules/dialog2/dlg_var.h

@@ -0,0 +1,91 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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
+ */
+		       
+#ifndef _DLG_VAR_H_
+#define _DLG_VAR_H_
+
+#include "../../pvar.h"
+#include "dlg_hash.h"
+
+#define DLG_TOROUTE_SIZE	32
+/*! dialog context */
+typedef struct _dlg_ctx {
+	int on;
+	unsigned int flags;
+	int to_route;
+	char to_route_name[DLG_TOROUTE_SIZE];
+	int to_bye;
+	int timeout;
+	struct dlg_cell *dlg;
+	int set;
+	unsigned int dir;
+} dlg_ctx_t;
+
+/* A dialog-variable */
+struct dlg_var {
+	str key;
+	str value;
+	unsigned int vflags;		/*!< internal variable flags */
+	struct dlg_var *next;
+};
+
+str * api_get_dlg_variable(str *callid, str *ftag, str *ttag, str *key);
+str * get_dlg_variable(struct dlg_cell *dlg, str *key);
+
+int api_set_dlg_variable(str *callid, str *ftag, str *ttag, str *key, str *val);
+int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val);
+
+int pv_parse_dialog_var_name(pv_spec_p sp, str *in);
+
+int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
+
+int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
+
+/*! Retrieve the current var-list */
+struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointer);
+
+/* Adds, updates and deletes dialog variables */
+int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new);
+
+extern dlg_ctx_t _dlg_ctx;
+
+int pv_get_dlg_ctx(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+int pv_set_dlg_ctx(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val);
+int pv_parse_dlg_ctx_name(pv_spec_p sp, str *in);
+
+int pv_get_dlg(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+int pv_parse_dlg_name(pv_spec_p sp, str *in);
+
+int dlg_cfg_cb(struct sip_msg *foo, unsigned int flags, void *bar);
+
+void dlg_set_ctx_dialog(struct dlg_cell *dlg);
+struct dlg_cell* dlg_get_ctx_dialog(void);
+
+dlg_ctx_t* dlg_get_dlg_ctx(void);
+
+int spiral_detect_reset(struct sip_msg *foo, unsigned int flags, void *bar);
+
+#endif

+ 4 - 0
modules/dialog2/doc/Makefile

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

+ 101 - 0
modules/dialog2/doc/dialog2.xml

@@ -0,0 +1,101 @@
+<?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>dialog2 Module</title>
+        <productname class="trade">&kamailioname;</productname>
+        <authorgroup>
+            <author>
+                <firstname>Bogdan-Andrei</firstname>
+                <surname>Iancu</surname>
+                <affiliation>
+                    <orgname>Voice Sistem SRL</orgname>
+                </affiliation>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </author>
+            <author>
+                <firstname>Carsten</firstname>
+                <surname>Bock</surname>
+                <affiliation>
+                    <orgname>ng-voice.com</orgname>
+                </affiliation>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </author>
+            <author>
+                <firstname>Jason</firstname>
+                <surname>Penton</surname>
+                <affiliation>
+                    <orgname>Smile Communications</orgname>
+                </affiliation>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </author>
+            <author>
+                <firstname>Richard</firstname>
+                <surname>Good</surname>
+                <affiliation>
+                    <orgname>Smile Communications</orgname>
+                </affiliation>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </author>
+            <editor>
+                <firstname>Bogdan-Andrei</firstname>
+                <surname>Iancu</surname>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </editor>
+            <editor>
+                <firstname>Carsten</firstname>
+                <surname>Bock</surname>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </editor>
+            <editor>
+                <firstname>Jason</firstname>
+                <surname>Penton</surname>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </editor>
+            <editor>
+                <firstname>Richard</firstname>
+                <surname>Good</surname>
+                <address>
+                    <email>[email protected]</email>
+                </address>
+            </editor>
+                
+        </authorgroup>
+        <copyright>
+            <year>2006</year>
+            <holder>Voice Sistem SRL</holder>
+        </copyright>
+        <copyright>
+            <year>2011</year>
+            <holder>Carsten Bock, http://www.ng-voice.com</holder>
+        </copyright>
+    </bookinfo>
+    <toc></toc>
+	
+    <xi:include href="dialog2_admin.xml"/>
+    <xi:include href="dialog2_devel.xml"/>
+    <xi:include href="dialog2_faq.xml"/>
+	
+</book>

+ 1211 - 0
modules/dialog2/doc/dialog2_admin.xml

@@ -0,0 +1,1211 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+    <title>&adminguide;</title>
+	
+    <section>
+        <title>Overview</title>
+        <para>
+	The dialog2 module provides dialog awareness to the &kamailio; proxy. Its
+	functionality is to keep track of the current dialogs, to offer information
+	about them (like how many dialogs are active) or to manage them. The module
+	exports several functions that could be used directly from scripts.
+        The dialog2 module extends the original dialog module by providing support
+        for forked calling and early dialog termination.  It is the intention that
+        the dialog2 module will eventually replace the dialog module.
+        </para>
+        <para>
+	The module, via an internal API, also provide the foundation to build on
+	top of it more complex dialog-based functionalities via other &kamailio;
+	modules.
+        </para>
+    </section>
+
+    <section>
+        <title>How it works</title>
+        <para>
+	To create the dialog associated to an initial request, the flag
+            <quote>dlg_flag</quote> (
+            <xref linkend="dlg-flag-id"/>) must be set before
+	creating the corresponding transaction.
+        </para>
+        <para>
+	The dialog is automatically destroyed when a 
+            <quote>BYE</quote> is
+	received. In case of no 
+            <quote>BYE</quote>, the dialog lifetime is
+	controlled via the default timeout (see 
+            <quote>default_timeout</quote>
+	- 
+            <xref linkend="default-timeout-id"/>) and custom timeout (see
+            <quote>timeout_avp</quote> - 
+            <xref linkend="timeout-avp-id"/>). The
+	dialog timeout is reset each time a sequential request passes.
+       
+        
+        </para>
+    </section>
+
+    <section>
+        <title>Dialog profiling</title>
+        <para>
+	Dialog profiling is a mechanism that helps in classifying, sorting and
+	keeping trace of certain types of dialogs, using whatever properties of
+	the dialog (like caller, destination, type of calls, etc).
+	Dialogs can be dynamically added in different (and several) profile
+	tables - logically, each profile table can have a special meaning (like
+	dialogs outside the domain, dialogs terminated to PSTN, etc).
+        </para>
+        <para>
+	There are two types of profiles:
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>with no value</emphasis> - a dialog simply belongs
+			to a profile. (like outbound calls profile). There is no other
+			additional information to describe the dialog's belonging to the
+			profile;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>with value</emphasis> - a dialog belongs to a profile
+			having a certain value (like in caller profile, where the value
+			is the caller ID). The belonging of the dialog to the profile is
+			strictly related to the value.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </para>
+        <para>
+	A dialog can be added to multiple profiles in the same time.
+        </para>
+        <para>
+	Profiles are visible (at the moment) in the request route (for initial
+	and sequential requests) and in the branch, failure and reply routes of
+	the original request.
+        </para>
+    </section>
+
+    <section>
+        <title>Dependencies</title>
+        <section>
+            <title>&kamailio; Modules</title>
+            <para>
+		The following modules must be loaded before this module:
+                <itemizedlist>
+                    <listitem>
+                        <para>
+                            <emphasis>TM</emphasis> - Transaction module
+                        </para>
+                    </listitem>
+                    <listitem>
+                        <para>
+                            <emphasis>RR</emphasis> - Record-Route module
+                        </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>enable_stats</varname> (integer)
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>hash_size</varname> (integer)
+            </title>
+            <para>
+		The size of the hash table internally used to keep the dialogs. A
+		larger table is much faster but consumes more memory. The hash size
+		must be a power of two number.
+            </para>
+            <para>
+		IMPORTANT: If dialogs' information should be stored in a database,
+		a constant hash_size should be used, otherwise the restoring process
+		will not take place. If you really want to modify the hash_size you
+		must delete all table's rows before restarting the server.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>4096</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>hash_size</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "hash_size", 1024)
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>rr_param</varname> (string)
+            </title>
+            <para>
+		Name of the Record-Route parameter to be added with the dialog cookie.
+		It is used for the fast dialog matching of sequential requests.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>did</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>rr_param</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "rr_param", "xyz")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="dlg-flag-id">
+            <title>
+                <varname>dlg_flag</varname> (integer)
+            </title>
+            <para>
+		Flag to be used for marking if a dialog should be constructed for the
+		current request (this make sense only for initial requests).
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>none</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>dlg_flag</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "dlg_flag", 4)
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="timeout-avp-id">
+            <title>
+                <varname>timeout_avp</varname> (string)
+            </title>
+            <para>
+		The specification of an AVP that contain a custom timeout (in seconds)
+		for the dialog. It may be used only in a request (initial or sequential)
+		context
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>none</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>timeout_avp</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "timeout_avp", "$avp(i:10)")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="default-timeout-id">
+            <title>
+                <varname>default_timeout</varname> (integer)
+            </title>
+            <para>
+		The default dialog timeout (in seconds) if no custom one is set.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>43200 (12 hours)</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>default_timeout</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "default_timeout", 21600)
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_extra_hdrs</varname> (string)
+            </title>
+            <para>
+		A string containing the extra headers (full format, with EOH)
+		to be added in the requests generated by the module (like BYEs).
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>NULL</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>dlf_extra_hdrs</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "dlg_extra_hdrs", "Hint: credit expired\r\n")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_match_mode</varname> (integer)
+            </title>
+            <para>
+		Deprecated - in the new dialog module we always match using DID ONLY
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>detect_spirals</varname> (integer)
+            </title>
+            <para>
+			Whether spirals (i.e., messages routed through the proxy multiple times)
+			should be detected or not.
+            </para>
+            <para>
+			If set to 0, spirals will not be detected and result in the generation of a
+			new, possibly dangling dialog structure per occurring spiral. If set to 1,
+			spirals are detected and internally mapped to existing dialog structures.
+            </para>
+            <para>
+			Default value is 1.
+            </para>
+            <example>
+                <title>Set 
+                    <varname>detect_spirals</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog2", "detect_spirals", 1)
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>db_url</varname> (string)
+            </title>
+            <para>
+		Db storage not yet supported by dialog2 - this to be done in future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>db_mode</varname> (integer)
+            </title>
+            <para>
+		Db storage not yet supported by dialog2 - this to be done in future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>db_update_period</varname> (integer)
+            </title>
+            <para>
+		Db storage not yet supported by dialog2 - this to be done in future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>db_fetch_rows</varname> (integer)
+            </title>
+            <para>
+		Db storage not yet supported by dialog2 - this to be done in future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>table_name</varname> (string)
+            </title>
+            <para>
+		Db storage not yet supported by dialog2 - this to be done in future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>profiles_with_value</varname> (string)
+            </title>
+            <para>
+			List of names for profiles with values.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>empty</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>profiles_with_value</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog", "profiles_with_value", "caller ; my_profile")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>profiles_no_value</varname> (string)
+            </title>
+            <para>
+			List of names for profiles without values.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>empty</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>profiles_no_value</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog", "profiles_no_value", "inbound ; outbound")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>bridge_controller</varname> (string)
+            </title>
+            <para>
+			SIP address to be used in From header when initiating a call bridge.
+            </para>
+            <para>
+                <emphasis>
+			Default value is 
+                    <quote>sip:[email protected]</quote>.
+                </emphasis>
+            </para>
+            <example>
+                <title>Set 
+                    <varname>bridge_controller</varname> parameter
+                </title>
+                <programlisting format="linespecific">
+...
+modparam("dialog", "bridge_controller", "sip:[email protected]")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <varname>initial_cbs_inscript</varname> (string)
+            </title>
+            <para>
+            This has been deprecated since dlg_manage has been removed.
+            </para>
+        </section>
+
+    </section>
+
+
+    <section>
+        <title>Functions</title>
+        <section>
+            <title>
+                <function moreinfo="none">set_dlg_profile(profile,[value])</function>
+            </title>
+            <para>
+		Inserts the current dialog into a profile. Note that if the profile does
+		not supports values, this will be silently discarded. Also, there is
+		no check for inserting the same dialog in the same profile for multiple
+		times.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>profile</emphasis> - name of the profile to be
+			added to;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>value</emphasis> (optional) - string value to
+			define the belonging of the dialog to the profile - note that the
+			profile must support values.
+			Pseudo-variables are supported.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from REQUEST_ROUTE, BRANCH_ROUTE,
+			REPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>set_dlg_profile</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+set_dlg_profile("inbound_call");
+set_dlg_profile("caller","$fu");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">unset_dlg_profile(profile,[value])</function>
+            </title>
+            <para>
+		Removes the current dialog from a profile.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>profile</emphasis> - name of the profile to be
+			removed from;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>value</emphasis> (optional) - string value to
+			define the belonging of the dialog to the profile - note that the
+			profile must support values.
+			Pseudo-variables are supported.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from BRANCH_ROUTE,
+			REPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>unset_dlg_profile</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+unset_dlg_profile("inbound_call");
+unset_dlg_profile("caller","$fu");
+...
+                </programlisting>
+            </example>
+        </section>
+
+
+        <section>
+            <title>
+                <function moreinfo="none">is_in_profile(profile,[value])</function>
+            </title>
+            <para>
+		Checks if the current dialog belongs to a profile. If the profile
+		supports values, the check can be reinforced to take into account a
+		specific value - if the dialog was inserted into the profile for a
+		specific value. If no value is passed, only the simply belonging of
+		the  dialog to the profile is checked. Note that if the profile does
+		not supports values, this will be silently discarded.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>profile</emphasis> - name of the profile to be
+			checked against;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>value</emphasis> (optional) - string value to
+			further restrict the check. Pseudo-variables are supported.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from REQUEST_ROUTE, BRANCH_ROUTE,
+			REPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>is_in_profile</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+if (is_in_profile("inbound_call")) {
+	log("this request belongs to a inbound call\n");
+}
+...
+if (is_in_profile("caller","XX")) {
+	log("this request belongs to a call of user XX\n");
+}
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">get_profile_size(profile,[value],size)</function>
+            </title>
+            <para>
+		Returns the number of dialogs belonging to a profile. If the profile
+		supports values, the check can be reinforced to take into account a
+		specific value - how many dialogs were inserted into the profile with
+		a specific value. If no value is passed, only simply belonging of the
+		dialog to the profile is checked. Note that if the profile does not
+		supports values, this will be silently discarded.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>profile</emphasis> - name of the profile to get
+			the size for;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>value</emphasis> (optional) - string value to
+			further restrict the check. Pseudo-variables are supported;
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>size</emphasis> - an AVP or script variable to
+			return the profile size in.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from REQUEST_ROUTE, BRANCH_ROUTE,
+			REPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>get_profile_size</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+if(get_profile_size("inbound_call","$avp(size)"))
+    xlog("currently there are $avp(size) inbound calls\n");
+...
+if(get_profile_size("caller","$fu","$avp(size)"))
+    xlog("currently, the user $fu has $avp(size) active outgoing calls\n");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_isflagset(flag)</function>
+            </title>
+            <para>
+		Check if the dialog flag is set or not.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>flag</emphasis> - index of the flag - can be
+				pseudo-variable.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from BRANCH_ROUTE,
+			REQUEST_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>dlg_isflagset</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+if(dlg_isflagset("1"))
+{
+    ...
+}
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_setflag(flag)</function>
+            </title>
+            <para>
+		Set the dialog flag.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>flag</emphasis> - index of the flag - can be
+				pseudo-variable.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from BRANCH_ROUTE,
+			REQUEST_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>dlg_setflag</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+dlg_setflag("1");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_resetflag(flag)</function>
+            </title>
+            <para>
+		Reset the dialog flag.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>flag</emphasis> - index of the flag - can be
+				pseudo-variable.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from BRANCH_ROUTE,
+			REQUEST_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>dlg_resetflag</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+redlg_setflag("1");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_terminate</function>
+            </title>
+            <para>
+		Terminates a dialog.  In dialog2 module this function now
+                includes support for early as well as confirmed dialogs.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>side</emphasis> - which side to terminate. 
+                            It can be: caller, callee or both of them.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>reason</emphasis> - reason for termination.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		This function can be used from BRANCH_ROUTE,
+			REQUEST_ROUTE, ONREPLY_ROUTE and FAILURE_ROUTE.
+            </para>
+            <example>
+                <title>
+                    <function>dlg_terminate</function> usage
+                </title>
+                <programlisting format="linespecific">
+...
+dlg_terminate("all", "Insufficient QoS");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_refer(side, address)</function>
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_manage()</function>
+            </title>
+            <para>
+		This has been deprecated in dialog2. Instead set dialog flag for 
+                initial INVITE and Route-parameter-callback execution for 
+                within-dialog requests.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_bridge(from, to, op)</function>
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">dlg_get(callid, ftag, ttag)</function>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+	
+        <section>
+            <title>
+                <function moreinfo="none">is_known_dlg()</function>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+    </section>
+
+
+    <section>
+        <title>Exported statistics</title>
+        <section>
+            <title>
+                <varname>active_dialogs</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+        <section>
+            <title>
+                <varname>early_dialogs</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>		
+        <section>
+            <title>
+                <varname>processed_dialogs</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+        <section>
+            <title>
+                <varname>expired_dialogs</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+        <section>
+            <title>
+                <varname>failed_dialogs</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+    </section>
+
+
+    <section>
+        <title>MI Commands</title>
+
+        <section>
+            <title>
+                <varname>dlg_list</varname>
+            </title>
+            <para>
+		Lists the description of a dialog or of all dialogs (calls). If only
+		one dialogs is to be listed, the dialog identifiers are to be passed
+		as parameter (callid and fromtag).  In dialog2 module this also now 
+                also lists all dlg_out entries for early dialogs.
+                
+            </para>
+            <para>
+		Name: 
+                <emphasis>dlg_list</emphasis>
+            </para>
+            <para>Parameters:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>callid</emphasis> (optional) - callid if a single
+				dialog to be listed.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>from_tag</emphasis> (optional, but cannot be present
+				without the callid parameter) - from tag (as per initial request)
+				of the dialog to be listed.  Note that if the from_tag is not
+				specified, only dialogs created by a request without a from tag
+				are matched, which will only occur with broken clients and is
+				thus a very rare situation.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+		MI FIFO Command Format:
+            </para>
+            <programlisting  format="linespecific">
+		:dlg_list:_reply_fifo_file_
+		_empty_line_
+            </programlisting>
+            <programlisting  format="linespecific">
+		:dlg_list:_reply_fifo_file_
+		[email protected]
+		AAdfeEFF33
+            </programlisting>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_list_ctx</varname>
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_end_dlg</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_terminate_dlg</varname>
+            </title>
+            <para>
+		Terminates a singe dialog, identified by the call_id, ftag, ttag. In dialog2 module this
+                dialog can be terminated in the early or confirmed states.
+            </para>
+            <para>
+		Name: 
+                <emphasis>dlg_terminate_dlg</emphasis>
+            </para>
+            <para>Parameters:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>callid</emphasis> - callid of the dialog to be terminated.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>ftag</emphasis> fromtag of dialog to be terminated.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>ttag</emphasis> totag of dialog to be terminated.
+                    </para>
+                </listitem>
+            </itemizedlist>
+            <para>
+                <emphasis>Note: Works for confirmed and early dialogs.</emphasis>
+            </para>
+            <para>
+		MI FIFO Command Format:
+            </para>
+            <programlisting  format="linespecific">
+		:dlg_terminate_dlg:_reply_fifo_file_
+		[email protected]
+		AAdfeEFF33 ftag-1234 t-tag1234
+            </programlisting>
+        </section>
+
+        <section>
+            <title>
+                <varname>profile_get_size</varname>
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>profile_list_dlgs</varname>
+            </title>
+            <para>
+		This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg_bridge</varname>
+            </title>
+            <para>
+			This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+    </section>
+
+
+    <section>
+        <title>Exported RPC Functions</title>
+        <section>
+            <title>
+                <varname>dlg.list</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.list_ctx</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.dlg_list</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.dlg_list_ctx</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.end_dlg</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.profile_get_size</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.profile_list</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>dlg.bridge_dlg</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+    </section>
+
+
+
+    <section>
+        <title>Exported pseudo-variables</title>
+
+        <section>
+            <title>
+                <varname>$DLG_count</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>$DLG_status</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>$DLG_lifetime</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+        <section>
+            <title>
+                <varname>$dlg(...)</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+        <section>
+            <title>
+                <varname>$dlg_ctx(...)</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+        <section>
+            <title>
+                <varname>$dlg_var(key)</varname>
+            </title>
+            <para>
+                    This function is currently not supported by the dialog2 module.
+                    To be incorporated in the future.
+            </para>
+        </section>
+
+    </section>
+
+
+
+</chapter>

+ 268 - 0
modules/dialog2/doc/dialog2_devel.xml

@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module Developer's Guide -->
+
+<chapter>
+	
+    <title>&develguide;</title>
+    <section>
+        <title>Available Functions</title>
+
+        <section>
+            <title>
+                <function moreinfo="none">register_dlgcb (dialog, type, cb, param, free_param_cb)</function>
+            </title>
+            <para>
+		Register a new callback to the dialog.
+            </para>
+            <para>Meaning of the parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>struct dlg_cell* dlg</emphasis> - dialog to 
+			register callback to. If maybe NULL only for DLGCB_CREATED callback
+			type, which is not a per dialog type.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>int type</emphasis> - types of callbacks; more
+			types may be register for the same callback function; only 
+			DLGCB_CREATED must be register alone. Possible types:
+                        <itemizedlist>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_LOADED</emphasis>
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_CREATED</emphasis> - called when a new 
+				dialog is created - it's a global type (not associated to 
+				any dialog)
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_FAILED</emphasis> - called when the dialog
+				was negatively replied (non-2xx) - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_CONFIRMED_NA</emphasis> - called when the
+				dialog is confirmed (2xx replied) but the setup-concluding ACK
+				message from the caller is yet pending - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_CONFIRMED</emphasis> - called when the
+				dialog is confirmed (2xx replied) and the setup-concluding ACK
+				message from the caller has been seen - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_REQ_WITHIN</emphasis> - called when the 
+				dialog matches a sequential request (excluding setup-concluding
+				ACK messages which are handled in DLGCB_CONFIRMED) - it's a per
+				dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_TERMINATED</emphasis> - called when the 
+				dialog is terminated via BYE - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_TERMINATED_CONFIRMED</emphasis> -
+				called when response to a BYE request is received - it's a
+				per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_EXPIRED</emphasis> - called when the 
+				dialog expires without receiving a BYE - it's a per dialog 
+				type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_EARLY</emphasis> - called when the
+				dialog is created in an early state (18x replied) - it's
+				a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_RESPONSE_FWDED</emphasis> - called when
+				the dialog matches a reply to the initial INVITE request - it's
+				a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_RESPONSE_WITHIN</emphasis> - called when
+				the dialog matches a reply to a subsequent in dialog request
+				- it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_MI_CONTEXT</emphasis> - called when the
+				mi dlg_list_ctx command is invoked - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_SPIRALED</emphasis> - called when the
+				dialog matches a spiraling request - it's a per dialog type.
+                                </para>
+                            </listitem>
+                            <listitem>
+                                <para>
+                                    <emphasis>DLGCB_DESTROY</emphasis>
+                                </para>
+                            </listitem>
+                        </itemizedlist>
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>dialog_cb cb</emphasis> - callback function to be 
+			called. Prototype is: 
+                        <quote>void (dialog_cb) 
+			(struct dlg_cell* dlg, int type, struct dlg_cb_params * params);
+                        </quote>
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>void *param</emphasis> - parameter to be passed to
+			the callback function.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>param_free callback_param_free</emphasis> - 
+			callback function to be called to free the param.
+			Prototype is: 
+                        <quote>void (param_free_cb) (void *param);</quote>
+                    </para>
+                </listitem>
+
+            </itemizedlist>
+        </section>
+
+        <section>
+            <title>
+                <function moreinfo="none">terminate_dlg (str callid, str ftag, str ttag, hdrs)</function>
+            </title>
+            <para>
+		Terminate a Dialog identified by callid, ftag and ttag in early or confirmed state.
+            </para>
+            <para>Meaning of parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>str* callid</emphasis> - callid of dialog to 
+			terminate.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* ftag</emphasis> - from_tag of dialog to 
+			terminate.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* ttag</emphasis> - to tag of dialog to 
+			terminate.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* hdrs</emphasis> - string containg extra headers (full format) 
+			to be added to the BYE requests of the dialog.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </section>
+        
+        <section>
+            <title>
+                <function moreinfo="none">set_dlg_var (dlg, key, val)</function>
+            </title>
+            <para>
+		Add a variable to the dialog structure
+            </para>
+            <para>Meaning of parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>struct dlg_cell* dlg</emphasis> - dialog to 
+			add to.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* key</emphasis> - Name of the variable.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* val</emphasis> - Value of the variable.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </section>
+        
+        <section>
+            <title>
+                <function moreinfo="none">get_dlg_var (dlg, key)</function>
+            </title>
+            <para>
+		Retrieves a variable attached to the dialog structure
+            </para>
+            <para>Meaning of parameters is as follows:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis>struct dlg_cell* dlg</emphasis> - dialog to 
+			get the variable from.
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>
+                        <emphasis>str* key</emphasis> - Name of the variable.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </section>
+        
+        <section>
+            <title>
+                <function moreinfo="none">get_current_dialog ()</function>
+            </title>
+            <para>
+		Get the current dialog for a message, if exists
+            </para>
+        </section>
+        
+
+    </section>
+
+</chapter>
+

+ 95 - 0
modules/dialog2/doc/dialog2_faq.xml

@@ -0,0 +1,95 @@
+<?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 FAQ -->
+
+<chapter>
+	
+	<title>&faqguide;</title>
+	<qandaset defaultlabel="number">
+	<qandaentry>
+		<question>
+		<para>What happend with <quote>use_tight_match</quote> 
+		parameter?</para>
+		</question>
+		<answer>
+		<para>
+			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).
+		</para>
+		</answer>
+	</qandaentry>
+        <qandaentry>
+		<question>
+		<para>Why is there a dialog2 module and a dialog module?</para>
+		</question>
+		<answer>
+		<para>
+			The dialog2 module addresses shortcomings in the intial dialog
+                        module design.  It makes some large changes to the API and 
+                        therefore must be introduced slowly.  It is currently in the
+                        early development stages.  Eventually the dialog2 module should
+                        replace the dialog module.
+		</para>
+		</answer>
+	</qandaentry>
+        <qandaentry>
+		<question>
+		<para>Where can I find more about &kamailio;?</para>
+		</question>
+		<answer>
+		<para>
+			Take a look at &kamailiohomelink;.
+		</para>
+		</answer>
+	</qandaentry>
+	<qandaentry>
+		<question>
+		<para>Where can I post a question about this module?</para>
+		</question>
+		<answer>
+		<para>
+			First at all check if your question was already answered on one of
+			our mailing lists: 
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>User Mailing List - &kamailiouserslink;</para>
+			</listitem>
+			<listitem>
+			<para>Developer Mailing List - &kamailiodevlink;</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+			E-mails regarding any stable &kamailio; release should be sent to 
+			&kamailiousersmail; and e-mails regarding development versions
+			should be sent to &kamailiodevmail;.
+		</para>
+		<para>
+			If you want to keep the mail private, send it to 
+			&kamailiohelpmail;.
+		</para>
+		</answer>
+	</qandaentry>
+	<qandaentry>
+		<question>
+		<para>How can I report a bug?</para>
+		</question>
+		<answer>
+		<para>
+			Please follow the guidelines provided at:
+			&kamailiobugslink;.
+		</para>
+		</answer>
+	</qandaentry>
+	</qandaset>
+</chapter>
+