src/modules/dialog/dlg_handlers.c
5b3d7218
 /*
  * Copyright (C) 2006 Voice System SRL
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
5b3d7218
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
5b3d7218
  * 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
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
5b3d7218
  * 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.
  *
67f04f26
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
5b3d7218
  *
  */
 
 
f7aa0a89
 /*!
  * \file
  * \brief Functions related to dialog handling
  * \ingroup dialog
  * Module: \ref dialog
  */
 
59c151b6
 #include <string.h>
 #include <time.h>
5b3d7218
 
cf83221d
 #include "../../core/trim.h"
 #include "../../core/pvar.h"
 #include "../../core/timer.h"
 #include "../../core/counters.h"
 #include "../../core/action.h"
 #include "../../core/script_cb.h"
 #include "../../core/pt.h"
5b7838c9
 #include "../../core/kemi.h"
cf83221d
 #include "../../core/fmsg.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_cseq.h"
 #include "../../core/parser/contact/parse_contact.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_rr.h"
eb38b3df
 #include "../../modules/tm/tm_load.h"
5b3d7218
 #include "../rr/api.h"
 #include "dlg_hash.h"
 #include "dlg_timer.h"
 #include "dlg_cb.h"
e349af55
 #include "dlg_cseq.h"
5b3d7218
 #include "dlg_handlers.h"
29d70f7d
 #include "dlg_req_within.h"
a70300b1
 #include "dlg_db_handler.h"
4d41d724
 #include "dlg_profile.h"
29d70f7d
 #include "dlg_var.h"
9fe51ed4
 #include "dlg_dmq.h"
5b3d7218
 
f7aa0a89
 static str       rr_param;		/*!< record-route parameter for matching */
974abf6a
 static int       dlg_flag_mask=0;	/*!< flag for dialog tracking */
f7aa0a89
 static pv_spec_t *timeout_avp;		/*!< AVP for timeout setting */
 static int       default_timeout;	/*!< default dialog timeout */
7004969c
 static int       seq_match_mode;	/*!< dlg_match mode */
0b3562ac
 static int       keep_proxy_rr;		/*!< keep the proxy's record-route in both route-sets */
f7aa0a89
 static int       shutdown_done = 0;	/*!< 1 when destroy_dlg_handlers was called */
0b665153
 extern int       detect_spirals;
b5993240
 extern int       dlg_timeout_noreset;
48b8190c
 extern int       initial_cbs_inscript;
84d38ce7
 extern int       dlg_send_bye;
a46f2ee6
 extern int       dlg_event_rt[DLG_EVENTRT_MAX];
53ec53db
 extern int       dlg_wait_ack;
9fe51ed4
 extern int       dlg_enable_dmq;
14348216
 extern int       dlg_filter_mode;
48b8190c
 int              spiral_detected = -1;
5b3d7218
 
f7aa0a89
 extern struct rr_binds d_rrb;		/*!< binding to record-routing module */
5b3d7218
 
 
8c1b49d5
 extern pv_elem_t *ruri_param_model;	/*!< pv-string to get r-uri */
ee90e8e2
 
5b7838c9
 extern str dlg_event_callback;
 
f7aa0a89
 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 */
ee90e8e2
 
f7aa0a89
 
 /*! size of the dialog record-route parameter */
5b3d7218
 #define RR_DLG_PARAM_SIZE  (2*2*sizeof(int)+3+MAX_DLG_RR_PARAM_NAME)
f7aa0a89
 /*! separator inside the record-route paramter */
5b3d7218
 #define DLG_SEPARATOR      '.'
 
ad4cfe8a
 int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg,
 		int mode);
53ec53db
 int dlg_set_tm_waitack(tm_cell_t *t, dlg_cell_t *dlg);
5b3d7218
 
f7aa0a89
 /*!
  * \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
60d9c557
  * \param seq_match_mode_p matching mode
f7aa0a89
  */
5b3d7218
 void init_dlg_handlers(char *rr_param_p, int dlg_flag_p,
60d9c557
 		pv_spec_t *timeout_avp_p ,int default_timeout_p,
0b3562ac
 		int seq_match_mode_p, int keep_proxy_rr_p)
5b3d7218
 {
 	rr_param.s = rr_param_p;
 	rr_param.len = strlen(rr_param.s);
 
974abf6a
 	if(dlg_flag_p>=0) dlg_flag_mask = 1<<dlg_flag_p;
5b3d7218
 
 	timeout_avp = timeout_avp_p;
 	default_timeout = default_timeout_p;
60d9c557
 	seq_match_mode = seq_match_mode_p;
0b3562ac
 	keep_proxy_rr = keep_proxy_rr_p;
5b3d7218
 }
 
 
f7aa0a89
 /*!
  * \brief Shutdown operation of the module
  */
351fc577
 void destroy_dlg_handlers(void)
d6cc5af6
 {
 	shutdown_done = 1;
 }
 
5b3d7218
 
f7aa0a89
 /*!
  * \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
  */
5b3d7218
 static inline int add_dlg_rr_param(struct sip_msg *req, unsigned int entry,
f7aa0a89
 		unsigned int id)
5b3d7218
 {
 	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++) = '=';
 
 	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) {
b9bb99d2
 		LM_ERR("failed to add rr param\n");
5b3d7218
 		return -1;
 	}
 
 	return 0;
 }
 
 
f7aa0a89
 /*!
  * \brief Parse SIP message and populate leg informations
62c98ed1
  *
7004969c
  * Parse SIP message and populate leg informations.
f7aa0a89
  * \param dlg the dialog to add cseq, contact & record_route
  * \param msg sip message
3c5a8364
  * \param t transaction
  * \param leg type of the call leg
  * \param tag SIP To tag
f7aa0a89
  * \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
62c98ed1
  */
29d70f7d
 int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg,
abe76f96
 	struct cell* t, unsigned int leg, str *tag)
62c98ed1
 {
0b3562ac
 	unsigned int skip_recs, own_rr = 0;
62c98ed1
 	str cseq;
 	str contact;
 	str rr_set;
 
cc12bc17
 	if(parse_headers(msg,HDR_EOH_F,0)<0){
 		LM_ERR("failed to parse headers\n");
 		goto error0;
 	}
 
fd68980d
 	dlg->bind_addr[leg] = msg->rcv.bind_address;
 
62c98ed1
 	/* extract the cseq number as string */
 	if (leg==DLG_CALLER_LEG) {
2f11ad85
 		if((!msg->cseq && (parse_headers(msg,HDR_CSEQ_F,0)<0 || !msg->cseq))
 			|| !msg->cseq->parsed){
46401e46
 			LM_ERR("bad sip message or missing CSeq hdr\n");
62c98ed1
 			goto error0;
 		}
 		cseq = (get_cseq(msg))->number;
 	} else {
 		/* use the same as in request */
ed6dbb0c
 		cseq = dlg->cseq[DLG_CALLEE_LEG];
62c98ed1
 	}
740bc8ce
 	if ((leg==DLG_CALLER_LEG) && (cseq.s==NULL || cseq.len<=0)) {
ae4087ef
 		LM_DBG("empty CSeq number (leg: %d)\n", leg);
46401e46
 	}
62c98ed1
 
 	/* extract the contact address */
 	if (!msg->contact&&(parse_headers(msg,HDR_CONTACT_F,0)<0||!msg->contact)){
b9bb99d2
 		LM_ERR("bad sip message or missing Contact hdr\n");
62c98ed1
 		goto error0;
 	}
 	if ( parse_contact(msg->contact)<0 ||
cc12bc17
 			((contact_body_t *)msg->contact->parsed)->contacts==NULL ||
 			((contact_body_t *)msg->contact->parsed)->contacts->next!=NULL ) {
b9bb99d2
 		LM_ERR("bad Contact HDR\n");
62c98ed1
 		goto error0;
 	}
 	contact = ((contact_body_t *)msg->contact->parsed)->contacts->uri;
46401e46
 	if(contact.s==NULL || contact.len<=0) {
ae4087ef
 		LM_DBG("empty contact uri (leg: %d)\n", leg);
46401e46
 	}
62c98ed1
 
cc12bc17
 	/* extract the record-route addresses */
abe76f96
 	if (leg==DLG_CALLER_LEG) {
 		skip_recs = 0;
 	} else {
 		/* was the 200 OK received or local generated */
0b3562ac
 		own_rr = ((t->relayed_reply_branch>=0)?
f6a66b42
 				((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);
0b3562ac
 		skip_recs = dlg->from_rr_nb + ((keep_proxy_rr & 1) > 0 ? 0 : own_rr);
abe76f96
 	}
62c98ed1
 
 	if(msg->record_route){
67f04f26
 		if( print_rr_body(msg->record_route, &rr_set, leg,
62c98ed1
 							&skip_recs) != 0 ){
b9bb99d2
 			LM_ERR("failed to print route records \n");
62c98ed1
 			goto error0;
 		}
 	} else {
 		rr_set.s = 0;
 		rr_set.len = 0;
 	}
 
abe76f96
 	if(leg==DLG_CALLER_LEG)
 		dlg->from_rr_nb = skip_recs;
62c98ed1
 
47bfa060
 	LM_DBG("leg(%d) route_set [%.*s], contact [%.*s], cseq [%.*s]"
 			" and bind_addr [%.*s]\n",
46401e46
 		leg, rr_set.len, ZSW(rr_set.s), contact.len, ZSW(contact.s),
 		cseq.len, ZSW(cseq.s),
62c98ed1
 		msg->rcv.bind_address->sock_str.len,
46401e46
 		ZSW(msg->rcv.bind_address->sock_str.s));
62c98ed1
 
 	if (dlg_set_leg_info( dlg, tag, &rr_set, &contact, &cseq, leg)!=0) {
47bfa060
 		LM_ERR("dlg_set_leg_info failed (leg %d)\n", leg);
62c98ed1
 		if (rr_set.s) pkg_free(rr_set.s);
 		goto error0;
 	}
 
 	if (rr_set.s) pkg_free(rr_set.s);
 
0b3562ac
 	if ((keep_proxy_rr & 2) > 0 && leg==DLG_CALLEE_LEG && msg->record_route && own_rr > 0) {
 		/* skip_recs contains the number of RR's for the callee */
 		skip_recs -= own_rr;
 		/* Add local RR's to caller's routeset */
 		if( print_rr_body(msg->record_route, &rr_set, DLG_CALLER_LEG,
 		                 &skip_recs) != 0) {
 			LM_ERR("failed to print route records \n");
 			goto error0;
 		}
 		LM_DBG("updating caller route_set %.*s\n",
 			rr_set.len, rr_set.s);
 		if (dlg_update_rr_set( dlg, DLG_CALLER_LEG, &rr_set)!=0) {
 			LM_ERR("dlg_update_rr_set failed\n");
 			if (rr_set.s) pkg_free(rr_set.s);
 			goto error0;
 		}
 		if (rr_set.s) pkg_free(rr_set.s);
 	}
 
62c98ed1
 	return 0;
 error0:
 	return -1;
 }
 
5bdee1ff
 /*!
  * \brief Clone dialog internal unique id to shared memory
  */
 dlg_iuid_t *dlg_get_iuid_shm_clone(dlg_cell_t *dlg)
 {
 	dlg_iuid_t *iuid = NULL;
 
 	if(dlg==NULL)
 		return NULL;
 
 	iuid = (dlg_iuid_t*)shm_malloc(sizeof(dlg_iuid_t));
 	if(iuid==NULL)
 	{
 		LM_ERR("failed to clone dialog iuid\n");
 		return NULL;
 	}
 
 	memset(iuid, 0, sizeof(dlg_iuid_t));
 	iuid->h_entry = dlg->h_entry;
 	iuid->h_id = dlg->h_id;
 
 	return iuid;
 }
 
 
 /*!
  * \brief Free dialog internal unique id stored in shared memory
  */
29036bcc
 void dlg_iuid_sfree(void *iuid)
5bdee1ff
 {
53ec53db
     if(iuid) {
 		LM_DBG("freeing dlg iuid [%u:%u] (%p)\n",
 				((dlg_iuid_t*)iuid)->h_entry,
 				((dlg_iuid_t*)iuid)->h_id, iuid);
5bdee1ff
 		shm_free(iuid);
53ec53db
 	}
5bdee1ff
 }
 
 
c021559e
 /*!
  * \brief Function that executes BYE reply callbacks
  * \param t transaction, unused
  * \param type type of the callback, should be TMCB_RESPONSE_FWDED
377b2f75
  * \param params saved dialog structure inside the callback
c021559e
  */
5bdee1ff
 static void dlg_terminated_confirmed(tm_cell_t *t, int type,
c021559e
                                      struct tmcb_params* params)
 {
5bdee1ff
     dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
 
c021559e
     if(!params || !params->req || !params->param)
     {
         LM_ERR("invalid parameters!\n");
         return;
     }
 
5bdee1ff
 	iuid = (dlg_iuid_t*)*params->param;
 	if(iuid==NULL)
 		return;
c021559e
 
5bdee1ff
     dlg = dlg_get_by_iuid(iuid);
 
     if(dlg==NULL)
c021559e
     {
         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);
5bdee1ff
 	dlg_release(dlg);
c021559e
 }
 
 /*!
  * \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
  */
5bdee1ff
 static void dlg_terminated(sip_msg_t *req, dlg_cell_t *dlg, unsigned int dir)
c021559e
 {
5bdee1ff
 	dlg_iuid_t *iuid = NULL;
 
c021559e
     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);
 
5bdee1ff
 	iuid = dlg_get_iuid_shm_clone(dlg);
 	if(iuid==NULL)
 		return;
 
c021559e
     /* register callback for the coresponding reply */
     if (d_tmb.register_tmcb(req,
                             0,
                             TMCB_RESPONSE_OUT,
                             dlg_terminated_confirmed,
5bdee1ff
                             (void*)iuid,
                             dlg_iuid_sfree) <= 0 ) {
c021559e
         LM_ERR("cannot register response callback for BYE request\n");
         return;
     }
 }
5b3d7218
 
53ec53db
 /*!
  * \brief Function that is registered as TM callback and called on T destroy
  *
  * - happens when wait_ack==1
  *
  */
 static void dlg_ontdestroy(struct cell* t, int type, struct tmcb_params *param)
 {
 	dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
 
 	iuid = (dlg_iuid_t*)(*param->param);
 	dlg = dlg_get_by_iuid(iuid);
 	if(dlg==0)
 		return;
 	/* 1 for callback and 1 for dlg lookup */
 	dlg_unref(dlg, 2);
 }
 
f7aa0a89
 /*!
  * \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
  */
5b3d7218
 static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
 {
5bdee1ff
     dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
c021559e
     int new_state, old_state, unref, event;
     str tag;
5bdee1ff
     sip_msg_t *req = param->req;
 	sip_msg_t *rpl = param->rpl;
5b3d7218
 
5bdee1ff
 	if (shutdown_done)
 		return;
 	iuid = (dlg_iuid_t*)(*param->param);
 	dlg = dlg_get_by_iuid(iuid);
 	if(dlg==0)
5b3d7218
 		return;
 
6b9837c3
 	if (rpl != FAKED_REPLY) {
 		if(parse_headers(rpl, HDR_EOH_F, 0) < 0) {
 			LM_ERR("failed to parse the reply headers\n");
 			goto done_early;
 		}
 	}
 
5bdee1ff
 	unref = 0;
811bb515
 	if (type & (TMCB_RESPONSE_IN|TMCB_ON_FAILURE)) {
 		/* Set the dialog context so it is available in onreply_route and failure_route*/
 		set_current_dialog(req, dlg);
25a32979
 		dlg_set_ctx_iuid(dlg);
9fe51ed4
 		goto done_early;
25a32979
 	}
 
88d0c09f
 	if (type==TMCB_RESPONSE_FWDED) {
d6cc5af6
 		/* The state does not change, but the msg is mutable in this callback*/
c021559e
 		run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
9fe51ed4
 		goto done_early;
88d0c09f
 	}
 
f6a66b42
 	if (type==TMCB_DESTROY)
2201c556
 		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;
f56ae82e
 
2201c556
 	next_state_dlg( dlg, event, &old_state, &new_state, &unref);
74825c89
 	if(new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
 		/* set end time */
 		dlg->end_ts = (unsigned int)(time(0));
 	}
a8b7f94a
 	if(dlg_run_event_route(dlg, (rpl==FAKED_REPLY)?NULL:rpl, old_state,
 			new_state)<0) {
 		/* dialog is gone */
 		return;
 	}
f56ae82e
 
2201c556
 	if (new_state==DLG_STATE_EARLY) {
c021559e
 		run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
2201c556
 		if (old_state!=DLG_STATE_EARLY)
 			if_update_stat(dlg_enable_stats, early_dlgs, 1);
5bdee1ff
 		goto done;
5b3d7218
 	}
 
2201c556
 	if (new_state==DLG_STATE_CONFIRMED_NA &&
 	old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) {
e1068cb0
 		LM_DBG("dialog %p confirmed (ACK pending)\n",dlg);
62c98ed1
 
787fabb1
 		 if (rpl != FAKED_REPLY) {
 			/* get to tag*/
 			if ( !rpl->to && ((parse_headers(rpl, HDR_TO_F,0)<0)
 						|| !rpl->to) ) {
 				LM_ERR("bad reply or missing TO hdr :-/\n");
ab1784d2
 				tag.s = 0;
 				tag.len = 0;
787fabb1
 			} else {
 				tag = get_to(rpl)->tag_value;
 				if (tag.s==0 || tag.len==0) {
 					LM_ERR("missing TAG param in TO hdr :-/\n");
 					tag.s = 0;
 					tag.len = 0;
 				}
ab1784d2
 			}
62c98ed1
 
787fabb1
 			/* save callee's tag, cseq, contact and record route*/
 			if (populate_leg_info( dlg, rpl, t, DLG_CALLEE_LEG, &tag) !=0) {
 				LM_ERR("could not add further info to the dialog\n");
 			}
 		 } else {
 			 LM_ERR("Faked reply!\n");
 		 }
d6cc5af6
 
2201c556
 		/* set start time */
5acc1882
 		dlg->start_ts = (unsigned int)(time(0));
f56ae82e
 
67f04f26
 		/* save the settings to the database,
a70300b1
 		 * if realtime saving mode configured- save dialog now
d1828714
 		 * else: the next time the timer will fire the update*/
29d70f7d
 		dlg->dflags |= DLG_FLAG_NEW;
a70300b1
 		if ( dlg_db_mode==DB_MODE_REALTIME )
 			update_dialog_dbinfo(dlg);
 
dac700e5
 		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' '%.*s'\n",
 				dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state,
 				dlg->callid.len, dlg->callid.s,
 				dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
 				dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
29d70f7d
 		} else {
5bdee1ff
 			/* dialog pointer inserted in timer list */
 			dlg_ref(dlg, 1);
dac700e5
 		}
f56ae82e
 
e1068cb0
 		/* dialog confirmed (ACK pending) */
 		run_dlg_callbacks( DLGCB_CONFIRMED_NA, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
5b3d7218
 
2201c556
 		if (old_state==DLG_STATE_EARLY)
 			if_update_stat(dlg_enable_stats, early_dlgs, -1);
f56ae82e
 
5bdee1ff
 		if (unref) dlg_unref(dlg, unref);
2201c556
 		if_update_stat(dlg_enable_stats, active_dlgs, 1);
5bdee1ff
 		goto done;
5b3d7218
 	}
 
53ec53db
 	if ( new_state==DLG_STATE_DELETED
 				&& (old_state==DLG_STATE_UNCONFIRMED
 					|| old_state==DLG_STATE_EARLY) ) {
b9bb99d2
 		LM_DBG("dialog %p failed (negative reply)\n", dlg);
2201c556
 		/* dialog setup not completed (3456XX) */
c021559e
 		run_dlg_callbacks( DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
d81fd1bf
 		if(dlg_wait_ack==1)
 			dlg_set_tm_waitack(t, dlg);
f24fc42a
 		/* do unref */
2201c556
 		if (unref)
5bdee1ff
 			dlg_unref(dlg, unref);
2201c556
 		if (old_state==DLG_STATE_EARLY)
 			if_update_stat(dlg_enable_stats, early_dlgs, -1);
dd9681ce
 
 		if_update_stat(dlg_enable_stats, failed_dlgs, 1);
 
5bdee1ff
 		goto done;
f56ae82e
 	}
 
5bdee1ff
 	if (unref) dlg_unref(dlg, unref);
29d70f7d
 
5bdee1ff
 done:
9fe51ed4
 	if(dlg_enable_dmq && (dlg->iflags & DLG_IFLAG_DMQ_SYNC) && new_state>old_state) {
 		dlg_dmq_replicate_action(DLG_DMQ_STATE, dlg, 0, 0);
 	}
 
 done_early:
5bdee1ff
 	/* unref due to dlg_get_by_iuid() */
 	dlg_release(dlg);
00cca1f4
 	return;
 }
 
 
f7aa0a89
 /*!
80d27be8
  * \brief Helper function that run dialog callbacks on forwarded requests
  * \see dlg_seq_up_onreply
  * \see dlg_seq_down_onreply
f7aa0a89
  * \param t transaction, unused
  * \param type type of the callback, should be TMCB_RESPONSE_FWDED
  * \param param saved dialog structure inside the callback
80d27be8
  * \param direction direction of the request
f7aa0a89
  */
80d27be8
 static void dlg_seq_onreply_helper(struct cell* t, int type,
 		struct tmcb_params *param, const int direction)
00cca1f4
 {
5bdee1ff
 	dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
00cca1f4
 
5bdee1ff
 	if (shutdown_done)
 		return;
 	iuid = (dlg_iuid_t*)(*param->param);
 	dlg = dlg_get_by_iuid(iuid);
 	if (dlg==0)
00cca1f4
 		return;
 
c021559e
 	if (type==TMCB_RESPONSE_FWDED)
 	{
 		run_dlg_callbacks( DLGCB_RESPONSE_WITHIN,
 		                   dlg,
 		                   param->req,
 		                   param->rpl,
 		                   direction,
 		                   0);
00cca1f4
 	}
5bdee1ff
 	dlg_release(dlg);
00cca1f4
 
5b3d7218
 	return;
 }
 
 
f7aa0a89
 /*!
80d27be8
  * \brief Run dialog callbacks on forwarded requests in upstream direction
  * \see dlg_seq_onreply_helper
f7aa0a89
  * \param t transaction, unused
  * \param type type of the callback, should be TMCB_RESPONSE_FWDED
  * \param param saved dialog structure inside the callback
  */
80d27be8
 static void dlg_seq_up_onreply(struct cell* t, int type, struct tmcb_params *param)
e65a9a51
 {
d90f3b86
 	dlg_seq_onreply_helper(t, type, param, DLG_DIR_UPSTREAM);
80d27be8
 }
e65a9a51
 
 
80d27be8
 /*!
  * \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)
 {
d90f3b86
 	dlg_seq_onreply_helper(t, type, param, DLG_DIR_DOWNSTREAM);
e65a9a51
 }
 
5b3d7218
 
f7aa0a89
 /*!
  * \brief Return the timeout for a dialog
  * \param req SIP message
  * \return value from timeout AVP if present or default timeout
  */
5b3d7218
 inline static int get_dlg_timeout(struct sip_msg *req)
 {
b0a7f212
 	pv_value_t pv_val;
5b3d7218
 
3ed5fe7f
 	if( timeout_avp ) {
20013e4e
 		if ( pv_get_spec_value( req, timeout_avp, &pv_val)==0) {
 			if(pv_val.flags&PV_VAL_INT) {
 				if(pv_val.ri>0 ) {
 					return pv_val.ri;
 				} else {
 					LM_DBG("invalid AVP value\n");
 				}
 			} else {
 				LM_DBG("invalid AVP type\n");
 			}
3ed5fe7f
 		}
20013e4e
 		LM_DBG("unable to get valid AVP value, using default timeout\n");
 	} else {
 		LM_DBG("using default timeout\n");
5b3d7218
 	}
 	return default_timeout;
 }
 
0b665153
 /*!
  * \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;
 }
 
9fe51ed4
 /*!
  * \brief Sync dialog from tm callback (another wrapper)
  * \param t transaction, unused
  * \param type type of the entered callback
  * \param param saved dialog structure in the callback
  */
 static void dlg_on_send(struct cell* t, int type, struct tmcb_params *param)
 {
 	dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
 
 	LM_DBG("dialog_on_send CB\n");
 	iuid = (dlg_iuid_t*)(*param->param);
 	if (iuid==NULL)
a3443211
 		return;
9fe51ed4
 
 	dlg = dlg_get_by_iuid(iuid);
 	if(dlg==NULL)
a3443211
 		return;
9fe51ed4
 
 	/* sync over dmq */
 	if (dlg_enable_dmq) {
a3443211
 		dlg_dmq_replicate_action(DLG_DMQ_UPDATE, dlg, 1, 0);
9fe51ed4
 	}
 
 	/* unref by 2: 1 set when adding in tm cb, 1 set by dlg_get_by_iuid() */
 	dlg_unref(dlg, 1);
 }
 
0b665153
 
f7aa0a89
 /*!
  * \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
  */
5b3d7218
 void dlg_onreq(struct cell* t, int type, struct tmcb_params *param)
29d70f7d
 {
ad4cfe8a
 	sip_msg_t *req = param->req;
5bdee1ff
 	dlg_cell_t *dlg = NULL;
9fe51ed4
 	dlg_iuid_t *iuid = NULL;
0b665153
 
02e43f87
 	if(req->first_line.u.request.method_value == METHOD_BYE) {
 		_dlg_ctx.t = 1;
 		return;
 	}
 
ad4cfe8a
 	if(req->first_line.u.request.method_value != METHOD_INVITE)
29d70f7d
 		return;
ad4cfe8a
 
5bdee1ff
 	dlg = dlg_get_ctx_dialog();
 
 	if (dlg!=NULL) {
ad4cfe8a
 		if (!initial_cbs_inscript) {
 			if (spiral_detected == 1)
5bdee1ff
 				run_dlg_callbacks( DLGCB_SPIRALED, dlg,
ad4cfe8a
 						req, NULL, DLG_DIR_DOWNSTREAM, 0);
 			else if (spiral_detected == 0)
5bdee1ff
 				run_create_callbacks(dlg, req);
ad4cfe8a
 		}
 	}
5bdee1ff
 	if (dlg==NULL) {
404f4037
 		if((dlg_flag_mask==0) || (req->flags&dlg_flag_mask)!=dlg_flag_mask) {
 			LM_DBG("flag not set for creating a new dialog\n");
ad4cfe8a
 			return;
404f4037
 		}
fa0339b1
 		LM_DBG("dialog creation on config flag\n");
ad4cfe8a
 		dlg_new_dialog(req, t, 1);
5bdee1ff
 		dlg = dlg_get_ctx_dialog();
ad4cfe8a
 	}
5bdee1ff
 	if (dlg!=NULL) {
fa0339b1
 		LM_DBG("dialog added to tm callbacks\n");
5bdee1ff
 		dlg_set_tm_callbacks(t, req, dlg, spiral_detected);
fa0339b1
 		_dlg_ctx.t = 1;
5bdee1ff
 		dlg_release(dlg);
ad4cfe8a
 	}
9fe51ed4
 
 	if (dlg_enable_dmq) {
 		iuid = dlg_get_iuid_shm_clone(dlg);
 		if(iuid==NULL)
 		{
 			LM_ERR("failed to create dialog unique id clone\n");
 		} else {
 			/* register callback for when the request is sent */
 			if ( d_tmb.register_tmcb(req, t, TMCB_REQUEST_FWDED,
 			dlg_on_send,
 			(void*)iuid, dlg_iuid_sfree)<0 ) {
 				LM_ERR("failed to register TMCB_REQUEST_FWDED\n");
 				shm_free(iuid);
 			}
 		}
 	}
 
29d70f7d
 }
 
f7aa0a89
 
 /*!
  * \brief Unreference a new dialog, helper function for dlg_onreq
  * \see dlg_onreq
  * \param dialog unreferenced dialog
  */
880b447b
 #if 0
5bdee1ff
 static void unref_new_dialog(void *iuid)
29d70f7d
 {
 	struct tmcb_params p;
 
787fabb1
 	memset(&p, 0, sizeof(struct tmcb_params));
5bdee1ff
 	p.param = (void*)&iuid;
f6a66b42
 	dlg_onreply(0, TMCB_DESTROY, &p);
29d70f7d
 }
880b447b
 #endif
29d70f7d
 
f7aa0a89
 
 /*!
  * \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
377b2f75
  * \param req SIP message
f7aa0a89
  * \param t transaction
377b2f75
  * \param run_initial_cbs if set zero, initial callbacks are not executed
f7aa0a89
  * \return 0 on success, -1 on failure
7004969c
  */
7a8ad674
 int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
5b3d7218
 {
5bdee1ff
 	dlg_cell_t *dlg;
5b3d7218
 	str s;
c021559e
 	str callid;
     str ftag;
     str ttag;
     str req_uri;
     unsigned int dir;
5b3d7218
 
5bdee1ff
 	dlg = dlg_get_ctx_dialog();
     if(dlg != NULL) {
 		dlg_release(dlg);
c021559e
         return -1;
5bdee1ff
 	}
5b3d7218
 
f4b7f301
 	if(req->first_line.u.request.method_value != METHOD_INVITE)
29d70f7d
 		return -1;
5b3d7218
 
4e1ebc6b
 	if(parse_headers(req, HDR_EOH_F, 0) < 0) {
 		LM_ERR("failed to parse the request headers\n");
 		return -1;
 	}
 
c021559e
     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);
 
6fb686de
 	dir = DLG_DIR_NONE;
 	/* search dialog by SIP attributes
9d807d1d
 	 * - hash table slot is left locked  */
 	dlg = dlg_search(&callid, &ftag, &ttag, &dir);
6fb686de
 	if(dlg) {
 		if (detect_spirals) {
b198b390
 			if (spiral_detected == 1) {
 				dlg_hash_release(&callid);
6fb686de
 				return 0;
b198b390
 			}
c021559e
 
6fb686de
 			if ( dlg->state != DLG_STATE_DELETED ) {
5a78a8b8
 				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);
6fb686de
 				/* set ctx dlg id shortcuts */
 				_dlg_ctx.iuid.h_entry = dlg->h_entry;
 				_dlg_ctx.iuid.h_id = dlg->h_id;
 				/* search_dlg() has incremented the ref count by 1 */
fcf5f5b3
 				dlg_release(dlg);
9d807d1d
 				dlg_hash_release(&callid);
6fb686de
 				return 0;
5a78a8b8
 			}
 			dlg_release(dlg);
7f9194c4
 		}
c021559e
     }
48b8190c
     spiral_detected = 0;
c021559e
 
     dlg = build_new_dlg (&callid /*callid*/,
                          &(get_from(req)->uri) /*from uri*/,
                          &(get_to(req)->uri) /*to uri*/,
                          &ftag/*from_tag*/,
                          &req_uri /*r-uri*/ );
 
6fb686de
 	if (dlg==0) {
9d807d1d
 		dlg_hash_release(&callid);
b9bb99d2
 		LM_ERR("failed to create new dialog\n");
29d70f7d
 		return -1;
5b3d7218
 	}
 
62c98ed1
 	/* save caller's tag, cseq, contact and record route*/
c021559e
 	if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
6fb686de
 			&(get_from(req)->tag_value)) !=0) {
9d807d1d
 		dlg_hash_release(&callid);
b9bb99d2
 		LM_ERR("could not add further info to the dialog\n");
62c98ed1
 		shm_free(dlg);
29d70f7d
 		return -1;
62c98ed1
 	}
 
60d9c557
 	/* Populate initial varlist: */
c021559e
 	dlg->vars = get_local_varlist_pointer(req, 1);
 
9d807d1d
 	/* after dlg_search() slot was kept locked */
02fa3bf1
 	link_dlg(dlg, 0, 1);
9d807d1d
 	/* unlock after dlg_search() */
 	dlg_hash_release(&callid);
60d9c557
 
987c8a5f
 	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;
5194b4c7
 	dlg->iflags |= _dlg_ctx.iflags;
987c8a5f
 
84d38ce7
 	if (dlg_send_bye!=0 || _dlg_ctx.to_bye!=0)
82fe2bff
 		dlg->iflags |= DLG_IFLAG_TIMEOUTBYE;
987c8a5f
 
48b8190c
     if (run_initial_cbs)  run_create_callbacks( dlg, req);
aedf352d
 
5b3d7218
 	/* first INVITE seen (dialog created, unconfirmed) */
2fc86906
 	if ( seq_match_mode!=SEQ_MATCH_NO_ID &&
c021559e
 			add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) {
b9bb99d2
 		LM_ERR("failed to add RR param\n");
5b3d7218
 		goto error;
 	}
 
c021559e
     if_update_stat( dlg_enable_stats, processed_dlgs, 1);
4d41d724
 
c125cf6f
 	_dlg_ctx.cpid = my_pid();
5bdee1ff
     _dlg_ctx.iuid.h_entry = dlg->h_entry;
     _dlg_ctx.iuid.h_id = dlg->h_id;
ad4cfe8a
     set_current_dialog(req, dlg);
 
 	return 0;
 
 error:
 	if (!spiral_detected)
5bdee1ff
 		dlg_unref(dlg, 1);               // undo ref regarding linking
ad4cfe8a
 	return -1;
 }
 
 
 /*!
  * \brief add dlg structure to tm callbacks
  * \param t current transaction
  * \param req current sip request
  * \param dlg current dialog
  * \param smode if the sip request was spiraled
  * \return 0 on success, -1 on failure
  */
 int dlg_set_tm_callbacks(tm_cell_t *t, sip_msg_t *req, dlg_cell_t *dlg,
 		int smode)
 {
53ec53db
 	dlg_iuid_t *iuid = NULL;
5bdee1ff
 	if(t==NULL)
 		return -1;
 
ad4cfe8a
 	if(smode==0) {
5bdee1ff
 		iuid = dlg_get_iuid_shm_clone(dlg);
53ec53db
 		if(iuid==NULL)
5bdee1ff
 		{
 			LM_ERR("failed to create dialog unique id clone\n");
 			goto error;
 		}
ad4cfe8a
 		if ( d_tmb.register_tmcb( req, t,
811bb515
 				TMCB_RESPONSE_IN|TMCB_RESPONSE_READY|TMCB_RESPONSE_FWDED|TMCB_ON_FAILURE,
5bdee1ff
 				dlg_onreply, (void*)iuid, dlg_iuid_sfree)<0 ) {
ad4cfe8a
 			LM_ERR("failed to register TMCB\n");
 			goto error;
 		}
 	}
 
 	dlg->dflags |= DLG_FLAG_TM;
71dd7548
 
29d70f7d
 	return 0;
5b3d7218
 error:
53ec53db
 	dlg_iuid_sfree(iuid);
29d70f7d
 	return -1;
5b3d7218
 }
 
53ec53db
 /*!
  * \brief add dlg structure to tm callbacks to wait for negative ACK
  * \param t current transaction
  * \param dlg current dialog
  * \return 0 on success, -1 on failure
  */
 int dlg_set_tm_waitack(tm_cell_t *t, dlg_cell_t *dlg)
 {
 	dlg_iuid_t *iuid = NULL;
 	if(t==NULL)
 		return -1;
 
d9f7383c
 	LM_DBG("registering TMCB to wait for negative ACK\n");
53ec53db
 	iuid = dlg_get_iuid_shm_clone(dlg);
 	if(iuid==NULL)
 	{
 		LM_ERR("failed to create dialog unique id clone\n");
 		goto error;
 	}
 	dlg_ref(dlg, 1);
 	if ( d_tmb.register_tmcb( NULL, t,
 			TMCB_DESTROY,
 			dlg_ontdestroy, (void*)iuid, dlg_iuid_sfree)<0 ) {
 		LM_ERR("failed to register TMCB to wait for negative ACK\n");
 		dlg_unref(dlg, 1);
 		goto error;
 	}
 
 	return 0;
 error:
 	dlg_iuid_sfree(iuid);
 	return -1;
 }
5b3d7218
 
f7aa0a89
 /*!
  * \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)
5b3d7218
 {
 	char *s;
 
876787d9
 	/* sanity checks */
 	if (!p) {
 		LM_ERR("NULL start of parameter string");
 		return -1;
 	}
 
 	if (!end) {
 		LM_ERR("NULL end of parameter string");
 		return -1;
 	}
 
 	if (!h_entry) {
 		LM_ERR("NULL h_entry");
 		return -1;
 	}
 
 	if (!h_id) {
 		LM_ERR("NULL h_id");
 		return -1;
 	}
 
5b3d7218
 	for ( s=p ; p<end && *p!=DLG_SEPARATOR ; p++ );
876787d9
 
5b3d7218
 	if (*p!=DLG_SEPARATOR) {
b9bb99d2
 		LM_ERR("malformed rr param '%.*s'\n", (int)(long)(end-s), s);
5b3d7218
 		return -1;
 	}
 
dfc40122
 	if ( reverse_hex2int( s, p-s, (unsigned int*)h_entry)<0 ) {
b9bb99d2
 		LM_ERR("invalid hash entry '%.*s'\n", (int)(long)(p-s), s);
5b3d7218
 		return -1;
 	}
 
dfc40122
 	if ( reverse_hex2int( p+1, end-(p+1), (unsigned int*)h_id)<0 ) {
b9bb99d2
 		LM_ERR("invalid hash id '%.*s'\n", (int)(long)(end-(p+1)), p+1 );
5b3d7218
 		return -1;
 	}
 
 	return 0;
 }
 
 
de07fe4e
 /*!
  * \brief Update the saved Contact 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 dlg_refresh_contacts(struct dlg_cell *dlg, struct sip_msg *req,
 		unsigned int dir)
 {
 	str contact;
 
 	if(req->first_line.type == SIP_REPLY)
 		return 0;
 	if(req->first_line.u.request.method_value != METHOD_INVITE)
 		return 0;
 
 	/* extract the contact address */
 	if (!req->contact&&(parse_headers(req,HDR_CONTACT_F,0)<0||!req->contact)){
 		LM_ERR("bad sip message or missing Contact hdr\n");
 		return -1;
 	}
 	if ( parse_contact(req->contact)<0 ||
 	((contact_body_t *)req->contact->parsed)->contacts==NULL ||
 	((contact_body_t *)req->contact->parsed)->contacts->next!=NULL ) {
 		LM_ERR("bad Contact HDR\n");
 		return -1;
 	}
 	contact = ((contact_body_t *)req->contact->parsed)->contacts->uri;
 
 	if ( dir==DLG_DIR_UPSTREAM) {
 		return dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact);
 	} else if ( dir==DLG_DIR_DOWNSTREAM) {
 		return dlg_update_contact(dlg, DLG_CALLER_LEG, &contact);
 	} else {
 		LM_CRIT("dir is not set!\n");
 		return -1;
 	}
 }
 
 
f7aa0a89
 /*!
  * \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
  */
c7f46953
 static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req,
f7aa0a89
 		unsigned int dir)
62c98ed1
 {
 	if ( (!req->cseq && parse_headers(req,HDR_CSEQ_F,0)<0) || !req->cseq ||
 	!req->cseq->parsed) {
b9bb99d2
 		LM_ERR("bad sip message or missing CSeq hdr :-/\n");
62c98ed1
 		return -1;
 	}
 
c7f46953
 	if ( dir==DLG_DIR_UPSTREAM) {
62c98ed1
 		return dlg_update_cseq(dlg, DLG_CALLEE_LEG,&((get_cseq(req))->number));
c7f46953
 	} else if ( dir==DLG_DIR_DOWNSTREAM) {
62c98ed1
 		return dlg_update_cseq(dlg, DLG_CALLER_LEG,&((get_cseq(req))->number));
c7f46953
 	} else {
b9bb99d2
 		LM_CRIT("dir is not set!\n");
c7f46953
 		return -1;
62c98ed1
 	}
 }
 
3537c2d8
 /*!
  * \brief Unreference a dialog from tm callback (another wrapper)
  * \param t transaction, unused
  * \param type type of the entered callback
  * \param param saved dialog structure in the callback
  */
 static void unref_dlg_from_cb(struct cell* t, int type, struct tmcb_params *param)
 {
5bdee1ff
 	dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
75fc49f7
 
5bdee1ff
 	iuid = (dlg_iuid_t*)(*param->param);
 	if (iuid==NULL)
75fc49f7
 		return;
3537c2d8
 
5bdee1ff
 	dlg = dlg_get_by_iuid(iuid);
 	if(dlg==NULL)
 		return;
 	/* unref by 2: 1 set when adding in tm cb, 1 sent by dlg_get_by_iuid() */
 	dlg_unref(dlg, 2);
3537c2d8
 }
 
94f18a95
 /*!
  *
  */
 dlg_cell_t *dlg_lookup_msg_dialog(sip_msg_t *msg, unsigned int *dir)
5bdee1ff
 {
 	dlg_cell_t *dlg = NULL;
 	str callid;
fc0f544b
 	str ftag;
 	str ttag;
94f18a95
 	unsigned int vdir;
5bdee1ff
 
 	/* Retrieve the current dialog */
 	dlg = dlg_get_ctx_dialog();
94f18a95
 	if(dlg!=NULL) {
 		if(dir) {
 			if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0) {
 				dlg_release(dlg);
 				return NULL;
 			}
 			if (dlg->tag[DLG_CALLER_LEG].len == ftag.len &&
 					   strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag.s, ftag.len)==0 &&
 					   strncmp(dlg->callid.s, callid.s, callid.len)==0) {
 				*dir = DLG_DIR_DOWNSTREAM;
 			} else {
 				if (ttag.len>0 && dlg->tag[DLG_CALLER_LEG].len == ttag.len &&
 						   strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag.s, ttag.len)==0 &&
 						   strncmp(dlg->callid.s, callid.s, callid.len)==0) {
 					*dir = DLG_DIR_UPSTREAM;
 				}
 			}
 		}
5bdee1ff
 		return dlg;
94f18a95
 	}
7004969c
 
5bdee1ff
 	if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0)
 		return NULL;
94f18a95
 	vdir = DLG_DIR_NONE;
 	dlg = get_dlg(&callid, &ftag, &ttag, &vdir);
5bdee1ff
 	if (dlg==NULL){
 		LM_DBG("dlg with callid '%.*s' not found\n",
 				msg->callid->body.len, msg->callid->body.s);
 		return NULL;
 	}
53778cad
         if(msg->first_line.u.request.method_value == METHOD_CANCEL) {
                 dlg_set_ctx_iuid(dlg);
         }
94f18a95
 	if(dir) *dir = vdir;
5bdee1ff
 	return dlg;
 }
 
94f18a95
 /*!
  *
  */
 dlg_cell_t *dlg_get_msg_dialog(sip_msg_t *msg)
 {
 	return dlg_lookup_msg_dialog(msg, NULL);
 }
 
f7aa0a89
 /*!
  * \brief Function that is registered as RR callback for dialog tracking
e349af55
  *
f7aa0a89
  * 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
  */
5b3d7218
 void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
 {
e4a11d71
 	dlg_cell_t *dlg = NULL;
 	dlg_iuid_t *iuid = NULL;
80d27be8
 	str val, callid, ftag, ttag;
e4a11d71
 	int h_entry=0, h_id=0, new_state=0, old_state=0;
 	int unref=0, event=0, timeout=0, reset=0;
 	unsigned int dir=0;
 	int ret=0;
5b3d7218
 
5bdee1ff
 	dlg = dlg_get_ctx_dialog();
 	if (dlg!=NULL) {
 		dlg_release(dlg);
29d70f7d
 		return;
5bdee1ff
 	}
29d70f7d
 
1bc78405
 	/* 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;
 
2fc86906
 	dlg = 0;
c7f46953
 	dir = DLG_DIR_NONE;
 
2fc86906
 	if ( seq_match_mode!=SEQ_MATCH_NO_ID ) {
 		if( d_rrb.get_route_param( req, &rr_param, &val)!=0) {
b9bb99d2
 			LM_DBG("Route param '%.*s' not found\n", rr_param.len,rr_param.s);
2fc86906
 			if (seq_match_mode==SEQ_MATCH_STRICT_ID )
 				return;
 		} else {
00cca1f4
 			LM_DBG("route param is '%.*s' (len=%d)\n",val.len,val.s,val.len);
2fc86906
 
 			if ( parse_dlg_rr_param( val.s, val.s+val.len, &h_entry, &h_id)<0 )
 				return;
 
5bdee1ff
 			dlg = dlg_lookup(h_entry, h_id);
0723496c
 			if (dlg==0) {
109f8f3a
 				LM_WARN("unable to find dialog for %.*s "
c4684574
 					"with route param '%.*s' [%u:%u] "
 					"and call-id '%.*s'\n",
8307c654
 					req->first_line.u.request.method.len,
109f8f3a
 					req->first_line.u.request.method.s,
c4684574
 					val.len,val.s, h_entry, h_id,
 					req->callid->body.len, req->callid->body.s);
30326fe2
 				if (seq_match_mode==SEQ_MATCH_STRICT_ID )
 					return;
 			} else {
0b665153
 				if (pre_match_parse( req, &callid, &ftag, &ttag, 1)<0) {
f44534cb
 					// lookup_dlg has incremented the ref count by 1
5bdee1ff
 					dlg_release(dlg);
30326fe2
 					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, callee tag='%.*s'/%d\n",
 							dlg->callid.len, dlg->callid.s, dlg->callid.len,
 							dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
 							dlg->tag[DLG_CALLER_LEG].len,
 							dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s,
 							dlg->tag[DLG_CALLEE_LEG].len);
f44534cb
 					// lookup_dlg has incremented the ref count by 1
5bdee1ff
 					dlg_release(dlg);
67f04f26
 
30326fe2
 					// Reset variables in order to do a lookup based on SIP-Elements.
 					dlg = 0;
 					dir = DLG_DIR_NONE;
67f04f26
 
30326fe2
 					if (seq_match_mode==SEQ_MATCH_STRICT_ID )
 						return;
 				}
2fc86906
 			}
 		}
5b3d7218
 	}
 
 	if (dlg==0) {
e349af55
 		if (pre_match_parse(req, &callid, &ftag, &ttag, 1)<0)
5b3d7218
 			return;
c7f46953
 		/* TODO - try to use the RR dir detection to speed up here the
 		 * search -bogdan */
f44534cb
 		dlg = get_dlg(&callid, &ftag, &ttag, &dir);
5bdee1ff
 		if (dlg==0){
b9bb99d2
 			LM_DBG("Callid '%.*s' not found\n",
2fc86906
 				req->callid->body.len, req->callid->body.s);
5b3d7218
 			return;
 		}
 	}
 
f44534cb
     /* set current dialog - re-use ref increment from dlg_get() above */
c021559e
     set_current_dialog( req, dlg);
e4a11d71
     h_entry = dlg->h_entry;
     h_id = dlg->h_id;
     _dlg_ctx.iuid.h_entry = h_entry;
     _dlg_ctx.iuid.h_id = h_id;
c021559e
 
e349af55
 	if(dlg->iflags & DLG_IFLAG_CSEQ_DIFF) {
 		if(dlg_cseq_refresh(req, dlg, dir)<0) {
 			LM_ERR("failed to refresh cseq update\n");
 		}
 	}
 
25a32979
 	if (req->first_line.u.request.method_value != METHOD_ACK) {
 		iuid = dlg_get_iuid_shm_clone(dlg);
 		if(iuid!=NULL)
 		{
 			/* register callback for the replies of this request */
811bb515
 			if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_IN|TMCB_ON_FAILURE,
25a32979
 					dlg_onreply, (void*)iuid, dlg_iuid_sfree)<0 ) {
 				LM_ERR("failed to register TMCB (3)\n");
 				shm_free(iuid);
 			}
 			iuid = NULL;
 		}
 	}
23a34813
 
2201c556
 	/* run state machine */
62c98ed1
 	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;
 	}
ee90e8e2
 
2201c556
 	next_state_dlg( dlg, event, &old_state, &new_state, &unref);
5b3d7218
 
2201c556
 	CURR_DLG_ID = req->id;
5acc1882
 	CURR_DLG_LIFETIME = (unsigned int)(time(0))-dlg->start_ts;
2201c556
 	CURR_DLG_STATUS = new_state;
 
a8b7f94a
 	if(dlg_run_event_route(dlg, req, old_state, new_state)<0) {
 		/* dialog is gone */
e4a11d71
 		return;
 	}
 
3537c2d8
 	/* delay deletion of dialog until transaction has died off in order
 	 * to absorb in-air messages */
 	if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
23a34813
 		/* set end time */
 		dlg->end_ts = (unsigned int)(time(0));
5bdee1ff
 		iuid = dlg_get_iuid_shm_clone(dlg);
 		if(iuid!=NULL) {
 			if ( d_tmb.register_tmcb(req, NULL, TMCB_DESTROY,
 					unref_dlg_from_cb, (void*)iuid, dlg_iuid_sfree)<0 ) {
 				LM_ERR("failed to register deletion delay function\n");
 				shm_free(iuid);
 			} else {
 				dlg_ref(dlg, 1);
 			}
3537c2d8
 		}
 	}
 
5194b4c7
 	if (new_state==DLG_STATE_CONFIRMED && old_state!=DLG_STATE_CONFIRMED)
 		dlg_ka_add(dlg);
 
2201c556
 	/* run actions for the transition */
 	if (event==DLG_EVENT_REQBYE && new_state==DLG_STATE_DELETED &&
 	old_state!=DLG_STATE_DELETED) {
b9bb99d2
 		LM_DBG("BYE successfully processed\n");
2201c556
 		/* remove from timer */
29d70f7d
 		ret = remove_dialog_timer(&dlg->tl);
3e8d408e
 		if (ret < 0) {
 			LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] "
 				"with clid '%.*s' and tags '%.*s' '%.*s'\n",
 				dlg, dlg->h_entry, dlg->h_id,
 				dlg->callid.len, dlg->callid.s,
 				dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
 				dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
 		} else if (ret > 0) {
389843c3
 			LM_WARN("inconsistent dlg timer data on dlg %p [%u:%u] "
3e8d408e
 				"with clid '%.*s' and tags '%.*s' '%.*s'\n",
 				dlg, dlg->h_entry, dlg->h_id,
 				dlg->callid.len, dlg->callid.s,
 				dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
 				dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
29d70f7d
 		} else {
5bdee1ff
 			/* one extra unref due to removal from timer list */
29d70f7d
 			unref++;
3e8d408e
 		}
5b3d7218
 		/* dialog terminated (BYE) */
c021559e
         dlg_terminated( req, dlg, dir);
f24fc42a
 
5bdee1ff
 		dlg_unref(dlg, unref);
f24fc42a
 
02e43f87
 		_dlg_ctx.cpid = my_pid();
 		_dlg_ctx.expect_t = 1;
5b733fa3
 		dlg_set_ctx_iuid(dlg);
02e43f87
 
5b3d7218
 		if_update_stat( dlg_enable_stats, active_dlgs, -1);
5bdee1ff
 		goto done;
5b3d7218
 	}
 
4efe19f6
 	if ( (event==DLG_EVENT_REQ || event==DLG_EVENT_REQACK || event==DLG_EVENT_REQPRACK)
6cf3ab0c
 	&& (new_state==DLG_STATE_CONFIRMED || new_state==DLG_STATE_EARLY)) {
e1068cb0
 
6cec4124
 		timeout = get_dlg_timeout(req);
 		if (timeout!=default_timeout) {
 			dlg->lifetime = timeout;
3ed5fe7f
 		}
b5993240
 		reset = !((dlg->iflags & DLG_IFLAG_TIMER_NORESET) || dlg_timeout_noreset);
c70823c0
 
 		if ((new_state!=DLG_STATE_EARLY) && (old_state!=DLG_STATE_CONFIRMED || reset)) {
6cf3ab0c
 			if (update_dlg_timer( &dlg->tl, dlg->lifetime )==-1) {
 				LM_ERR("failed to update dialog lifetime\n");
 			} else {
 				dlg->dflags |= DLG_FLAG_CHANGED;
 			}
6cec4124
 		}
6cf3ab0c
 		if(event != DLG_EVENT_REQACK) {
de07fe4e
 			if(dlg_refresh_contacts(dlg, req, dir)!=0) {
 				LM_ERR("contacts update failed\n");
 			} else {
 				dlg->dflags |= DLG_FLAG_CHANGED;
 			}
6cf3ab0c
 			if(update_cseqs(dlg, req, dir)!=0) {
 				LM_ERR("cseqs update failed\n");
 			} else {
 				dlg->dflags |= DLG_FLAG_CHANGED;
 			}
 		}
 		if(dlg_db_mode==DB_MODE_REALTIME && (dlg->dflags&DLG_FLAG_CHANGED)) {
 			update_dialog_dbinfo(dlg);
6cec4124
 		}
62c98ed1
 
e1068cb0
 		if (old_state==DLG_STATE_CONFIRMED_NA) {
 			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);
de4ad807
 			if (add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) {
 				LM_ERR("failed to add RR param\n");
18644f93
 				goto done;
de4ad807
 			}
e1068cb0
 
 			if ( (event!=DLG_EVENT_REQACK) &&
 					(dlg->cbs.types)&DLGCB_RESPONSE_WITHIN ) {
5bdee1ff
 				iuid = dlg_get_iuid_shm_clone(dlg);
 				if(iuid!=NULL)
 				{
 					/* 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*)iuid, dlg_iuid_sfree)<0 ) {
 						LM_ERR("failed to register TMCB (2)\n");
 						shm_free(iuid);
 					}
e1068cb0
 				}
e65a9a51
 			}
a70300b1
 		}
5b3d7218
 	}
 
e65a9a51
 	if(new_state==DLG_STATE_CONFIRMED && old_state==DLG_STATE_CONFIRMED_NA){
29d70f7d
 		dlg->dflags |= DLG_FLAG_CHANGED;
d1828714
 		if(dlg_db_mode == DB_MODE_REALTIME)
 			update_dialog_dbinfo(dlg);
 	}
 
5bdee1ff
 done:
9fe51ed4
 	if(dlg_enable_dmq && (dlg->iflags & DLG_IFLAG_DMQ_SYNC) && new_state>old_state) {
 		dlg_dmq_replicate_action(DLG_DMQ_STATE, dlg, 0, 0);
 	}
 
5bdee1ff
 	dlg_release(dlg);
5b3d7218
 	return;
 }
 
 
f7aa0a89
 /*!
  * \brief Timer function that removes expired dialogs, run timeout route
  * \param tl dialog timer list
  */
5bdee1ff
 void dlg_ontimeout(struct dlg_tl *tl)
5b3d7218
 {
5bdee1ff
 	dlg_cell_t *dlg;
80d27be8
 	int new_state, old_state, unref;
5bdee1ff
 	sip_msg_t *fmsg;
d46f3f64
 	void* timeout_cb = 0;
5b7838c9
 	sr_kemi_eng_t *keng = NULL;
 	str evname;
5b3d7218
 
80d27be8
 	/* get the dialog tl payload */
 	dlg = ((struct dlg_cell*)((char *)(tl) -
5bdee1ff
 			(unsigned long)(&((struct dlg_cell*)0)->tl)));
5b3d7218
 
14348216
 	if (dlg_filter_mode & DLG_FILTER_LOCALONLY) {
e530ad44
 		if (dlg->bind_addr[0] == NULL) {
 			LM_DBG("skipping dialog without bind address\n");
 			return;
 		}
79c4c7d0
 
e530ad44
 		if (lookup_local_socket(&(dlg->bind_addr[0]->sock_str)) == NULL) {
 			LM_DBG("skipping non local dialog\n");
 			return;
 		}
79c4c7d0
 	}
14348216
 
fa90ad3d
 	/* mark dialog as expired */
 	dlg->dflags |= DLG_FLAG_EXPIRED;
 
29036bcc
 	if(dlg->state==DLG_STATE_CONFIRMED_NA
 				|| dlg->state==DLG_STATE_CONFIRMED)
29d70f7d
 	{
5b7838c9
 		if((dlg->toroute>0 && dlg->toroute<main_rt.entries
 					&& main_rt.rlist[dlg->toroute]!=NULL)
 				|| (dlg->toroute_name.len>0
 					&& dlg_event_callback.s!=NULL && dlg_event_callback.len>0))
29d70f7d
 		{
29036bcc
 			fmsg = faked_msg_next();
 			if (exec_pre_script_cb(fmsg, REQUEST_CB_TYPE)>0)
 			{
 				dlg_ref(dlg, 1);
 				dlg_set_ctx_iuid(dlg);
 				LM_DBG("executing route %d on timeout\n", dlg->toroute);
 				set_route_type(REQUEST_ROUTE);
5b7838c9
 				if(dlg->toroute>0) {
 					run_top_route(main_rt.rlist[dlg->toroute], fmsg, 0);
 				} else {
b8ab9fcc
 					keng = sr_kemi_eng_get();
5b7838c9
 					if(keng!=NULL) {
 						evname.s = "dialog:timeout";
 						evname.len = sizeof("dialog:timeout") - 1;
d4f4a8ad
 						if(sr_kemi_route(keng, fmsg, EVENT_ROUTE,
5b7838c9
 									&dlg_event_callback, &evname)<0) {
 							LM_ERR("error running event route kemi callback\n");
 						}
 					}
 				}
29036bcc
 				dlg_reset_ctx_iuid();
 				exec_post_script_cb(fmsg, REQUEST_CB_TYPE);
 				dlg_unref(dlg, 1);
 			}
29d70f7d
 		}
 
82fe2bff
 		if(dlg->iflags&DLG_IFLAG_TIMEOUTBYE)
29036bcc
 		{
5002f66a
 			/* set the dialog context so that it's available in
 			 * tm:local-request event route */
 			dlg_set_ctx_iuid(dlg);
e39a90e1
 			if(dlg_bye_all(dlg, NULL)<0)
 				dlg_unref(dlg, 1);
23a34813
 			dlg_reset_ctx_iuid();
265dc95a
 
29036bcc
 			dlg_unref(dlg, 1);
 			if_update_stat(dlg_enable_stats, expired_dlgs, 1);
 			return;
 		}
29d70f7d
 	}
 
2201c556
 	next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref);
d46f3f64
     /* used for computing duration for timed out acknowledged dialog */
 	if (DLG_STATE_CONFIRMED == old_state) {
 		timeout_cb = (void *)CONFIRMED_DIALOG_STATE;
23a34813
 	}
d46f3f64
 
a8b7f94a
 	if(dlg_run_event_route(dlg, NULL, old_state, new_state)<0) {
 		/* dialog is gone */
 		return;
 	}
2201c556
 
 	if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
06b7e416
 		LM_WARN("dlg timeout - callid: '%.*s' tags: '%.*s' '%.*s' ostate: %d\n",
76e731f1
 			dlg->callid.len, dlg->callid.s,
 			dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
06b7e416
 			dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s,
 			old_state);
5b3d7218
 
23a34813
 		/* set end time */
 		dlg->end_ts = (unsigned int)(time(0));
 
2201c556
 		/* dialog timeout */
d46f3f64
 		run_dlg_callbacks( DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, timeout_cb);
f24fc42a
 
5bdee1ff
 		dlg_unref(dlg, unref+1);
5b3d7218
 
2201c556
 		if_update_stat( dlg_enable_stats, expired_dlgs, 1);
 		if_update_stat( dlg_enable_stats, active_dlgs, -1);
29d70f7d
 	} else {
5bdee1ff
 		dlg_unref(dlg, 1);
2201c556
 	}
5b3d7218
 
9fe51ed4
 	if(dlg_enable_dmq && (dlg->iflags & DLG_IFLAG_DMQ_SYNC) && new_state>old_state) {
 		dlg_dmq_replicate_action(DLG_DMQ_STATE, dlg, 0, 0);
 	}
 
5b3d7218
 	return;
 }
 
ee90e8e2
 
f7aa0a89
 /*!
  * \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)
ee90e8e2
 {
 	int l = 0;
 	char *ch = NULL;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
 	if (CURR_DLG_ID!=msg->id)
b0a7f212
 		return pv_get_null( msg, param, res);
ee90e8e2
 
 	res->ri = CURR_DLG_LIFETIME;
 	ch = int2str( (unsigned long)res->ri, &l);
 
 	res->rs.s = ch;
 	res->rs.len = l;
 
b0a7f212
 	res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT;
ee90e8e2
 
 	return 0;
 }
 
 
f7aa0a89
 /*!
  * \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)
ee90e8e2
 {
 	int l = 0;
 	char *ch = NULL;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
 	if (CURR_DLG_ID!=msg->id)
b0a7f212
 		return pv_get_null( msg, param, res);
ee90e8e2
 
 	res->ri = CURR_DLG_STATUS;
 	ch = int2str( (unsigned long)res->ri, &l);
 
 	res->rs.s = ch;
 	res->rs.len = l;
 
b0a7f212
 	res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT;
ee90e8e2
 
 	return 0;
 }
a46f2ee6
 
 /*!
  * \brief Execute event routes based on new state
a8b7f94a
  * - returns: -1 if dialog doesn't exist after event route execution
  *             0 if all ok
a46f2ee6
  */
a8b7f94a
 int dlg_run_event_route(dlg_cell_t *dlg, sip_msg_t *msg, int ostate, int nstate)
a46f2ee6
 {
 	sip_msg_t *fmsg;
 	int rt;
4fab97fb
 	int bkroute;
5b7838c9
 	sr_kemi_eng_t *keng = NULL;
a8b7f94a
 	str evname = str_init("unknown");
 	int h_entry=0;
 	int h_id=0;
 	dlg_cell_t *dlg0 = NULL;
a46f2ee6
 
 	if(dlg==NULL)
a8b7f94a
 		return -1;
a46f2ee6
 	if(ostate==nstate)
a8b7f94a
 		return 0;
a46f2ee6
 
 	rt = -1;
5b7838c9
 	if(dlg_event_callback.s==NULL || dlg_event_callback.len<=0) {
 		if(nstate==DLG_STATE_CONFIRMED_NA) {
 			rt = dlg_event_rt[DLG_EVENTRT_START];
b8ab9fcc
 		} else if(nstate==DLG_STATE_DELETED) {
 			if(ostate==DLG_STATE_CONFIRMED || ostate==DLG_STATE_CONFIRMED_NA) {
 				rt = dlg_event_rt[DLG_EVENTRT_END];
 			} else if(ostate==DLG_STATE_UNCONFIRMED || ostate==DLG_STATE_EARLY) {
 				rt = dlg_event_rt[DLG_EVENTRT_FAILED];
 			}
 		}
 		if(rt==-1 || event_rt.rlist[rt]==NULL)
 			return 0;
 	}  else {
 		if(nstate==DLG_STATE_CONFIRMED_NA) {
5b7838c9
 			evname.s = "dialog:start";
 			evname.len = sizeof("dialog:start") - 1;
 		} else if(nstate==DLG_STATE_DELETED) {
 			if(ostate==DLG_STATE_CONFIRMED || ostate==DLG_STATE_CONFIRMED_NA) {
 				evname.s = "dialog:end";
 				evname.len = sizeof("dialog:end") - 1;
 			} else if(ostate==DLG_STATE_UNCONFIRMED || ostate==DLG_STATE_EARLY) {
 				evname.s = "dialog:failed";
 				evname.len = sizeof("dialog:failed") - 1;
 			}
 		}
 		keng = sr_kemi_eng_get();
 		if(keng==NULL) {
 			LM_DBG("event callback (%s) set, but no cfg engine\n",
 					dlg_event_callback.s);
a8b7f94a
 			return 0;
5b7838c9
 		}
a46f2ee6
 	}
 
7004969c
 	if(rt>=0 || dlg_event_callback.len>0) {
 		if(msg==NULL)
 			fmsg = faked_msg_next();
 		else
 			fmsg = msg;
a46f2ee6
 
7004969c
 		if (exec_pre_script_cb(fmsg, LOCAL_CB_TYPE)<=0)
a8b7f94a
 			return 0;
5b7838c9
 
a46f2ee6
 		dlg_ref(dlg, 1);
a8b7f94a
 		h_entry = dlg->h_entry;
 		h_id = dlg->h_id;
a46f2ee6
 		dlg_set_ctx_iuid(dlg);
 		LM_DBG("executing event_route %d on state %d\n", rt, nstate);
4fab97fb
 		bkroute = get_route_type();
88a9ffee
 		set_route_type(LOCAL_ROUTE);
5b7838c9
 		if(rt>=0) {
 			run_top_route(event_rt.rlist[rt], fmsg, 0);
 		} else {
 			if(keng!=NULL) {
d4f4a8ad
 				if(sr_kemi_route(keng, fmsg, EVENT_ROUTE,
5b7838c9
 							&dlg_event_callback, &evname)<0) {
 					LM_ERR("error running event route kemi callback (%d %d)\n",
 							ostate, nstate);
 				}
 			}
 		}
a46f2ee6
 		dlg_reset_ctx_iuid();
88a9ffee
 		exec_post_script_cb(fmsg, LOCAL_CB_TYPE);
4fab97fb
 		set_route_type(bkroute);
a8b7f94a
 		/* re-lookup the dialog, execution of the route could take long time */
 		dlg0 = dlg_lookup(h_entry, h_id);
 		if (dlg0==0) {
 			LM_ALERT("after event route - dialog not found [%u:%u] (%d/%d) (%p) (%.*s)\n",
 					h_entry, h_id, ostate, nstate, dlg, evname.len, evname.s);
c6faf3af
 			if (nstate == DLG_STATE_DELETED) {
 				if (ostate == DLG_STATE_UNCONFIRMED) {
 					if_update_stat(dlg_enable_stats, failed_dlgs, 1);
 				} else if (ostate == DLG_STATE_EARLY) {
 					if_update_stat(dlg_enable_stats, early_dlgs, -1);
 					if_update_stat(dlg_enable_stats, failed_dlgs, 1);
 				} else if (ostate != DLG_STATE_DELETED) {
 					if_update_stat(dlg_enable_stats, active_dlgs, -1);
 				}
 			}
a8b7f94a
 			return -1;
 		} else {
 			dlg_release(dlg0);
 			dlg_unref(dlg, 1);
 		}
a46f2ee6
 	}
a8b7f94a
 	return 0;
a46f2ee6
 }
7a8ad674
 
 int dlg_manage(sip_msg_t *msg)
 {
 	str tag;
 	int backup_mode;
 	dlg_cell_t *dlg = NULL;
 	tm_cell_t *t = NULL;
 
 	if( (msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL )
 	{
 		LM_ERR("bad TO header\n");
 		return -1;
 	}
 	tag = get_to(msg)->tag_value;
 	if(tag.s!=0 && tag.len!=0)
 	{
 		backup_mode = seq_match_mode;
 		seq_match_mode = SEQ_MATCH_NO_ID;
 		dlg_onroute(msg, NULL, NULL);
 		seq_match_mode = backup_mode;
 	} else {
 		t = d_tmb.t_gett();
 		if(t==T_UNDEFINED)
 			t = NULL;
 		if(dlg_new_dialog(msg, t, initial_cbs_inscript)!=0)
 			return -1;
 		dlg = dlg_get_ctx_dialog();
 		if(dlg==NULL)
 			return -1;
fa0339b1
 		if(t!=NULL) {
7a8ad674
 			dlg_set_tm_callbacks(t, msg, dlg, spiral_detected);
fa0339b1
 			_dlg_ctx.t = 1;
 			LM_DBG("dialog created on existing transaction\n");
 		} else {
 			LM_DBG("dialog created before transaction\n");
 		}
7a8ad674
 		dlg_release(dlg);
 	}
 	return 1;
 }