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)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@62593 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.0
Russell Bryant 19 years ago
parent 37d94ceab9
commit 7df63e233c

@ -34,6 +34,13 @@ context => parkedcalls ; Which context parked calls are in
;featuredigittimeout = 500 ; Max time (ms) between digits for ;featuredigittimeout = 500 ; Max time (ms) between digits for
; feature activation (default is 500 ms) ; 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] [featuremap]
;blindxfer => #1 ; Blind transfer (default is #) ;blindxfer => #1 ; Blind transfer (default is #)

@ -63,6 +63,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 #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 #define AST_MAX_WATCHERS 256
@ -104,6 +107,9 @@ static int featuredigittimeout;
static int comebacktoorigin = 1; static int comebacktoorigin = 1;
static int atxfernoanswertimeout; static int atxfernoanswertimeout;
static unsigned int atxferdropcall;
static unsigned int atxferloopdelay;
static unsigned int atxfercallbackretries;
static char *registrar = "res_features"; /*!< Registrar for operations */ static char *registrar = "res_features"; /*!< Registrar for operations */
@ -219,7 +225,7 @@ static void check_goto_on_transfer(struct ast_channel *chan)
} }
} }
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) static void *ast_bridge_call_thread(void *data)
@ -770,6 +776,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
struct ast_channel *transferee; struct ast_channel *transferee;
const char *transferer_real_context; const char *transferer_real_context;
char xferto[256] = ""; char xferto[256] = "";
char callbackto[256] = "";
int res; int res;
int outstate=0; int outstate=0;
struct ast_channel *newchan; struct ast_channel *newchan;
@ -821,8 +828,11 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
l = strlen(xferto); l = strlen(xferto);
snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */ 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), 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); 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); ast_indicate(transferer, -1);
if (!newchan) { if (!newchan) {
finishup(transferee); finishup(transferee);
@ -830,6 +840,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY && if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
ast_stream_and_wait(transferer, xferfailsound, "")) ast_stream_and_wait(transferer, xferfailsound, ""))
return -1; return -1;
if (ast_stream_and_wait(transferer, xfersound, ""))
ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
return FEATURE_RETURN_SUCCESS; return FEATURE_RETURN_SUCCESS;
} }
@ -847,10 +859,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
transferer->_softhangup = 0; transferer->_softhangup = 0;
return FEATURE_RETURN_SUCCESS; return FEATURE_RETURN_SUCCESS;
} }
if (check_compat(transferee, newchan)) if (check_compat(transferee, newchan))
return -1; return -1;
ast_indicate(transferee, AST_CONTROL_UNHOLD); ast_indicate(transferee, AST_CONTROL_UNHOLD);
if ((ast_autoservice_stop(transferee) < 0) if ((ast_autoservice_stop(transferee) < 0)
@ -861,7 +871,6 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_hangup(newchan); ast_hangup(newchan);
return -1; return -1;
} }
xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
if (!xferchan) { if (!xferchan) {
ast_hangup(newchan); ast_hangup(newchan);
@ -875,16 +884,12 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
xferchan->_state = AST_STATE_UP; xferchan->_state = AST_STATE_UP;
ast_clear_flag(xferchan, AST_FLAGS_ALL); ast_clear_flag(xferchan, AST_FLAGS_ALL);
xferchan->_softhangup = 0; xferchan->_softhangup = 0;
if ((f = ast_read(xferchan))) if ((f = ast_read(xferchan)))
ast_frfree(f); ast_frfree(f);
newchan->_state = AST_STATE_UP; newchan->_state = AST_STATE_UP;
ast_clear_flag(newchan, AST_FLAGS_ALL); ast_clear_flag(newchan, AST_FLAGS_ALL);
newchan->_softhangup = 0; newchan->_softhangup = 0;
if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
if (!tobj) {
ast_hangup(xferchan); ast_hangup(xferchan);
ast_hangup(newchan); ast_hangup(newchan);
return -1; return -1;
@ -897,11 +902,107 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
ast_bridge_call_thread_launch(tobj); ast_bridge_call_thread_launch(tobj);
return -1; /* XXX meaning the channel is bridged ? */ 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);
return -1;
}
}
/* add atxfer and automon as undefined so you can only use em if you configure them */ /* 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[] = struct ast_call_feature builtin_features[] =
{ {
@ -1152,7 +1253,7 @@ static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer,
} }
/*! \todo XXX Check - this is very similar to the code in channel.c */ /*! \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 state = 0;
int cause = 0; int cause = 0;
@ -1194,7 +1295,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
x = 0; x = 0;
started = ast_tvnow(); started = ast_tvnow();
to = timeout; 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; struct ast_frame *f = NULL;
monitor_chans[0] = caller; monitor_chans[0] = caller;
@ -1249,6 +1350,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
} else if (caller && (active_channel == caller)) { } else if (caller && (active_channel == caller)) {
f = ast_read(caller); f = ast_read(caller);
if (f == NULL) { /*doh! where'd he go?*/ if (f == NULL) { /*doh! where'd he go?*/
if (!igncallerstate) {
if (caller->_softhangup && !chan->_softhangup) { if (caller->_softhangup && !chan->_softhangup) {
/* make this a blind transfer */ /* make this a blind transfer */
ready = 1; ready = 1;
@ -1258,6 +1360,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
res = 0; res = 0;
break; break;
} }
} else {
if (f->frametype == AST_FRAME_DTMF) { if (f->frametype == AST_FRAME_DTMF) {
dialed_code[x++] = f->subclass; dialed_code[x++] = f->subclass;
@ -1277,6 +1380,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
} }
} }
} }
}
if (f) if (f)
ast_frfree(f); ast_frfree(f);
} /* end while */ } /* end while */
@ -2345,6 +2449,9 @@ static int load_config(void)
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; 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"); cfg = ast_config_load("features.conf");
if (!cfg) { if (!cfg) {
@ -2406,6 +2513,19 @@ static int load_config(void)
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
} else } else
atxfernoanswertimeout = atxfernoanswertimeout * 1000; 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")) { } else if (!strcasecmp(var->name, "courtesytone")) {
ast_copy_string(courtesytone, var->value, sizeof(courtesytone)); ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
} else if (!strcasecmp(var->name, "parkedplay")) { } else if (!strcasecmp(var->name, "parkedplay")) {

Loading…
Cancel
Save