Explorar o código

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

Carsten Bock %!s(int64=3) %!d(string=hai) anos
pai
achega
26ef622ff3

+ 249 - 0
src/modules/dialog/dialog.c

@@ -123,6 +123,9 @@ str dlg_bridge_controller = str_init("sip:[email protected]");
 
 str dlg_bridge_contact = str_init("sip:[email protected]: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;

+ 2 - 0
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,

+ 1 - 0
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;

+ 277 - 3
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 @@ err:
 	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

+ 1 - 0
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

+ 2 - 2
src/modules/dialog/doc/dialog.xml

@@ -69,8 +69,8 @@
 		<holder>Voice Sistem SRL</holder>
 	</copyright>
 	<copyright>
-		<year>2011</year>
-		<holder>Carsten Bock, http://www.ng-voice.com</holder>
+		<year>2011, 2022</year>
+		<holder>ng-voice GmbH, Carsten Bock, http://www.ng-voice.com</holder>
 	</copyright>
 	</bookinfo>
 	<toc></toc>

+ 106 - 2
src/modules/dialog/doc/dialog_admin.xml

@@ -1468,7 +1468,7 @@ modparam("dialog", "timer_procs", 1)
 		</example>
 	</section>
 
-	<section>
+	<section id="dialog.p.enable_dmq">
 		<title><varname>enable_dmq</varname> (int)</title>
 		<para>
 			If set to 1, the dialog will be synced via dmq.
@@ -1666,6 +1666,47 @@ modparam("dialog", "keep_proxy_rr", 1)
 </programlisting>
 		</example>
 	</section>
+
+	<section id="dialog.p.bye_early_code">
+		<title><varname>bye_early_code</varname> (int)</title>
+		<para>
+			This parameter defines the reply-code being used for
+			dialogs being terminated in early stage (e.g. before
+			200 OK/ACK).
+		</para>
+		<emphasis>
+			Default value is <quote>480</quote>.
+		</emphasis>
+		<example>
+		<title>Set <varname>bye_early_code</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "bye_early_code", 503)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="dialog.p.bye_early_reason">
+		<title><varname>bye_early_reason</varname> (string)</title>
+		<para>
+			This parameter defines the reply-reason being used for
+			dialogs being terminated in early stage (e.g. before
+			200 OK/ACK).
+		</para>
+		<emphasis>
+			Default value is <quote>Temporarily Unavailable</quote>.
+		</emphasis>
+		<example>
+		<title>Set <varname>bye_early_reason</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "bye_early_reason", "Call terminated")
+...
+</programlisting>
+		</example>
+	</section>
+
 	</section>
 
 	<section>
@@ -1936,7 +1977,9 @@ redlg_setflag("1");
 		<function moreinfo="none">dlg_bye(side)</function>
 		</title>
 		<para>
-		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).
 		</para>
 		<para>Meaning of the parameters is as follows:</para>
 		<itemizedlist>
@@ -2444,6 +2487,67 @@ dlg_reset_property("timeout-noreset");
 </programlisting>
 		</example>
 	</section>
+
+	<section id="dialog.f.dlg_req_within">
+		<title>
+		<function moreinfo="none">dlg_req_within(side, method, [headers], [content_type, content])</function>
+		</title>
+		<para>
+		Sends a in-dialog SIP Request with method to a party of a dialog indicated by the side parameter.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+				<emphasis>side</emphasis> - where to send the request. It can be:
+				'caller', 'callee', or 'all' (send to both sides).
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+				<emphasis>method</emphasis> - Method of the request
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+				<emphasis>headers</emphasis> (optional) - additional headers to be added to the request.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+				<emphasis>content_type</emphasis> (optional) - Content-Type of the request body - will
+				be added as Content-Type Header.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+				<emphasis>content</emphasis> (optional) - Content to be sent as body.
+			</para>
+		</listitem>
+		</itemizedlist>		
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>dlg_req_within</function> usage</title>
+		<programlisting format="linespecific">
+...
+	# 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...");
+...
+		</programlisting>
+		</example>
+	</section>
+
 	</section>