[asterisk-commits] russell: trunk r62593 - in /trunk: configs/ res/

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Tue May 1 15:24:51 MST 2007


Author: russell
Date: Tue May  1 17:24:51 2007
New Revision: 62593

URL: http://svn.digium.com/view/asterisk?view=rev&rev=62593
Log:
In addition to making it so attended transfers don't fail unnecessarily,
add some new options to control what happens when you hangup on an attended
transfer before the target extension answers the transferred channel.  You
can now have it send the transferee back to the transferer.
(issue #8413, patch from sergee with very minor modifications by me)

Modified:
    trunk/configs/features.conf.sample
    trunk/res/res_features.c

Modified: trunk/configs/features.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/features.conf.sample?view=diff&rev=62593&r1=62592&r2=62593
==============================================================================
--- trunk/configs/features.conf.sample (original)
+++ trunk/configs/features.conf.sample Tue May  1 17:24:51 2007
@@ -33,7 +33,14 @@
 ;pickupexten = *8		; Configure the pickup extension. (default is *8)
 ;featuredigittimeout = 500	; Max time (ms) between digits for 
 				; feature activation  (default is 500 ms)
-;atxfernoanswertimeout = 15	; Timeout for answer on attended transfer default is 15 seconds.
+;atxfernoanswertimeout = 15 ; Timeout for answer on attended transfer default is 15 seconds.
+;atxferdropcall = no        ; If someone does an attended transfer, then hangs up before the transferred
+                            ; caller is connected, then by default, the system will try to call back the
+                            ; person that did the transfer.  If this is set to "yes", the callback will
+                            ; not be attempted and the transfer will just fail.
+;atxferloopdelay = 10       ; Number of seconds to sleep between retries (if atxferdropcall = no)
+;atxfercallbackretries = 2  ; Number of times to attempt to send the call back to the transferer.
+                            ; By default, this is 2.
 
 [featuremap]
 ;blindxfer => #1		; Blind transfer  (default is #)

Modified: trunk/res/res_features.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_features.c?view=diff&rev=62593&r1=62592&r2=62593
==============================================================================
--- trunk/res/res_features.c (original)
+++ trunk/res/res_features.c Tue May  1 17:24:51 2007
@@ -63,6 +63,9 @@
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
+#define DEFAULT_ATXFER_DROP_CALL 0
+#define DEFAULT_ATXFER_LOOP_DELAY 10000
+#define DEFAULT_ATXFER_CALLBACK_RETRIES 2
 
 #define AST_MAX_WATCHERS 256
 
@@ -104,6 +107,9 @@
 static int comebacktoorigin = 1;
 
 static int atxfernoanswertimeout;
+static unsigned int atxferdropcall;
+static unsigned int atxferloopdelay;
+static unsigned int atxfercallbackretries;
 
 static char *registrar = "res_features";		   /*!< Registrar for operations */
 
@@ -219,7 +225,7 @@
 	}
 }
 
-static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name);
+static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, struct ast_channel *transferee, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, int igncallerstate);
 
 
 static void *ast_bridge_call_thread(void *data) 
@@ -770,6 +776,7 @@
 	struct ast_channel *transferee;
 	const char *transferer_real_context;
 	char xferto[256] = "";
+	char callbackto[256] = "";
 	int res;
 	int outstate=0;
 	struct ast_channel *newchan;
@@ -821,87 +828,181 @@
 
 	l = strlen(xferto);
 	snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);	/* append context */
-	newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats),
-		xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name);
-	ast_indicate(transferer, -1);
-	if (!newchan) {
+	newchan = ast_feature_request_and_dial(transferer, transferee, "Local", ast_best_codec(transferer->nativeformats),
+		xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, 1);
+
+	if (!ast_check_hangup(transferer)) {
+		/* Transferer is up - old behaviour */
+		ast_indicate(transferer, -1);
+		if (!newchan) {
+			finishup(transferee);
+			/* any reason besides user requested cancel and busy triggers the failed sound */
+			if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
+				ast_stream_and_wait(transferer, xferfailsound, ""))
+				return -1;
+			if (ast_stream_and_wait(transferer, xfersound, ""))
+				ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+			return FEATURE_RETURN_SUCCESS;
+		}
+
+		if (check_compat(transferer, newchan))
+			return -1;
+		memset(&bconfig,0,sizeof(struct ast_bridge_config));
+		ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
+		ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
+		res = ast_bridge_call(transferer, newchan, &bconfig);
+		if (newchan->_softhangup || !transferer->_softhangup) {
+			ast_hangup(newchan);
+			if (ast_stream_and_wait(transferer, xfersound, ""))
+				ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+			finishup(transferee);
+			transferer->_softhangup = 0;
+			return FEATURE_RETURN_SUCCESS;
+		}
+		if (check_compat(transferee, newchan))
+			return -1;
+		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+		if ((ast_autoservice_stop(transferee) < 0)
+		 || (ast_waitfordigit(transferee, 100) < 0)
+		 || (ast_waitfordigit(newchan, 100) < 0)
+		 || ast_check_hangup(transferee)
+		 || ast_check_hangup(newchan)) {
+			ast_hangup(newchan);
+			return -1;
+		}
+		xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+		if (!xferchan) {
+			ast_hangup(newchan);
+			return -1;
+		}
+		/* Make formats okay */
+		xferchan->readformat = transferee->readformat;
+		xferchan->writeformat = transferee->writeformat;
+		ast_channel_masquerade(xferchan, transferee);
+		ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+		xferchan->_state = AST_STATE_UP;
+		ast_clear_flag(xferchan, AST_FLAGS_ALL);
+		xferchan->_softhangup = 0;
+		if ((f = ast_read(xferchan)))
+			ast_frfree(f);
+		newchan->_state = AST_STATE_UP;
+		ast_clear_flag(newchan, AST_FLAGS_ALL);
+		newchan->_softhangup = 0;
+		if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+			ast_hangup(xferchan);
+			ast_hangup(newchan);
+			return -1;
+		}
+		tobj->chan = xferchan;
+		tobj->peer = newchan;
+		tobj->bconfig = *config;
+
+		if (ast_stream_and_wait(newchan, xfersound, ""))
+			ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+		ast_bridge_call_thread_launch(tobj);
+		return -1;      /* XXX meaning the channel is bridged ? */
+	} else if (!ast_check_hangup(transferee)) {
+		/* act as blind transfer */
+		if (ast_autoservice_stop(transferee) < 0) {
+			ast_hangup(newchan);
+			return -1;
+		}
+
+		if (!newchan) {
+			unsigned int tries = 0;
+
+			/* newchan wasn't created - we should callback to transferer */
+			if (!ast_exists_extension(transferer, transferer_real_context, transferer->cid.cid_num, 1, transferee->cid.cid_num)) {
+				ast_log(LOG_WARNING, "Extension %s does not exist in context %s - callback failed\n",transferer->cid.cid_num,transferer_real_context);
+				if (ast_stream_and_wait(transferee, "beeperr", ""))
+					return -1;
+				return FEATURE_RETURN_SUCCESS;
+			}
+			snprintf(callbackto, sizeof(callbackto), "%s@%s/n", transferer->cid.cid_num, transferer_real_context);  /* append context */
+
+			newchan = ast_feature_request_and_dial(transferee, NULL, "Local", ast_best_codec(transferee->nativeformats),
+				callbackto, atxfernoanswertimeout, &outstate, transferee->cid.cid_num, transferee->cid.cid_name, 0);
+			while (!newchan && !atxferdropcall && tries < atxfercallbackretries) {
+				/* Trying to transfer again */
+				ast_autoservice_start(transferee);
+				ast_indicate(transferee, AST_CONTROL_HOLD);
+
+				newchan = ast_feature_request_and_dial(transferer, transferee, "Local", ast_best_codec(transferer->nativeformats),
+				xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, 1);
+				if (ast_autoservice_stop(transferee) < 0) {
+					ast_hangup(newchan);
+					return -1;
+				}
+				if (!newchan) {
+					/* Transfer failed, sleeping */
+					if (option_debug)
+						ast_log(LOG_DEBUG, "Sleeping for %d ms before callback.\n", atxferloopdelay);
+					ast_safe_sleep(transferee, atxferloopdelay);
+					if (option_debug)
+						ast_log(LOG_DEBUG, "Trying to callback...\n");
+					newchan = ast_feature_request_and_dial(transferee, NULL, "Local", ast_best_codec(transferee->nativeformats),
+						callbackto, atxfernoanswertimeout, &outstate, transferee->cid.cid_num, transferee->cid.cid_name, 0);
+				}
+				tries++;
+			}
+		}
+		if (!newchan)
+			return -1;
+
+		/* newchan is up, we should prepare transferee and bridge them */
+		if (check_compat(transferee, newchan))
+			return -1;
+		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+		if ((ast_waitfordigit(transferee, 100) < 0)
+		   || (ast_waitfordigit(newchan, 100) < 0)
+		   || ast_check_hangup(transferee)
+		   || ast_check_hangup(newchan)) {
+			ast_hangup(newchan);
+			return -1;
+		}
+
+		xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+		if (!xferchan) {
+			ast_hangup(newchan);
+			return -1;
+		}
+		/* Make formats okay */
+		xferchan->readformat = transferee->readformat;
+		xferchan->writeformat = transferee->writeformat;
+		ast_channel_masquerade(xferchan, transferee);
+		ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+		xferchan->_state = AST_STATE_UP;
+		ast_clear_flag(xferchan, AST_FLAGS_ALL);
+		xferchan->_softhangup = 0;
+		if ((f = ast_read(xferchan)))
+			ast_frfree(f);
+		newchan->_state = AST_STATE_UP;
+		ast_clear_flag(newchan, AST_FLAGS_ALL);
+		newchan->_softhangup = 0;
+		if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+			ast_hangup(xferchan);
+			ast_hangup(newchan);
+			return -1;
+		}
+		tobj->chan = xferchan;
+		tobj->peer = newchan;
+		tobj->bconfig = *config;
+
+		if (ast_stream_and_wait(newchan, xfersound, ""))
+			ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+		ast_bridge_call_thread_launch(tobj);
+		return -1;      /* XXX meaning the channel is bridged ? */
+	} else {
+		/* Transferee hung up */
 		finishup(transferee);
-		/* any reason besides user requested cancel and busy triggers the failed sound */
-		if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
-				ast_stream_and_wait(transferer, xferfailsound, ""))
-			return -1;
-		return FEATURE_RETURN_SUCCESS;
-	}
-
-	if (check_compat(transferer, newchan))
 		return -1;
-	memset(&bconfig,0,sizeof(struct ast_bridge_config));
-	ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
-	ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
-	res = ast_bridge_call(transferer, newchan, &bconfig);
-	if (newchan->_softhangup || !transferer->_softhangup) {
-		ast_hangup(newchan);
-		if (ast_stream_and_wait(transferer, xfersound, ""))
-			ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
-		finishup(transferee);
-		transferer->_softhangup = 0;
-		return FEATURE_RETURN_SUCCESS;
-	}
-	
-	if (check_compat(transferee, newchan))
-		return -1;
-
-	ast_indicate(transferee, AST_CONTROL_UNHOLD);
-	
-	if ((ast_autoservice_stop(transferee) < 0)
-	   || (ast_waitfordigit(transferee, 100) < 0)
-	   || (ast_waitfordigit(newchan, 100) < 0) 
-	   || ast_check_hangup(transferee) 
-	   || ast_check_hangup(newchan)) {
-		ast_hangup(newchan);
-		return -1;
-	}
-
-	xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
-	if (!xferchan) {
-		ast_hangup(newchan);
-		return -1;
-	}
-	/* Make formats okay */
-	xferchan->readformat = transferee->readformat;
-	xferchan->writeformat = transferee->writeformat;
-	ast_channel_masquerade(xferchan, transferee);
-	ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
-	xferchan->_state = AST_STATE_UP;
-	ast_clear_flag(xferchan, AST_FLAGS_ALL);	
-	xferchan->_softhangup = 0;
-
-	if ((f = ast_read(xferchan)))
-		ast_frfree(f);
-
-	newchan->_state = AST_STATE_UP;
-	ast_clear_flag(newchan, AST_FLAGS_ALL);	
-	newchan->_softhangup = 0;
-
-	tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
-	if (!tobj) {
-		ast_hangup(xferchan);
-		ast_hangup(newchan);
-		return -1;
-	}
-	tobj->chan = xferchan;
-	tobj->peer = newchan;
-	tobj->bconfig = *config;
-
-	if (ast_stream_and_wait(newchan, xfersound, ""))
-		ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
-	ast_bridge_call_thread_launch(tobj);
-	return -1;	/* XXX meaning the channel is bridged ? */
-}
-
+	}
+}
 
 /* add atxfer and automon as undefined so you can only use em if you configure them */
-#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
+#define FEATURES_COUNT ARRAY_LEN(builtin_features)
 
 struct ast_call_feature builtin_features[] = 
  {
@@ -1152,7 +1253,7 @@
 }
 
 /*! \todo XXX Check - this is very similar to the code in channel.c */
-static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name)
+static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, struct ast_channel *transferee, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, int igncallerstate)
 {
 	int state = 0;
 	int cause = 0;
@@ -1194,7 +1295,7 @@
 			x = 0;
 			started = ast_tvnow();
 			to = timeout;
-			while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) {
+			while (!((transferee && transferee->_softhangup) && (!igncallerstate && ast_check_hangup(caller))) && timeout && (chan->_state != AST_STATE_UP)) {
 				struct ast_frame *f = NULL;
 
 				monitor_chans[0] = caller;
@@ -1249,31 +1350,34 @@
 				} else if (caller && (active_channel == caller)) {
 					f = ast_read(caller);
 					if (f == NULL) { /*doh! where'd he go?*/
-						if (caller->_softhangup && !chan->_softhangup) {
-							/* make this a blind transfer */
-							ready = 1;
+						if (!igncallerstate) {
+							if (caller->_softhangup && !chan->_softhangup) {
+								/* make this a blind transfer */
+								ready = 1;
+								break;
+							}
+							state = AST_CONTROL_HANGUP;
+							res = 0;
 							break;
 						}
-						state = AST_CONTROL_HANGUP;
-						res = 0;
-						break;
-					}
+					} else {
 					
-					if (f->frametype == AST_FRAME_DTMF) {
-						dialed_code[x++] = f->subclass;
-						dialed_code[x] = '\0';
-						if (strlen(dialed_code) == len) {
-							x = 0;
-						} else if (x && strncmp(dialed_code, disconnect_code, x)) {
-							x = 0;
+						if (f->frametype == AST_FRAME_DTMF) {
+							dialed_code[x++] = f->subclass;
 							dialed_code[x] = '\0';
-						}
-						if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
-							/* Caller Canceled the call */
-							state = AST_CONTROL_UNHOLD;
-							ast_frfree(f);
-							f = NULL;
-							break;
+							if (strlen(dialed_code) == len) {
+								x = 0;
+							} else if (x && strncmp(dialed_code, disconnect_code, x)) {
+								x = 0;
+								dialed_code[x] = '\0';
+							}
+							if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
+								/* Caller Canceled the call */
+								state = AST_CONTROL_UNHOLD;
+								ast_frfree(f);
+								f = NULL;
+								break;
+							}
 						}
 					}
 				}
@@ -2345,6 +2449,9 @@
 	transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
 	featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
 	atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
+	atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+	atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
+	atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
 
 	cfg = ast_config_load("features.conf");
 	if (!cfg) {
@@ -2406,6 +2513,19 @@
 				atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
 			} else
 				atxfernoanswertimeout = atxfernoanswertimeout * 1000;
+		} else if (!strcasecmp(var->name, "atxferloopdelay")) {
+			if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+				ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
+				atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+			} else 
+				atxferloopdelay *= 1000;
+		} else if (!strcasecmp(var->name, "atxferdropcall")) {
+			atxferdropcall = ast_true(var->value);
+		} else if (!strcasecmp(var->name, "atxfercallbackretries")) {
+			if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+				ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
+				atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
+			}
 		} else if (!strcasecmp(var->name, "courtesytone")) {
 			ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
 		}  else if (!strcasecmp(var->name, "parkedplay")) {



More information about the asterisk-commits mailing list