/*! * \brief Terminate all or 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_dlgs(struct mi_root *cmd_tree, void *param ) { struct mi_root* rpl_tree= NULL; struct dlg_cell* dlg = NULL; str headers = {0, 0}; rpl_tree = process_mi_params( cmd_tree, &dlg); if (rpl_tree) /* param error */ return rpl_tree; if (dlg==NULL) return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM)); rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK)); if (rpl_tree==0) return 0; if (dlg_bye_all(dlg, &headers)!=0) goto error; return rpl_tree; error: free_mi_tree(rpl_tree); return NULL; }
/*! * \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; }
/* 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: h_entry, h_id of the requested dialog*/ struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param ){ struct mi_node* node; unsigned int h_entry, h_id; struct dlg_cell * dlg = NULL; str mi_extra_hdrs = {NULL,0}; int status, msg_len; char *msg; if( d_table ==NULL) goto end; node = cmd_tree->node.kids; h_entry = h_id = 0; if (node==NULL || node->next==NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); if (!node->value.s|| !node->value.len|| strno2int(&node->value,&h_entry)<0) goto error; node = node->next; if ( !node->value.s || !node->value.len || strno2int(&node->value,&h_id)<0) goto error; if (node->next) { node = node->next; if (node->value.len && node->value.s) mi_extra_hdrs = node->value; } LM_DBG("h_entry %u h_id %u\n", h_entry, h_id); dlg = dlg_lookup(h_entry, h_id); // lookup_dlg has incremented the reference count if(dlg){ if(dlg_bye_all(dlg,(mi_extra_hdrs.len>0)?&mi_extra_hdrs:NULL)<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; } dlg_release(dlg); 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); }
/*! * \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; }