From 26ef622ff3621c8a82c1c3560a32e8d735e30c4e Mon Sep 17 00:00:00 2001 From: Carsten Bock Date: Wed, 27 Apr 2022 11:33:31 +0200 Subject: [PATCH] dialog: Terminate dialogs in Early stage and add functionality to send messages within a dialog - This change adds the capability to termiante a dialog in early stage by either sending a SIP response to the A-Party or by sending a CANCEL to the B-Party - This change adds a function to send a request in-dialog from script (e.g. send INFO to a party to provide additional information or for example UPDATE a Media-Session in early stage, when certain conditions are met --- src/modules/dialog/dialog.c | 249 +++++++++++++++++++++ src/modules/dialog/dlg_handlers.c | 2 + src/modules/dialog/dlg_hash.h | 1 + src/modules/dialog/dlg_req_within.c | 280 +++++++++++++++++++++++- src/modules/dialog/dlg_req_within.h | 1 + src/modules/dialog/doc/dialog.xml | 4 +- src/modules/dialog/doc/dialog_admin.xml | 108 ++++++++- 7 files changed, 638 insertions(+), 7 deletions(-) diff --git a/src/modules/dialog/dialog.c b/src/modules/dialog/dialog.c index 08f0a858825..c70f87ed483 100644 --- a/src/modules/dialog/dialog.c +++ b/src/modules/dialog/dialog.c @@ -123,6 +123,9 @@ str dlg_bridge_controller = str_init("sip:controller@kamailio.org"); str dlg_bridge_contact = str_init("sip:controller@kamailio.org:5060"); +int bye_early_code = 480; +str bye_early_reason = str_init("Temporarily Unavailable"); + str ruri_pvar_param = str_init("$ru"); pv_elem_t * ruri_param_model = NULL; str empty_str = STR_NULL; @@ -199,6 +202,16 @@ static int w_dlg_remote_profile(sip_msg_t *msg, char *cmd, char *pname, char *pval, char *puid, char *expires); static int fixup_dlg_remote_profile(void** param, int param_no); +static int w_dlg_req_with_headers_and_content(struct sip_msg *, char *, char *, char* , char *, char *); +static int w_dlg_req_with_content(struct sip_msg *, char *, char *, char *, char *); +static int w_dlg_req_with_headers(struct sip_msg *, char *, char *, char *); +static int w_dlg_req_within(struct sip_msg *, char *, char *); + +static int fixup_dlg_dlg_req_within(void** , int ); +static int fixup_dlg_req_with_headers(void** , int ); +static int fixup_dlg_req_with_content(void** , int ); +static int fixup_dlg_req_with_headers_and_content(void** , int ); + static cmd_export_t cmds[]={ {"dlg_manage", (cmd_function)w_dlg_manage, 0,0, 0, REQUEST_ROUTE }, @@ -256,6 +269,14 @@ static cmd_export_t cmds[]={ 0, ANY_ROUTE }, {"dlg_db_load_extra", (cmd_function)w_dlg_db_load_extra, 0, 0, 0, ANY_ROUTE }, + {"dlg_req_within", (cmd_function)w_dlg_req_within, 2, fixup_dlg_dlg_req_within, + 0, ANY_ROUTE}, + {"dlg_req_within", (cmd_function)w_dlg_req_with_headers, 3, fixup_dlg_req_with_headers, + 0, ANY_ROUTE}, + {"dlg_req_within", (cmd_function)w_dlg_req_with_content, 4, fixup_dlg_req_with_content, + 0, ANY_ROUTE}, + {"dlg_req_within", (cmd_function)w_dlg_req_with_headers_and_content, 5, + fixup_dlg_req_with_headers_and_content, 0, ANY_ROUTE}, {"load_dlg", (cmd_function)load_dlg, 0, 0, 0, 0}, {0,0,0,0,0,0} @@ -329,6 +350,8 @@ static param_export_t mod_params[]={ { "h_id_step", PARAM_INT, &dlg_h_id_step }, { "keep_proxy_rr", INT_PARAM, &dlg_keep_proxy_rr }, { "dlg_filter_mode", INT_PARAM, &dlg_filter_mode }, + { "bye_early_code", PARAM_INT, &bye_early_code }, + { "bye_early_reason", PARAM_STR, &bye_early_reason }, { 0,0,0 } }; @@ -1099,6 +1122,232 @@ static int w_dlg_manage(struct sip_msg *msg, char *s1, char *s2) return dlg_manage(msg); } +static int fixup_dlg_dlg_req_within(void** param, int param_no) +{ + char *val; + int n = 0; + + if (param_no==1) { + val = (char*)*param; + if (strcasecmp(val,"all")==0) { + n = 0; + } else if (strcasecmp(val,"caller")==0) { + n = 1; + } else if (strcasecmp(val,"callee")==0) { + n = 2; + } else { + LM_ERR("invalid param \"%s\"\n", val); + return E_CFG; + } + pkg_free(*param); + *param=(void*)(long)n; + } else if (param_no==2) { + return fixup_spve_null(param, 1); + } else { + LM_ERR("called with parameter != 1\n"); + return E_BUG; + } + return 0; +} + +static int fixup_dlg_req_with_headers(void** param, int param_no) +{ + char *val; + int n = 0; + + if (param_no==1) { + val = (char*)*param; + if (strcasecmp(val,"all")==0) { + n = 0; + } else if (strcasecmp(val,"caller")==0) { + n = 1; + } else if (strcasecmp(val,"callee")==0) { + n = 2; + } else { + LM_ERR("invalid param \"%s\"\n", val); + return E_CFG; + } + pkg_free(*param); + *param=(void*)(long)n; + } else if (param_no==2) { + return fixup_spve_null(param, 1); + } else if (param_no==3) { + return fixup_spve_null(param, 1); + } else { + LM_ERR("called with parameter != 1\n"); + return E_BUG; + } + return 0; +} + + +static int fixup_dlg_req_with_content(void** param, int param_no) +{ + char *val; + int n = 0; + + if (param_no==1) { + val = (char*)*param; + if (strcasecmp(val,"all")==0) { + n = 0; + } else if (strcasecmp(val,"caller")==0) { + n = 1; + } else if (strcasecmp(val,"callee")==0) { + n = 2; + } else { + LM_ERR("invalid param \"%s\"\n", val); + return E_CFG; + } + pkg_free(*param); + *param=(void*)(long)n; + } else if (param_no==2) { + return fixup_spve_null(param, 1); + } else if (param_no==3) { + return fixup_spve_null(param, 1); + } else if (param_no==4) { + return fixup_spve_null(param, 1); + } else { + LM_ERR("called with parameter != 1\n"); + return E_BUG; + } + return 0; +} + +static int fixup_dlg_req_with_headers_and_content(void** param, int param_no) +{ + char *val; + int n = 0; + + if (param_no==1) { + val = (char*)*param; + if (strcasecmp(val,"all")==0) { + n = 0; + } else if (strcasecmp(val,"caller")==0) { + n = 1; + } else if (strcasecmp(val,"callee")==0) { + n = 2; + } else { + LM_ERR("invalid param \"%s\"\n", val); + return E_CFG; + } + pkg_free(*param); + *param=(void*)(long)n; + } else if (param_no==2) { + return fixup_spve_null(param, 1); + } else if (param_no==3) { + return fixup_spve_null(param, 1); + } else if (param_no==4) { + return fixup_spve_null(param, 1); + } else if (param_no==5) { + return fixup_spve_null(param, 1); + } else { + LM_ERR("called with parameter != 1\n"); + return E_BUG; + } + return 0; +} + +static int w_dlg_req_with_headers_and_content(struct sip_msg *msg, char *side, char *method, char* headers, char *content_type, char *content) +{ + dlg_cell_t *dlg = NULL; + int n; + str str_method = {0,0}; + str str_headers = {0,0}; + str str_content_type = {0,0}; + str str_content = {0,0}; + + dlg = dlg_get_ctx_dialog(); + if(dlg==NULL) + return -1; + + if(fixup_get_svalue(msg, (gparam_p)method, &str_method)!=0) + { + LM_ERR("unable to get Method\n"); + goto error; + } + if(str_method.s==NULL || str_method.len == 0) + { + LM_ERR("invalid Method parameter\n"); + goto error; + } + + if (headers) { + if(fixup_get_svalue(msg, (gparam_p)headers, &str_headers)!=0) + { + LM_ERR("unable to get Method\n"); + goto error; + } + if(str_headers.s==NULL || str_headers.len == 0) + { + LM_ERR("invalid Headers parameter\n"); + goto error; + } + } + if (content_type && content) { + if(fixup_get_svalue(msg, (gparam_p)content_type, &str_content_type)!=0) + { + LM_ERR("unable to get Content-Type\n"); + goto error; + } + if(str_content_type.s==NULL || str_content_type.len == 0) + { + LM_ERR("invalid Headers parameter\n"); + goto error; + } + if(fixup_get_svalue(msg, (gparam_p)content, &str_content)!=0) + { + LM_ERR("unable to get Content\n"); + goto error; + } + if(str_content.s==NULL || str_content.len == 0) + { + LM_ERR("invalid Content parameter\n"); + goto error; + } + } + + n = (int)(long)side; + if(n==1) + { + if(dlg_request_within(msg, dlg, DLG_CALLER_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0) + goto error; + goto done; + } else if(n==2) { + if(dlg_request_within(msg, dlg, DLG_CALLEE_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0) + goto error; + goto done; + } else { + if(dlg_request_within(msg, dlg, DLG_CALLER_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0) + goto error; + if(dlg_request_within(msg, dlg, DLG_CALLEE_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0) + goto error; + goto done; + } + +done: + dlg_release(dlg); + return 1; + +error: + dlg_release(dlg); + return -1; +} + +static int w_dlg_req_with_content(struct sip_msg *msg, char *side, char *method, char *content_type, char *content) +{ + return w_dlg_req_with_headers_and_content(msg, side, method, NULL, content_type, content); +} + +static int w_dlg_req_with_headers(struct sip_msg *msg, char *side, char *method, char *headers) +{ + return w_dlg_req_with_headers_and_content(msg, side, method, headers, NULL, NULL); +} + +static int w_dlg_req_within(struct sip_msg *msg, char *side, char *method) +{ + return w_dlg_req_with_headers_and_content(msg, side, method, NULL, NULL, NULL); +} + static int w_dlg_bye(struct sip_msg *msg, char *side, char *s2) { dlg_cell_t *dlg = NULL; diff --git a/src/modules/dialog/dlg_handlers.c b/src/modules/dialog/dlg_handlers.c index bf49981b4cd..39f1f83dd75 100644 --- a/src/modules/dialog/dlg_handlers.c +++ b/src/modules/dialog/dlg_handlers.c @@ -945,6 +945,8 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs) LM_ERR("failed to create new dialog\n"); return -1; } + // Store link to Transaction + dlg->t = t; /* save caller's tag, cseq, contact and record route*/ if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG, diff --git a/src/modules/dialog/dlg_hash.h b/src/modules/dialog/dlg_hash.h index 7367a1b7839..6913a5a3726 100644 --- a/src/modules/dialog/dlg_hash.h +++ b/src/modules/dialog/dlg_hash.h @@ -133,6 +133,7 @@ typedef struct dlg_cell struct dlg_head_cbl cbs; /*!< dialog callbacks */ struct dlg_profile_link *profile_links; /*!< dialog profiles */ struct dlg_var *vars; /*!< dialog variables */ + struct cell *t; /*!< Reference to Transaction of the INVITE */ unsigned int ka_src_counter; /*!< keepalive src (caller) counter */ unsigned int ka_dst_counter; /*!< keepalive dst (callee) counter */ } dlg_cell_t; diff --git a/src/modules/dialog/dlg_req_within.c b/src/modules/dialog/dlg_req_within.c index bd65d995d2e..9e412a950a6 100644 --- a/src/modules/dialog/dlg_req_within.c +++ b/src/modules/dialog/dlg_req_within.c @@ -40,6 +40,7 @@ #include "../../modules/tm/dlg.h" #include "../../modules/tm/tm_load.h" #include "../../core/counters.h" +#include "../../core/parser/contact/parse_contact.h" #include "dlg_timer.h" #include "dlg_hash.h" #include "dlg_handlers.h" @@ -55,6 +56,9 @@ extern str dlg_lreq_callee_headers; extern int dlg_ka_failed_limit; extern int dlg_filter_mode; +extern int bye_early_code; +extern str bye_early_reason; + /** * */ @@ -378,10 +382,24 @@ static inline int send_bye(struct dlg_cell * cell, int dir, str *hdrs) dlg_iuid_t *iuid = NULL; str lhdrs; - /* do not send BYE request for non-confirmed dialogs (not supported) */ + /* Send Cancel or final response for non-confirmed dialogs */ if (cell->state != DLG_STATE_CONFIRMED_NA && cell->state != DLG_STATE_CONFIRMED) { - LM_ERR("terminating non-confirmed dialogs not supported\n"); - return -1; + if (cell->t) { + if (dir == DLG_CALLER_LEG) { + if(d_tmb.t_reply(cell->t->uas.request, bye_early_code, bye_early_reason.s)< 0) { + LM_ERR("Failed to send reply to caller\n"); + return -1; + } + LM_DBG("\"%d %.*s\" sent to caller\n", bye_early_code, bye_early_reason.len, bye_early_reason.s); + } else { + d_tmb.cancel_all_uacs(cell->t, 0); + LM_DBG("CANCEL sent to callee(s)\n"); + } + return 0; + } else { + LM_ERR("terminating non-confirmed dialog not possible, transaction not longer available.\n"); + return -1; + } } /*verify direction*/ @@ -438,6 +456,262 @@ static inline int send_bye(struct dlg_cell * cell, int dir, str *hdrs) return -1; } +dlg_t * build_dlg_t_early(struct sip_msg *msg, struct dlg_cell * cell, int branch_id, str * rr_set){ + + dlg_t* td = NULL; + str cseq; + unsigned int loc_seq; + char nbuf[MAX_URI_SIZE]; + char dbuf[80]; + str nuri = STR_NULL; + str duri = STR_NULL; + size_t sz; + char *p; + unsigned int own_rr = 0, skip_recs = 0; + + if (cell->state != DLG_STATE_UNCONFIRMED && cell->state != DLG_STATE_EARLY) { + LM_ERR("Invalid state for build_dlg_state: %d (only working for unconfirmed or early dialogs)\n", cell->state); + goto error; + } + + if (msg == NULL || msg->first_line.type != SIP_REPLY) { + if (!cell->t) { + LM_ERR("No Transaction associated\n"); + goto error; + } + + if (branch_id <= 0 || branch_id > cell->t->nr_of_outgoings) { + LM_ERR("Invalid branch %d (%d branches in transaction)\n", branch_id, cell->t->nr_of_outgoings); + goto error; + } + msg = msg; + } + + if (!msg->contact && (parse_headers(msg,HDR_CONTACT_F,0)<0 + || !msg->contact)) { + LM_ERR("bad sip message or missing Contact hdr\n"); + goto error; + } + + if ( parse_contact(msg->contact)<0 || + ((contact_body_t *)msg->contact->parsed)->contacts==NULL) { + LM_ERR("bad Contact HDR\n"); + goto error; + } + + /*try to restore alias parameter if no route set */ + nuri.s = nbuf; + nuri.len = MAX_URI_SIZE; + duri.s = dbuf; + duri.len = 80; + if(uri_restore_rcv_alias(&((contact_body_t *)msg->contact->parsed)->contacts->uri, &nuri, &duri)<0) { + nuri.len = 0; + duri.len = 0; + } + + if(nuri.len>0 && duri.len>0) { + sz = sizeof(dlg_t) + (nuri.len+duri.len+2)*sizeof(char); + } else { + sz = sizeof(dlg_t); + } + + td = (dlg_t*)pkg_malloc(sz); + if(!td){ + LM_ERR("out of pkg memory\n"); + return NULL; + } + memset(td, 0, sz); + + /*route set*/ + if (msg->record_route) { + if (cell->t) { + LM_DBG("Transaction exists\n"); + own_rr = (cell->t->flags&TM_UAC_FLAG_R2)?2: + (cell->t->flags&TM_UAC_FLAG_RR)?1:0; + } else { + own_rr = (msg->flags&TM_UAC_FLAG_R2)?2: + (msg->flags&TM_UAC_FLAG_RR)?1:0; + } + skip_recs = cell->from_rr_nb + own_rr; + + LM_DBG("Skipping %u records, %u of myself\n", skip_recs, own_rr); + + if( print_rr_body(msg->record_route, rr_set, DLG_CALLEE_LEG, + &skip_recs) != 0 ){ + LM_ERR("failed to print route records \n"); + goto error; + } + LM_DBG("New Route-Set: %.*s\n", STR_FMT(rr_set)); + + if( parse_rr_body(rr_set->s, rr_set->len, + &td->route_set) !=0){ + LM_ERR("failed to parse route set\n"); + goto error; + } + } + + /*local sequence number*/ + cseq = cell->cseq[DLG_CALLER_LEG]; + + if (cseq.len > 0) { + LM_DBG("CSeq is %.*s\n", cseq.len, cseq.s); + if(str2int(&cseq, &loc_seq) != 0){ + LM_ERR("invalid cseq\n"); + goto error; + } + } else { + LM_DBG("CSeq not set yet, assuming 1\n"); + loc_seq = 1; + } + + /*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; + + LM_DBG("nuri: %.*s\n", STR_FMT(&nuri)); + LM_DBG("duri: %.*s\n", STR_FMT(&duri)); + + if(nuri.len>0 && duri.len>0) { + /* req uri */ + p = (char*)td + sizeof(dlg_t); + strncpy(p, nuri.s, nuri.len); + p[nuri.len] = '\0'; + td->rem_target.s = p; + td->rem_target.len = nuri.len; + /* dst uri */ + p += nuri.len + 1; + strncpy(p, duri.s, duri.len); + p[duri.len] = '\0'; + td->dst_uri.s = p; + td->dst_uri.len = duri.len; + } else { + td->rem_target = ((contact_body_t *)msg->contact->parsed)->contacts->uri; + } + + td->rem_uri = cell->from_uri; + td->loc_uri = cell->to_uri; + LM_DBG("rem_uri: %.*s\n", STR_FMT(&td->rem_uri)); + LM_DBG("loc_uri: %.*s\n", STR_FMT(&td->loc_uri)); + + LM_DBG("rem_target: %.*s\n", STR_FMT(&td->rem_target)); + LM_DBG("dst_uri: %.*s\n", STR_FMT(&td->dst_uri)); + + td->id.call_id = cell->callid; + td->id.rem_tag = cell->tag[DLG_CALLER_LEG]; + td->id.loc_tag = cell->tag[DLG_CALLEE_LEG]; + + td->state= DLG_EARLY; + td->send_sock = cell->bind_addr[DLG_CALLER_LEG]; + + return td; + +error: + LM_ERR("Error occured creating early dialog\n"); + free_tm_dlg(td); + return NULL; +} + +int dlg_request_within(struct sip_msg *msg, struct dlg_cell *dlg, int side, str * method, str * hdrs, str * content_type, str * content) +{ + uac_req_t uac_r; + dlg_t* dialog_info; + int result; + dlg_iuid_t *iuid = NULL; + char rr_set_s[MAX_URI_SIZE]; + str rr_set = {rr_set_s, 0}; + str allheaders = {0, 0}; + str content_type_hdr = {"Content-Type: ", 14}; + int idx = 0; + memset(rr_set_s, 0, 500); + + /* Special treatment for callee in early state*/ + if (dlg->state != DLG_STATE_CONFIRMED_NA && dlg->state != DLG_STATE_CONFIRMED && side == DLG_CALLEE_LEG) { + LM_DBG("Send request to callee in early state...\n"); + + if (dlg->t == NULL && d_tmb.t_gett) { + dlg->t = d_tmb.t_gett(); + if (dlg->t && dlg->t != T_UNDEFINED) + idx = dlg->t->nr_of_outgoings; + } + LM_DBG("Branch %i\n", idx); + + /*verify direction*/ + if ((dialog_info = build_dlg_t_early(msg, dlg, idx, &rr_set)) == 0){ + LM_ERR("failed to create dlg_t\n"); + goto err; + } + } else { + LM_DBG("Send request to caller or in confirmed state...\n"); + /*verify direction*/ + if ((dialog_info = build_dlg_t(dlg, side)) == 0){ + LM_ERR("failed to create dlg_t\n"); + goto err; + } + } + + LM_DBG("sending %.*s to %s\n", method->len, method->s, (side==DLG_CALLER_LEG)?"caller":"callee"); + + iuid = dlg_get_iuid_shm_clone(dlg); + if(iuid==NULL) + { + LM_ERR("failed to create dialog unique id clone\n"); + goto err; + } + + if (hdrs && hdrs->len > 0) { + LM_DBG("Extra headers: %.*s\n", STR_FMT(hdrs)); + allheaders.len += hdrs->len; + } + + if (content_type && content_type->s && content && content->s) { + LM_DBG("Content-Type: %.*s\n", STR_FMT(content_type)); + allheaders.len += content_type_hdr.len + content_type->len + 2; + } + if (allheaders.len > 0) { + allheaders.s = (char*)pkg_malloc(allheaders.len); + if (allheaders.s == NULL) { + PKG_MEM_ERROR; + goto err; + } + allheaders.len = 0; + if (hdrs && hdrs->len > 0) { + memcpy(allheaders.s, hdrs->s, hdrs->len); + allheaders.len += hdrs->len; + } + if (content_type && content_type->s && content && content->s) { + memcpy(allheaders.s + allheaders.len, content_type_hdr.s, content_type_hdr.len); + allheaders.len += content_type_hdr.len; + memcpy(allheaders.s + allheaders.len, content_type->s, content_type->len); + allheaders.len += content_type->len; + memcpy(allheaders.s + allheaders.len, "\r\n", 2); + allheaders.len += 2; + } + LM_DBG("All headers: %.*s\n", STR_FMT(&allheaders)); + } + + set_uac_req(&uac_r, method, allheaders.len?&allheaders:NULL, (content && content->len)?content:NULL, dialog_info, TMCB_LOCAL_COMPLETED, + bye_reply_cb, (void*)iuid); + + result = d_tmb.t_request_within(&uac_r); + + if (allheaders.s) + pkg_free(allheaders.s); + + if(result < 0){ + LM_ERR("failed to send request\n"); + goto err; + } + + free_tm_dlg(dialog_info); + + LM_DBG("%.*s sent to %s\n", method->len, method->s, (side==DLG_CALLER_LEG)?"caller":"callee"); + + return 0; +err: + if(dialog_info) + free_tm_dlg(dialog_info); + return -1; +} /* send keep-alive * dlg - pointer to a struct dlg_cell diff --git a/src/modules/dialog/dlg_req_within.h b/src/modules/dialog/dlg_req_within.h index 420928ea264..5acf55d8492 100644 --- a/src/modules/dialog/dlg_req_within.h +++ b/src/modules/dialog/dlg_req_within.h @@ -52,5 +52,6 @@ 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 dlg_send_ka(dlg_cell_t *dlg, int dir); +int dlg_request_within(struct sip_msg *msg, struct dlg_cell *dlg, int side, str * method, str * hdrs, str * content_type, str * content); #endif diff --git a/src/modules/dialog/doc/dialog.xml b/src/modules/dialog/doc/dialog.xml index 0f4ffd46e5c..8bb460f9b8b 100644 --- a/src/modules/dialog/doc/dialog.xml +++ b/src/modules/dialog/doc/dialog.xml @@ -69,8 +69,8 @@ Voice Sistem SRL - 2011 - Carsten Bock, http://www.ng-voice.com + 2011, 2022 + ng-voice GmbH, Carsten Bock, http://www.ng-voice.com diff --git a/src/modules/dialog/doc/dialog_admin.xml b/src/modules/dialog/doc/dialog_admin.xml index f8cbfb2e9c7..3de68fba389 100644 --- a/src/modules/dialog/doc/dialog_admin.xml +++ b/src/modules/dialog/doc/dialog_admin.xml @@ -1468,7 +1468,7 @@ modparam("dialog", "timer_procs", 1) -
+
<varname>enable_dmq</varname> (int) If set to 1, the dialog will be synced via dmq. @@ -1666,6 +1666,47 @@ modparam("dialog", "keep_proxy_rr", 1)
+ +
+ <varname>bye_early_code</varname> (int) + + This parameter defines the reply-code being used for + dialogs being terminated in early stage (e.g. before + 200 OK/ACK). + + + Default value is 480. + + + Set <varname>bye_early_code</varname> parameter + +... +modparam("dialog", "bye_early_code", 503) +... + + +
+ +
+ <varname>bye_early_reason</varname> (string) + + This parameter defines the reply-reason being used for + dialogs being terminated in early stage (e.g. before + 200 OK/ACK). + + + Default value is Temporarily Unavailable. + + + Set <varname>bye_early_reason</varname> parameter + +... +modparam("dialog", "bye_early_reason", "Call terminated") +... + + +
+
@@ -1936,7 +1977,9 @@ redlg_setflag("1"); dlg_bye(side) - Send BYE to both parties of a dialog. + Send BYE to parties of a dialog or - if in early stage - a CANCEL to the + B-Party and a SIP response to the A-Party (as defined in bye_early_code / + bye_early_reason). Meaning of the parameters is as follows: @@ -2444,6 +2487,67 @@ dlg_reset_property("timeout-noreset");
+ +
+ + <function moreinfo="none">dlg_req_within(side, method, [headers], [content_type, content])</function> + + + Sends a in-dialog SIP Request with method to a party of a dialog indicated by the side parameter. + + Meaning of the parameters is as follows: + + + + side - where to send the request. It can be: + 'caller', 'callee', or 'all' (send to both sides). + + + + + method - Method of the request + + + + + headers (optional) - additional headers to be added to the request. + + + + + content_type (optional) - Content-Type of the request body - will + be added as Content-Type Header. + + + + + content (optional) - Content to be sent as body. + + + + + This function can be used from ANY_ROUTE. + + + <function>dlg_req_within</function> usage + +... + # Send a simple request: + dlg_req_within("all", "OPTIONS"); +... + # Send a simple request with extra headers: + dlg_req_within("caller", "OPTIONS", "X-Info: Bandwidth granted\r\nX-Info-2: Go ahead\r\n"); +... + # Send a simple request with body: + dlg_req_within("caller", "UPDATE", "application/sdp", "...some SDP..."); +... + # Send a simple request with extra headers and body: + dlg_req_within("callee", "INFO", "X-Info: Bandwidth granted\r\n", "application/sdp", "...some SDP..."); +... + + +
+