コード例 #1
0
ファイル: dlg_handlers.c プロジェクト: alexdowad/kamailio
/*!
 * \brief Execute event routes based on new state
 *
 */
void dlg_run_event_route(dlg_cell_t *dlg, sip_msg_t *msg, int ostate, int nstate)
{
	sip_msg_t *fmsg;
	int rt;
	int bkroute;

	if(dlg==NULL)
		return;
	if(ostate==nstate)
		return;

	rt = -1;
	if(nstate==DLG_STATE_CONFIRMED_NA) {
		rt = dlg_event_rt[DLG_EVENTRT_START];
	} 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;

	if(msg==NULL)
		fmsg = faked_msg_next();
	else
		fmsg = msg;

	if (exec_pre_script_cb(fmsg, LOCAL_CB_TYPE)>0)
	{
		dlg_ref(dlg, 1);
		dlg_set_ctx_iuid(dlg);
		LM_DBG("executing event_route %d on state %d\n", rt, nstate);
		bkroute = get_route_type();
		set_route_type(LOCAL_ROUTE);
		run_top_route(event_rt.rlist[rt], fmsg, 0);
		dlg_reset_ctx_iuid();
		exec_post_script_cb(fmsg, LOCAL_CB_TYPE);
		dlg_unref(dlg, 1);
		set_route_type(bkroute);
	}
}
コード例 #2
0
ファイル: dlg_handlers.c プロジェクト: alexdowad/kamailio
/*!
 * \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)
{
    dlg_cell_t *dlg = NULL;
	dlg_iuid_t *iuid = NULL;
    int new_state, old_state, unref, event;
    str tag;
    sip_msg_t *req = param->req;
	sip_msg_t *rpl = param->rpl;

	if (shutdown_done)
		return;
	iuid = (dlg_iuid_t*)(*param->param);
	dlg = dlg_get_by_iuid(iuid);
	if(dlg==0)
		return;

	unref = 0;
	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);
		dlg_set_ctx_iuid(dlg);
		goto done;
	}

	if (type==TMCB_RESPONSE_FWDED) {
		/* The state does not change, but the msg is mutable in this callback*/
		run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
		goto done;
	}

	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;

	next_state_dlg( dlg, event, &old_state, &new_state, &unref);
	dlg_run_event_route(dlg, (rpl==FAKED_REPLY)?NULL:rpl, old_state, new_state);

	if (new_state==DLG_STATE_EARLY) {
		run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
		if (old_state!=DLG_STATE_EARLY)
			if_update_stat(dlg_enable_stats, early_dlgs, 1);
		goto done;
	}

	if (new_state==DLG_STATE_CONFIRMED_NA &&
	old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) {
		LM_DBG("dialog %p confirmed (ACK pending)\n",dlg);

		 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");
				tag.s = 0;
				tag.len = 0;
			} 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;
				}
			}

			/* 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");
		 }

		/* 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 ( dlg_db_mode==DB_MODE_REALTIME )
			update_dialog_dbinfo(dlg);

		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);
		} else {
			/* dialog pointer inserted in timer list */
			dlg_ref(dlg, 1);
		}

		/* dialog confirmed (ACK pending) */
		run_dlg_callbacks( DLGCB_CONFIRMED_NA, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);

		if (old_state==DLG_STATE_EARLY)
			if_update_stat(dlg_enable_stats, early_dlgs, -1);

		if (unref) dlg_unref(dlg, unref);
		if_update_stat(dlg_enable_stats, active_dlgs, 1);
		goto done;
	}

	if ( new_state==DLG_STATE_DELETED
				&& (old_state==DLG_STATE_UNCONFIRMED
					|| old_state==DLG_STATE_EARLY) ) {
		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);
		if(dlg_wait_ack==1)
			dlg_set_tm_waitack(t, dlg);
		/* do unref */
		if (unref)
			dlg_unref(dlg, unref);
		if (old_state==DLG_STATE_EARLY)
			if_update_stat(dlg_enable_stats, early_dlgs, -1);

		if_update_stat(dlg_enable_stats, failed_dlgs, 1);

		goto done;
	}

	if (unref) dlg_unref(dlg, unref);

done:
	/* unref due to dlg_get_by_iuid() */
	dlg_release(dlg);
	return;
}
コード例 #3
0
ファイル: dlg_handlers.c プロジェクト: alexdowad/kamailio
/*!
 * \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)
{
	dlg_cell_t *dlg;
	dlg_iuid_t *iuid;
	str val, callid, ftag, ttag;
	int h_entry, h_id, new_state, old_state, unref, event, timeout, reset;
	unsigned int dir;
	int ret = 0;

	dlg = dlg_get_ctx_dialog();
	if (dlg!=NULL) {
		dlg_release(dlg);
		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 = dlg_lookup(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
					dlg_release(dlg);
					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);
					// lookup_dlg has incremented the ref count by 1
					dlg_release(dlg);

					// 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==0){
			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.iuid.h_entry = dlg->h_entry;
    _dlg_ctx.iuid.h_id = dlg->h_id;

	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 */
			if ( d_tmb.register_tmcb( req, 0, TMCB_RESPONSE_IN|TMCB_ON_FAILURE,
					dlg_onreply, (void*)iuid, dlg_iuid_sfree)<0 ) {
				LM_ERR("failed to register TMCB (3)\n");
				shm_free(iuid);
			}
			iuid = NULL;
		}
	}
	
	/* 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);

	CURR_DLG_ID = req->id;
	CURR_DLG_LIFETIME = (unsigned int)(time(0))-dlg->start_ts;
	CURR_DLG_STATUS = new_state;

	dlg_run_event_route(dlg, req, old_state, new_state);

	/* 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) {
		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);
			}
		}
	}

	if (new_state==DLG_STATE_CONFIRMED && old_state!=DLG_STATE_CONFIRMED)
		dlg_ka_add(dlg);

	/* 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' '%.*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) {
			LM_WARN("inconsitent dlg timer data 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 {
			/* one extra unref due to removal from timer list */
			unref++;
		}
		/* dialog terminated (BYE) */
        dlg_terminated( req, dlg, dir);

		dlg_unref(dlg, unref);

		_dlg_ctx.cpid = my_pid();
		_dlg_ctx.expect_t = 1;
		dlg_set_ctx_iuid(dlg);

		if_update_stat( dlg_enable_stats, active_dlgs, -1);
		goto done;
	}

	if ( (event==DLG_EVENT_REQ || event==DLG_EVENT_REQACK)
	&& (new_state==DLG_STATE_CONFIRMED || new_state==DLG_STATE_EARLY)) {

		timeout = get_dlg_timeout(req);
		if (timeout!=default_timeout) {
			dlg->lifetime = timeout;
		}
		reset = !((dlg->iflags & DLG_IFLAG_TIMER_NORESET) || dlg_timeout_noreset);

		if ((new_state!=DLG_STATE_EARLY) && (old_state!=DLG_STATE_CONFIRMED || reset)) {
			if (update_dlg_timer( &dlg->tl, dlg->lifetime )==-1) {
				LM_ERR("failed to update dialog lifetime\n");
			} else {
				dlg->dflags |= DLG_FLAG_CHANGED;
			}
		}
		if(event != DLG_EVENT_REQACK) {
			if(dlg_refresh_contacts(dlg, req, dir)!=0) {
				LM_ERR("contacts update failed\n");
			} else {
				dlg->dflags |= DLG_FLAG_CHANGED;
			}
			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);
		}

		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);

			if ( (event!=DLG_EVENT_REQACK) &&
					(dlg->cbs.types)&DLGCB_RESPONSE_WITHIN ) {
				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);
					}
				}
			}
		}
	}

	if(new_state==DLG_STATE_CONFIRMED && old_state==DLG_STATE_CONFIRMED_NA){
		dlg->dflags |= DLG_FLAG_CHANGED;
		if(dlg_db_mode == DB_MODE_REALTIME)
			update_dialog_dbinfo(dlg);
	}

done:
	dlg_release(dlg);
	return;
}
コード例 #4
0
ファイル: dlg_handlers.c プロジェクト: alexdowad/kamailio
/*!
 * \brief Timer function that removes expired dialogs, run timeout route
 * \param tl dialog timer list
 */
void dlg_ontimeout(struct dlg_tl *tl)
{
	dlg_cell_t *dlg;
	int new_state, old_state, unref;
	sip_msg_t *fmsg;
	void* timeout_cb = 0;

	/* get the dialog tl payload */
	dlg = ((struct dlg_cell*)((char *)(tl) -
			(unsigned long)(&((struct dlg_cell*)0)->tl)));

	/* mark dialog as expired */
	dlg->dflags |= DLG_FLAG_EXPIRED;

	if(dlg->state==DLG_STATE_CONFIRMED_NA
				|| dlg->state==DLG_STATE_CONFIRMED)
	{
		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_ref(dlg, 1);
				dlg_set_ctx_iuid(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_reset_ctx_iuid();
				exec_post_script_cb(fmsg, REQUEST_CB_TYPE);
				dlg_unref(dlg, 1);
			}
		}

		if(dlg->iflags&DLG_IFLAG_TIMEOUTBYE)
		{
			/* set the dialog context so that it's available in
			 * tm:local-request event route */
			dlg_set_ctx_iuid(dlg);
			if(dlg_bye_all(dlg, NULL)<0)
				dlg_unref(dlg, 1);
			dlg_reset_ctx_iuid();	

			dlg_unref(dlg, 1);
			if_update_stat(dlg_enable_stats, expired_dlgs, 1);
			return;
		}
	}

	next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref);
    /* used for computing duration for timed out acknowledged dialog */
	if (DLG_STATE_CONFIRMED == old_state) {
		timeout_cb = (void *)CONFIRMED_DIALOG_STATE;
	}	

	dlg_run_event_route(dlg, NULL, old_state, new_state);

	if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
		LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n",
			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);

		/* dialog timeout */
		run_dlg_callbacks( DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, timeout_cb);

		dlg_unref(dlg, unref+1);

		if_update_stat( dlg_enable_stats, expired_dlgs, 1);
		if_update_stat( dlg_enable_stats, active_dlgs, -1);
	} else {
		dlg_unref(dlg, 1);
	}

	return;
}
コード例 #5
0
ファイル: dlg_handlers.c プロジェクト: billyyh/kamailio
/*!
 * \brief Timer function that removes expired dialogs, run timeout route
 * \param tl dialog timer list
 */
void dlg_ontimeout(struct dlg_tl *tl)
{
	dlg_cell_t *dlg;
	int new_state, old_state, unref;
	sip_msg_t *fmsg;

	/* get the dialog tl payload */
	dlg = ((struct dlg_cell*)((char *)(tl) -
			(unsigned long)(&((struct dlg_cell*)0)->tl)));

	if(dlg->state==DLG_STATE_CONFIRMED_NA
				|| dlg->state==DLG_STATE_CONFIRMED)
	{
		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_ref(dlg, 1);
				dlg_set_ctx_iuid(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_reset_ctx_iuid();
				exec_post_script_cb(fmsg, REQUEST_CB_TYPE);
				dlg_unref(dlg, 1);
			}
		}

		if(dlg->iflags&DLG_IFLAG_TIMEOUTBYE)
		{
			dlg_bye_all(dlg, NULL);
			/* run event route for end of dlg */
			dlg_run_event_route(dlg, NULL, dlg->state, DLG_STATE_DELETED);
			dlg_unref(dlg, 1);
			if_update_stat(dlg_enable_stats, expired_dlgs, 1);
			return;
		}
	}

	next_state_dlg( dlg, DLG_EVENT_REQBYE, &old_state, &new_state, &unref);
	dlg_run_event_route(dlg, NULL, old_state, new_state);

	if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
		LM_WARN("timeout for dlg with CallID '%.*s' and tags '%.*s' '%.*s'\n",
			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);

		/* dialog timeout */
		run_dlg_callbacks( DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0);

		dlg_unref(dlg, unref+1);

		if_update_stat( dlg_enable_stats, expired_dlgs, 1);
		if_update_stat( dlg_enable_stats, active_dlgs, -1);
	} else {
		dlg_unref(dlg, 1);
	}

	return;
}