Browse code

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 authored on 27/04/2022 09:33:31
Showing 7 changed files
... ...
@@ -123,6 +123,9 @@ str dlg_bridge_controller = str_init("sip:controller@kamailio.org");
123 123
 
124 124
 str dlg_bridge_contact = str_init("sip:controller@kamailio.org:5060");
125 125
 
126
+int bye_early_code = 480;
127
+str bye_early_reason = str_init("Temporarily Unavailable");
128
+
126 129
 str ruri_pvar_param = str_init("$ru");
127 130
 pv_elem_t * ruri_param_model = NULL;
128 131
 str empty_str = STR_NULL;
... ...
@@ -199,6 +202,16 @@ static int w_dlg_remote_profile(sip_msg_t *msg, char *cmd, char *pname,
199 202
 		char *pval, char *puid, char *expires);
200 203
 static int fixup_dlg_remote_profile(void** param, int param_no);
201 204
 
205
+static int w_dlg_req_with_headers_and_content(struct sip_msg *, char *, char *, char* , char *, char *);
206
+static int w_dlg_req_with_content(struct sip_msg *, char *, char *, char *, char *);
207
+static int w_dlg_req_with_headers(struct sip_msg *, char *, char *, char *);
208
+static int w_dlg_req_within(struct sip_msg *, char *, char *);
209
+
210
+static int fixup_dlg_dlg_req_within(void** , int );
211
+static int fixup_dlg_req_with_headers(void** , int );
212
+static int fixup_dlg_req_with_content(void** , int );
213
+static int fixup_dlg_req_with_headers_and_content(void** , int );
214
+
202 215
 static cmd_export_t cmds[]={
203 216
 	{"dlg_manage", (cmd_function)w_dlg_manage,            0,0,
204 217
 			0, REQUEST_ROUTE },
... ...
@@ -256,6 +269,14 @@ static cmd_export_t cmds[]={
256 269
 			0, ANY_ROUTE },
257 270
 	{"dlg_db_load_extra", (cmd_function)w_dlg_db_load_extra, 0, 0,
258 271
 			0, ANY_ROUTE },
272
+	{"dlg_req_within",  (cmd_function)w_dlg_req_within, 2, fixup_dlg_dlg_req_within,
273
+			0, ANY_ROUTE},
274
+	{"dlg_req_within",  (cmd_function)w_dlg_req_with_headers, 3, fixup_dlg_req_with_headers,
275
+			0, ANY_ROUTE},
276
+	{"dlg_req_within",  (cmd_function)w_dlg_req_with_content, 4, fixup_dlg_req_with_content,
277
+			0, ANY_ROUTE},
278
+	{"dlg_req_within",  (cmd_function)w_dlg_req_with_headers_and_content, 5,
279
+			fixup_dlg_req_with_headers_and_content, 0, ANY_ROUTE},
259 280
 
260 281
 	{"load_dlg",  (cmd_function)load_dlg,   0, 0, 0, 0},
261 282
 	{0,0,0,0,0,0}
... ...
@@ -329,6 +350,8 @@ static param_export_t mod_params[]={
329 350
 	{ "h_id_step",             PARAM_INT, &dlg_h_id_step            },
330 351
 	{ "keep_proxy_rr",         INT_PARAM, &dlg_keep_proxy_rr        },
331 352
 	{ "dlg_filter_mode",       INT_PARAM, &dlg_filter_mode          },
353
+	{ "bye_early_code",        PARAM_INT, &bye_early_code           },
354
+	{ "bye_early_reason",      PARAM_STR, &bye_early_reason         },
332 355
 	{ 0,0,0 }
333 356
 };
334 357
 
... ...
@@ -1099,6 +1122,232 @@ static int w_dlg_manage(struct sip_msg *msg, char *s1, char *s2)
1099 1122
 	return dlg_manage(msg);
1100 1123
 }
1101 1124
 
1125
+static int fixup_dlg_dlg_req_within(void** param, int param_no)
1126
+{
1127
+	char *val;
1128
+	int n = 0;
1129
+
1130
+	if (param_no==1) {
1131
+		val = (char*)*param;
1132
+		if (strcasecmp(val,"all")==0) {
1133
+			n = 0;
1134
+		} else if (strcasecmp(val,"caller")==0) {
1135
+			n = 1;
1136
+		} else if (strcasecmp(val,"callee")==0) {
1137
+			n = 2;
1138
+		} else {
1139
+			LM_ERR("invalid param \"%s\"\n", val);
1140
+			return E_CFG;
1141
+		}
1142
+		pkg_free(*param);
1143
+		*param=(void*)(long)n;
1144
+	} else if (param_no==2) {
1145
+		return fixup_spve_null(param, 1);
1146
+	} else {
1147
+		LM_ERR("called with parameter != 1\n");
1148
+		return E_BUG;
1149
+	}
1150
+	return 0;
1151
+}
1152
+
1153
+static int fixup_dlg_req_with_headers(void** param, int param_no)
1154
+{
1155
+	char *val;
1156
+	int n = 0;
1157
+
1158
+	if (param_no==1) {
1159
+		val = (char*)*param;
1160
+		if (strcasecmp(val,"all")==0) {
1161
+			n = 0;
1162
+		} else if (strcasecmp(val,"caller")==0) {
1163
+			n = 1;
1164
+		} else if (strcasecmp(val,"callee")==0) {
1165
+			n = 2;
1166
+		} else {
1167
+			LM_ERR("invalid param \"%s\"\n", val);
1168
+			return E_CFG;
1169
+		}
1170
+		pkg_free(*param);
1171
+		*param=(void*)(long)n;
1172
+	} else if (param_no==2) {
1173
+		return fixup_spve_null(param, 1);
1174
+	} else if (param_no==3) {
1175
+		return fixup_spve_null(param, 1);
1176
+	} else {
1177
+		LM_ERR("called with parameter != 1\n");
1178
+		return E_BUG;
1179
+	}
1180
+	return 0;
1181
+}
1182
+
1183
+
1184
+static int fixup_dlg_req_with_content(void** param, int param_no)
1185
+{
1186
+	char *val;
1187
+	int n = 0;
1188
+
1189
+	if (param_no==1) {
1190
+		val = (char*)*param;
1191
+		if (strcasecmp(val,"all")==0) {
1192
+			n = 0;
1193
+		} else if (strcasecmp(val,"caller")==0) {
1194
+			n = 1;
1195
+		} else if (strcasecmp(val,"callee")==0) {
1196
+			n = 2;
1197
+		} else {
1198
+			LM_ERR("invalid param \"%s\"\n", val);
1199
+			return E_CFG;
1200
+		}
1201
+		pkg_free(*param);
1202
+		*param=(void*)(long)n;
1203
+	} else if (param_no==2) {
1204
+		return fixup_spve_null(param, 1);
1205
+	} else if (param_no==3) {
1206
+		return fixup_spve_null(param, 1);
1207
+	} else if (param_no==4) {
1208
+		return fixup_spve_null(param, 1);
1209
+	} else {
1210
+		LM_ERR("called with parameter != 1\n");
1211
+		return E_BUG;
1212
+	}
1213
+	return 0;
1214
+}
1215
+
1216
+static int fixup_dlg_req_with_headers_and_content(void** param, int param_no)
1217
+{
1218
+	char *val;
1219
+	int n = 0;
1220
+
1221
+	if (param_no==1) {
1222
+		val = (char*)*param;
1223
+		if (strcasecmp(val,"all")==0) {
1224
+			n = 0;
1225
+		} else if (strcasecmp(val,"caller")==0) {
1226
+			n = 1;
1227
+		} else if (strcasecmp(val,"callee")==0) {
1228
+			n = 2;
1229
+		} else {
1230
+			LM_ERR("invalid param \"%s\"\n", val);
1231
+			return E_CFG;
1232
+		}
1233
+		pkg_free(*param);
1234
+		*param=(void*)(long)n;
1235
+	} else if (param_no==2) {
1236
+		return fixup_spve_null(param, 1);
1237
+	} else if (param_no==3) {
1238
+		return fixup_spve_null(param, 1);
1239
+	} else if (param_no==4) {
1240
+		return fixup_spve_null(param, 1);
1241
+	} else if (param_no==5) {
1242
+		return fixup_spve_null(param, 1);
1243
+	} else {
1244
+		LM_ERR("called with parameter != 1\n");
1245
+		return E_BUG;
1246
+	}
1247
+	return 0;
1248
+}
1249
+
1250
+static int w_dlg_req_with_headers_and_content(struct sip_msg *msg, char *side, char *method, char* headers, char *content_type, char *content)
1251
+{
1252
+	dlg_cell_t *dlg = NULL;
1253
+	int n;
1254
+	str str_method = {0,0};
1255
+	str str_headers = {0,0};
1256
+	str str_content_type = {0,0};
1257
+	str str_content = {0,0};
1258
+
1259
+	dlg = dlg_get_ctx_dialog();
1260
+	if(dlg==NULL)
1261
+		return -1;
1262
+
1263
+	if(fixup_get_svalue(msg, (gparam_p)method, &str_method)!=0)
1264
+	{
1265
+		LM_ERR("unable to get Method\n");
1266
+		goto error;
1267
+	}
1268
+	if(str_method.s==NULL || str_method.len == 0)
1269
+	{
1270
+		LM_ERR("invalid Method parameter\n");
1271
+		goto error;
1272
+	}
1273
+
1274
+	if (headers) {
1275
+		if(fixup_get_svalue(msg, (gparam_p)headers, &str_headers)!=0)
1276
+		{
1277
+			LM_ERR("unable to get Method\n");
1278
+			goto error;
1279
+		}
1280
+		if(str_headers.s==NULL || str_headers.len == 0)
1281
+		{
1282
+			LM_ERR("invalid Headers parameter\n");
1283
+			goto error;
1284
+		}		
1285
+	}
1286
+	if (content_type && content) {
1287
+		if(fixup_get_svalue(msg, (gparam_p)content_type, &str_content_type)!=0)
1288
+		{
1289
+			LM_ERR("unable to get Content-Type\n");
1290
+			goto error;
1291
+		}
1292
+		if(str_content_type.s==NULL || str_content_type.len == 0)
1293
+		{
1294
+			LM_ERR("invalid Headers parameter\n");
1295
+			goto error;
1296
+		}		
1297
+		if(fixup_get_svalue(msg, (gparam_p)content, &str_content)!=0)
1298
+		{
1299
+			LM_ERR("unable to get Content\n");
1300
+			goto error;
1301
+		}
1302
+		if(str_content.s==NULL || str_content.len == 0)
1303
+		{
1304
+			LM_ERR("invalid Content parameter\n");
1305
+			goto error;
1306
+		}		
1307
+	}
1308
+	
1309
+	n = (int)(long)side;
1310
+	if(n==1)
1311
+	{
1312
+		if(dlg_request_within(msg, dlg, DLG_CALLER_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0)
1313
+			goto error;
1314
+		goto done;
1315
+	} else if(n==2) {
1316
+		if(dlg_request_within(msg, dlg, DLG_CALLEE_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0)
1317
+			goto error;
1318
+		goto done;
1319
+	} else {
1320
+		if(dlg_request_within(msg, dlg, DLG_CALLER_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0)
1321
+			goto error;
1322
+		if(dlg_request_within(msg, dlg, DLG_CALLEE_LEG, &str_method, &str_headers, &str_content_type, &str_content)!=0)
1323
+			goto error;
1324
+		goto done;
1325
+	}
1326
+
1327
+done:
1328
+	dlg_release(dlg);
1329
+	return 1;
1330
+
1331
+error:
1332
+	dlg_release(dlg);
1333
+	return -1;		
1334
+}
1335
+
1336
+static int w_dlg_req_with_content(struct sip_msg *msg, char *side, char *method, char *content_type, char *content)
1337
+{
1338
+	return w_dlg_req_with_headers_and_content(msg, side, method, NULL, content_type, content);
1339
+}
1340
+
1341
+static int w_dlg_req_with_headers(struct sip_msg *msg, char *side, char *method, char *headers)
1342
+{
1343
+	return w_dlg_req_with_headers_and_content(msg, side, method, headers, NULL, NULL);
1344
+}
1345
+
1346
+static int w_dlg_req_within(struct sip_msg *msg, char *side, char *method)
1347
+{
1348
+	return w_dlg_req_with_headers_and_content(msg, side, method, NULL, NULL, NULL);
1349
+}
1350
+
1102 1351
 static int w_dlg_bye(struct sip_msg *msg, char *side, char *s2)
1103 1352
 {
1104 1353
 	dlg_cell_t *dlg = NULL;
... ...
@@ -945,6 +945,8 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
945 945
 		LM_ERR("failed to create new dialog\n");
946 946
 		return -1;
947 947
 	}
948
+	// Store link to Transaction
949
+	dlg->t = t;
948 950
 
949 951
 	/* save caller's tag, cseq, contact and record route*/
950 952
 	if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
... ...
@@ -133,6 +133,7 @@ typedef struct dlg_cell
133 133
 	struct dlg_head_cbl  cbs;		/*!< dialog callbacks */
134 134
 	struct dlg_profile_link *profile_links; /*!< dialog profiles */
135 135
 	struct dlg_var       *vars;		/*!< dialog variables */
136
+	struct cell          *t;            /*!< Reference to Transaction of the INVITE */
136 137
 	unsigned int         ka_src_counter;	/*!< keepalive src (caller) counter */
137 138
 	unsigned int         ka_dst_counter;	/*!< keepalive dst (callee) counter */
138 139
 } dlg_cell_t;
... ...
@@ -40,6 +40,7 @@
40 40
 #include "../../modules/tm/dlg.h"
41 41
 #include "../../modules/tm/tm_load.h"
42 42
 #include "../../core/counters.h"
43
+#include "../../core/parser/contact/parse_contact.h"
43 44
 #include "dlg_timer.h"
44 45
 #include "dlg_hash.h"
45 46
 #include "dlg_handlers.h"
... ...
@@ -55,6 +56,9 @@ extern str dlg_lreq_callee_headers;
55 56
 extern int dlg_ka_failed_limit;
56 57
 extern int dlg_filter_mode;
57 58
 
59
+extern int bye_early_code;
60
+extern str bye_early_reason;
61
+
58 62
 /**
59 63
  *
60 64
  */
... ...
@@ -378,10 +382,24 @@ static inline int send_bye(struct dlg_cell * cell, int dir, str *hdrs)
378 382
 	dlg_iuid_t *iuid = NULL;
379 383
 	str lhdrs;
380 384
 
381
-	/* do not send BYE request for non-confirmed dialogs (not supported) */
385
+	/* Send Cancel or final response for non-confirmed dialogs */
382 386
 	if (cell->state != DLG_STATE_CONFIRMED_NA && cell->state != DLG_STATE_CONFIRMED) {
383
-		LM_ERR("terminating non-confirmed dialogs not supported\n");
384
-		return -1;
387
+		if (cell->t) {
388
+			if (dir == DLG_CALLER_LEG) {
389
+				if(d_tmb.t_reply(cell->t->uas.request, bye_early_code, bye_early_reason.s)< 0) {
390
+					LM_ERR("Failed to send reply to caller\n");
391
+					return -1;
392
+				}
393
+				LM_DBG("\"%d %.*s\" sent to caller\n", bye_early_code, bye_early_reason.len, bye_early_reason.s);
394
+			} else {
395
+				d_tmb.cancel_all_uacs(cell->t, 0);
396
+				LM_DBG("CANCEL sent to callee(s)\n");
397
+			}
398
+			return 0;
399
+		} else {
400
+			LM_ERR("terminating non-confirmed dialog not possible, transaction not longer available.\n");
401
+			return -1;
402
+		}
385 403
 	}
386 404
 
387 405
 	/*verify direction*/
... ...
@@ -438,6 +456,262 @@ err:
438 456
 	return -1;
439 457
 }
440 458
 
459
+dlg_t * build_dlg_t_early(struct sip_msg *msg, struct dlg_cell * cell, int branch_id, str * rr_set){
460
+
461
+	dlg_t* td = NULL;
462
+	str cseq;
463
+	unsigned int loc_seq;
464
+	char nbuf[MAX_URI_SIZE];
465
+	char dbuf[80];
466
+	str nuri = STR_NULL;
467
+	str duri = STR_NULL;
468
+	size_t sz;
469
+	char *p;
470
+	unsigned int own_rr = 0, skip_recs = 0;
471
+
472
+	if (cell->state != DLG_STATE_UNCONFIRMED && cell->state != DLG_STATE_EARLY) {
473
+		LM_ERR("Invalid state for build_dlg_state: %d (only working for unconfirmed or early dialogs)\n", cell->state);
474
+		goto error;
475
+	}
476
+
477
+	if (msg == NULL || msg->first_line.type != SIP_REPLY) {
478
+		if (!cell->t) {
479
+			LM_ERR("No Transaction associated\n");
480
+			goto error;
481
+		}
482
+
483
+		if (branch_id <= 0 || branch_id > cell->t->nr_of_outgoings) {
484
+			LM_ERR("Invalid branch %d (%d branches in transaction)\n", branch_id, cell->t->nr_of_outgoings);
485
+			goto error;
486
+		}
487
+		msg = msg;
488
+	}
489
+
490
+	if (!msg->contact && (parse_headers(msg,HDR_CONTACT_F,0)<0 
491
+	   || !msg->contact)) {
492
+		LM_ERR("bad sip message or missing Contact hdr\n");
493
+		goto error;
494
+	}
495
+
496
+	if ( parse_contact(msg->contact)<0 ||
497
+	((contact_body_t *)msg->contact->parsed)->contacts==NULL) {
498
+		LM_ERR("bad Contact HDR\n");
499
+		goto error;
500
+	}
501
+
502
+	/*try to restore alias parameter if no route set */
503
+	nuri.s = nbuf;
504
+	nuri.len = MAX_URI_SIZE;
505
+	duri.s = dbuf;
506
+	duri.len = 80;
507
+	if(uri_restore_rcv_alias(&((contact_body_t *)msg->contact->parsed)->contacts->uri, &nuri, &duri)<0) {
508
+		nuri.len = 0;
509
+		duri.len = 0;
510
+	}
511
+
512
+	if(nuri.len>0 && duri.len>0) {
513
+		sz = sizeof(dlg_t) + (nuri.len+duri.len+2)*sizeof(char);
514
+	} else {
515
+		sz = sizeof(dlg_t);
516
+	}
517
+
518
+	td = (dlg_t*)pkg_malloc(sz);
519
+	if(!td){
520
+		LM_ERR("out of pkg memory\n");
521
+		return NULL;
522
+	}
523
+	memset(td, 0, sz);
524
+
525
+	/*route set*/
526
+	if (msg->record_route) {
527
+		if (cell->t) {
528
+			LM_DBG("Transaction exists\n");
529
+			own_rr = (cell->t->flags&TM_UAC_FLAG_R2)?2:
530
+			   (cell->t->flags&TM_UAC_FLAG_RR)?1:0;	
531
+		} else {
532
+			own_rr = (msg->flags&TM_UAC_FLAG_R2)?2:
533
+			   (msg->flags&TM_UAC_FLAG_RR)?1:0;
534
+		}
535
+		skip_recs = cell->from_rr_nb + own_rr;
536
+
537
+		LM_DBG("Skipping %u records, %u of myself\n", skip_recs, own_rr);
538
+
539
+		if( print_rr_body(msg->record_route, rr_set, DLG_CALLEE_LEG,
540
+							&skip_recs) != 0 ){
541
+			LM_ERR("failed to print route records \n");
542
+			goto error;
543
+		}
544
+		LM_DBG("New Route-Set: %.*s\n", STR_FMT(rr_set));
545
+
546
+		if( parse_rr_body(rr_set->s, rr_set->len,
547
+						&td->route_set) !=0){
548
+		 	LM_ERR("failed to parse route set\n");
549
+			goto error;
550
+		}
551
+	}
552
+
553
+	/*local sequence number*/
554
+	cseq = cell->cseq[DLG_CALLER_LEG];
555
+
556
+	if (cseq.len > 0) {
557
+		LM_DBG("CSeq is %.*s\n", cseq.len, cseq.s);
558
+		if(str2int(&cseq, &loc_seq) != 0){
559
+			LM_ERR("invalid cseq\n");
560
+			goto error;
561
+		}
562
+	} else {
563
+		LM_DBG("CSeq not set yet, assuming 1\n");
564
+		loc_seq = 1;
565
+	}
566
+
567
+	/*we don not increase here the cseq as this will be done by TM*/
568
+	td->loc_seq.value = loc_seq;
569
+	td->loc_seq.is_set = 1;
570
+
571
+	LM_DBG("nuri: %.*s\n", STR_FMT(&nuri));
572
+	LM_DBG("duri: %.*s\n", STR_FMT(&duri));
573
+
574
+	if(nuri.len>0 && duri.len>0) {
575
+		/* req uri */
576
+		p = (char*)td + sizeof(dlg_t);
577
+		strncpy(p, nuri.s, nuri.len);
578
+		p[nuri.len] = '\0';
579
+		td->rem_target.s = p;
580
+		td->rem_target.len = nuri.len;
581
+		/* dst uri */
582
+		p += nuri.len + 1;
583
+		strncpy(p, duri.s, duri.len);
584
+		p[duri.len] = '\0';
585
+		td->dst_uri.s = p;
586
+		td->dst_uri.len = duri.len;
587
+	} else {
588
+		td->rem_target = ((contact_body_t *)msg->contact->parsed)->contacts->uri;
589
+	}
590
+
591
+	td->rem_uri	= cell->from_uri;
592
+	td->loc_uri	= cell->to_uri;
593
+	LM_DBG("rem_uri: %.*s\n", STR_FMT(&td->rem_uri));
594
+	LM_DBG("loc_uri: %.*s\n", STR_FMT(&td->loc_uri));
595
+
596
+	LM_DBG("rem_target: %.*s\n", STR_FMT(&td->rem_target));
597
+	LM_DBG("dst_uri: %.*s\n", STR_FMT(&td->dst_uri));
598
+
599
+	td->id.call_id = cell->callid;
600
+	td->id.rem_tag = cell->tag[DLG_CALLER_LEG];
601
+	td->id.loc_tag = cell->tag[DLG_CALLEE_LEG];
602
+
603
+	td->state= DLG_EARLY;
604
+	td->send_sock = cell->bind_addr[DLG_CALLER_LEG];
605
+
606
+	return td;
607
+
608
+error:
609
+	LM_ERR("Error occured creating early dialog\n");	
610
+	free_tm_dlg(td);
611
+	return NULL;
612
+}
613
+
614
+int dlg_request_within(struct sip_msg *msg, struct dlg_cell *dlg, int side, str * method, str * hdrs, str * content_type, str * content)
615
+{
616
+	uac_req_t uac_r;
617
+	dlg_t* dialog_info;
618
+	int result;
619
+	dlg_iuid_t *iuid = NULL;
620
+	char rr_set_s[MAX_URI_SIZE];
621
+	str rr_set = {rr_set_s, 0};
622
+	str allheaders = {0, 0};
623
+	str content_type_hdr = {"Content-Type: ", 14};
624
+	int idx = 0;
625
+	memset(rr_set_s, 0, 500);
626
+
627
+	/* Special treatment for callee in early state*/
628
+	if (dlg->state != DLG_STATE_CONFIRMED_NA && dlg->state != DLG_STATE_CONFIRMED && side == DLG_CALLEE_LEG) {
629
+		LM_DBG("Send request to callee in early state...\n");
630
+
631
+		if (dlg->t == NULL && d_tmb.t_gett) {
632
+			dlg->t = d_tmb.t_gett();
633
+			if (dlg->t && dlg->t != T_UNDEFINED)
634
+				idx = dlg->t->nr_of_outgoings;
635
+		}
636
+		LM_DBG("Branch %i\n", idx);
637
+
638
+		/*verify direction*/
639
+		if ((dialog_info = build_dlg_t_early(msg, dlg, idx, &rr_set)) == 0){
640
+			LM_ERR("failed to create dlg_t\n");
641
+			goto err;
642
+		}
643
+	} else {
644
+		LM_DBG("Send request to caller or in confirmed state...\n");
645
+		/*verify direction*/
646
+		if ((dialog_info = build_dlg_t(dlg, side)) == 0){
647
+			LM_ERR("failed to create dlg_t\n");
648
+			goto err;
649
+		}
650
+	}
651
+
652
+	LM_DBG("sending %.*s to %s\n", method->len, method->s, (side==DLG_CALLER_LEG)?"caller":"callee");
653
+
654
+	iuid = dlg_get_iuid_shm_clone(dlg);
655
+	if(iuid==NULL)
656
+	{
657
+		LM_ERR("failed to create dialog unique id clone\n");
658
+		goto err;
659
+	}
660
+
661
+	if (hdrs && hdrs->len > 0) {
662
+		LM_DBG("Extra headers: %.*s\n", STR_FMT(hdrs));
663
+		allheaders.len += hdrs->len;
664
+	}
665
+
666
+	if (content_type && content_type->s && content && content->s) {
667
+		LM_DBG("Content-Type: %.*s\n", STR_FMT(content_type));
668
+		allheaders.len += content_type_hdr.len + content_type->len + 2;
669
+	}
670
+	if (allheaders.len > 0) {
671
+		allheaders.s = (char*)pkg_malloc(allheaders.len);
672
+		if (allheaders.s == NULL) {
673
+			PKG_MEM_ERROR;
674
+			goto err;
675
+		}
676
+		allheaders.len = 0;
677
+		if (hdrs && hdrs->len > 0) {
678
+			memcpy(allheaders.s, hdrs->s, hdrs->len);
679
+			allheaders.len += hdrs->len;
680
+		}
681
+		if (content_type && content_type->s && content && content->s) {
682
+			memcpy(allheaders.s + allheaders.len, content_type_hdr.s, content_type_hdr.len);
683
+			allheaders.len += content_type_hdr.len;
684
+			memcpy(allheaders.s + allheaders.len, content_type->s, content_type->len);
685
+			allheaders.len += content_type->len;
686
+			memcpy(allheaders.s + allheaders.len, "\r\n", 2);
687
+			allheaders.len += 2;
688
+		}
689
+		LM_DBG("All headers: %.*s\n", STR_FMT(&allheaders));
690
+	}
691
+
692
+	set_uac_req(&uac_r, method, allheaders.len?&allheaders:NULL, (content && content->len)?content:NULL, dialog_info, TMCB_LOCAL_COMPLETED,
693
+				bye_reply_cb, (void*)iuid);
694
+
695
+	result = d_tmb.t_request_within(&uac_r);
696
+
697
+	if (allheaders.s)
698
+		pkg_free(allheaders.s);
699
+
700
+	if(result < 0){
701
+		LM_ERR("failed to send request\n");
702
+		goto err;
703
+	}
704
+
705
+	free_tm_dlg(dialog_info);
706
+
707
+	LM_DBG("%.*s sent to %s\n", method->len, method->s, (side==DLG_CALLER_LEG)?"caller":"callee");
708
+
709
+	return 0;
710
+err:
711
+	if(dialog_info)
712
+		free_tm_dlg(dialog_info);
713
+	return -1;
714
+}
441 715
 
442 716
 /* send keep-alive
443 717
  * dlg - pointer to a struct dlg_cell
... ...
@@ -52,5 +52,6 @@ int free_tm_dlg(dlg_t *td);
52 52
 int dlg_bye(struct dlg_cell *dlg, str *hdrs, int side);
53 53
 int dlg_bye_all(struct dlg_cell *dlg, str *hdrs);
54 54
 int dlg_send_ka(dlg_cell_t *dlg, int dir);
55
+int dlg_request_within(struct sip_msg *msg, struct dlg_cell *dlg, int side, str * method, str * hdrs, str * content_type, str * content);
55 56
 
56 57
 #endif
... ...
@@ -69,8 +69,8 @@
69 69
 		<holder>Voice Sistem SRL</holder>
70 70
 	</copyright>
71 71
 	<copyright>
72
-		<year>2011</year>
73
-		<holder>Carsten Bock, http://www.ng-voice.com</holder>
72
+		<year>2011, 2022</year>
73
+		<holder>ng-voice GmbH, Carsten Bock, http://www.ng-voice.com</holder>
74 74
 	</copyright>
75 75
 	</bookinfo>
76 76
 	<toc></toc>
... ...
@@ -1468,7 +1468,7 @@ modparam("dialog", "timer_procs", 1)
1468 1468
 		</example>
1469 1469
 	</section>
1470 1470
 
1471
-	<section>
1471
+	<section id="dialog.p.enable_dmq">
1472 1472
 		<title><varname>enable_dmq</varname> (int)</title>
1473 1473
 		<para>
1474 1474
 			If set to 1, the dialog will be synced via dmq.
... ...
@@ -1666,6 +1666,47 @@ modparam("dialog", "keep_proxy_rr", 1)
1666 1666
 </programlisting>
1667 1667
 		</example>
1668 1668
 	</section>
1669
+
1670
+	<section id="dialog.p.bye_early_code">
1671
+		<title><varname>bye_early_code</varname> (int)</title>
1672
+		<para>
1673
+			This parameter defines the reply-code being used for
1674
+			dialogs being terminated in early stage (e.g. before
1675
+			200 OK/ACK).
1676
+		</para>
1677
+		<emphasis>
1678
+			Default value is <quote>480</quote>.
1679
+		</emphasis>
1680
+		<example>
1681
+		<title>Set <varname>bye_early_code</varname> parameter</title>
1682
+		<programlisting format="linespecific">
1683
+...
1684
+modparam("dialog", "bye_early_code", 503)
1685
+...
1686
+</programlisting>
1687
+		</example>
1688
+	</section>
1689
+
1690
+	<section id="dialog.p.bye_early_reason">
1691
+		<title><varname>bye_early_reason</varname> (string)</title>
1692
+		<para>
1693
+			This parameter defines the reply-reason being used for
1694
+			dialogs being terminated in early stage (e.g. before
1695
+			200 OK/ACK).
1696
+		</para>
1697
+		<emphasis>
1698
+			Default value is <quote>Temporarily Unavailable</quote>.
1699
+		</emphasis>
1700
+		<example>
1701
+		<title>Set <varname>bye_early_reason</varname> parameter</title>
1702
+		<programlisting format="linespecific">
1703
+...
1704
+modparam("dialog", "bye_early_reason", "Call terminated")
1705
+...
1706
+</programlisting>
1707
+		</example>
1708
+	</section>
1709
+
1669 1710
 	</section>
1670 1711
 
1671 1712
 	<section>
... ...
@@ -1936,7 +1977,9 @@ redlg_setflag("1");
1936 1977
 		<function moreinfo="none">dlg_bye(side)</function>
1937 1978
 		</title>
1938 1979
 		<para>
1939
-		Send BYE to both parties of a dialog.
1980
+		Send BYE to parties of a dialog or - if in early stage - a CANCEL to the 
1981
+		B-Party and a SIP response to the A-Party (as defined in bye_early_code /
1982
+		bye_early_reason).
1940 1983
 		</para>
1941 1984
 		<para>Meaning of the parameters is as follows:</para>
1942 1985
 		<itemizedlist>
... ...
@@ -2444,6 +2487,67 @@ dlg_reset_property("timeout-noreset");
2444 2487
 </programlisting>
2445 2488
 		</example>
2446 2489
 	</section>
2490
+
2491
+	<section id="dialog.f.dlg_req_within">
2492
+		<title>
2493
+		<function moreinfo="none">dlg_req_within(side, method, [headers], [content_type, content])</function>
2494
+		</title>
2495
+		<para>
2496
+		Sends a in-dialog SIP Request with method to a party of a dialog indicated by the side parameter.
2497
+		</para>
2498
+		<para>Meaning of the parameters is as follows:</para>
2499
+		<itemizedlist>
2500
+		<listitem>
2501
+			<para>
2502
+				<emphasis>side</emphasis> - where to send the request. It can be:
2503
+				'caller', 'callee', or 'all' (send to both sides).
2504
+			</para>
2505
+		</listitem>
2506
+		<listitem>
2507
+			<para>
2508
+				<emphasis>method</emphasis> - Method of the request
2509
+			</para>
2510
+		</listitem>
2511
+		<listitem>
2512
+			<para>
2513
+				<emphasis>headers</emphasis> (optional) - additional headers to be added to the request.
2514
+			</para>
2515
+		</listitem>
2516
+		<listitem>
2517
+			<para>
2518
+				<emphasis>content_type</emphasis> (optional) - Content-Type of the request body - will
2519
+				be added as Content-Type Header.
2520
+			</para>
2521
+		</listitem>
2522
+		<listitem>
2523
+			<para>
2524
+				<emphasis>content</emphasis> (optional) - Content to be sent as body.
2525
+			</para>
2526
+		</listitem>
2527
+		</itemizedlist>		
2528
+		<para>
2529
+		This function can be used from ANY_ROUTE.
2530
+		</para>
2531
+		<example>
2532
+		<title><function>dlg_req_within</function> usage</title>
2533
+		<programlisting format="linespecific">
2534
+...
2535
+	# Send a simple request:
2536
+	dlg_req_within("all", "OPTIONS");
2537
+...
2538
+	# Send a simple request with extra headers:
2539
+	dlg_req_within("caller", "OPTIONS", "X-Info: Bandwidth granted\r\nX-Info-2: Go ahead\r\n");
2540
+...
2541
+	# Send a simple request with body:
2542
+	dlg_req_within("caller", "UPDATE", "application/sdp", "...some SDP...");
2543
+...
2544
+	# Send a simple request with extra headers and body:
2545
+	dlg_req_within("callee", "INFO", "X-Info: Bandwidth granted\r\n", "application/sdp", "...some SDP...");
2546
+...
2547
+		</programlisting>
2548
+		</example>
2549
+	</section>
2550
+
2447 2551
 	</section>
2448 2552
 
2449 2553