diff options
author | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-23 22:12:01 +0000 |
---|---|---|
committer | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-23 22:12:01 +0000 |
commit | a6f4e0fac75333eb32e4b471dabd388de02c33ad (patch) | |
tree | ec6f97b43bdd64eeacad1a092447a919d0a322c8 /res | |
parent | c64a6879d69a43695feab41e4a3d6af0beaa4215 (diff) |
support cancellation of attended transfers using the defined disconnect code (bug #3729 with minor mods)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@5991 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'res')
-rwxr-xr-x | res/res_features.c | 190 |
1 files changed, 185 insertions, 5 deletions
diff --git a/res/res_features.c b/res/res_features.c index 2765cc159..65fa79b7d 100755 --- a/res/res_features.c +++ b/res/res_features.c @@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/options.h" +#include "asterisk/causes.h" #include "asterisk/module.h" #include "asterisk/translate.h" #include "asterisk/app.h" @@ -57,6 +58,8 @@ static void FREE(void *ptr) #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500 +#define AST_MAX_WATCHERS 256 + static char *parkedcall = "ParkedCall"; /* No more than 45 seconds parked before you do something with them */ @@ -196,6 +199,9 @@ 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 void *ast_bridge_call_thread(void *data) { struct ast_bridge_thread_obj *tobj = data; @@ -697,7 +703,9 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st cid_name = transferer->cid.cid_name; if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) { snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context); - if ((newchan = ast_request_and_dial("Local", ast_best_codec(transferer->nativeformats), dialstr,30000, &outstate, cid_num, cid_name))) { + newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), dialstr, 15000, &outstate, cid_num, cid_name); + ast_indicate(transferer, -1); + if(newchan){ res = ast_channel_make_compatible(transferer, newchan); if (res < 0) { ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name); @@ -708,7 +716,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st 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 || newchan->_state != AST_STATE_UP) { + if (newchan->_softhangup || newchan->_state != AST_STATE_UP || !transferer->_softhangup) { ast_hangup(newchan); if (f) { ast_frfree(f); @@ -792,17 +800,17 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st return -1; } else { - ast_log(LOG_WARNING, "Unable to create channel Local/%s do you have chan_local?\n",dialstr); ast_moh_stop(transferee); ast_autoservice_stop(transferee); ast_indicate(transferee, AST_CONTROL_UNHOLD); - if (!ast_strlen_zero(xferfailsound)) { + /* any reason besides user requested cancel and busy triggers the failed sound */ + if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY && !ast_strlen_zero(xferfailsound)) { res = ast_streamfile(transferer, xferfailsound, transferer->language); if (!res && (ast_waitstream(transferer, "") < 0)) { return -1; } } - return -1; + return FEATURE_RETURN_SUCCESS; } } else { ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context); @@ -914,6 +922,178 @@ static void set_config_flags(struct ast_bridge_config *config) } } + +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) +{ + int state = 0; + int cause = 0; + int to; + struct ast_channel *chan; + struct ast_channel *monitor_chans[2]; + struct ast_channel *active_channel; + struct ast_frame *f = NULL; + int res = 0, ready = 0; + + if ((chan = ast_request(type, format, data, &cause))) { + ast_set_callerid(chan, cid_num, cid_name, cid_num); + + if (!ast_call(chan, data, timeout)) { + struct timeval started, ended; + int x, len = 0; + char *disconnect_code = NULL, *dialed_code = NULL; + + ast_indicate(caller, AST_CONTROL_RINGING); + /* support dialing of the featuremap disconnect code while performing an attended tranfer */ + for (x=0; x<FEATURES_COUNT; x++) { + if (strcasecmp(builtin_features[x].sname, "disconnect")) + continue; + + disconnect_code = builtin_features[x].exten; + len = strlen(disconnect_code) + 1; + dialed_code = alloca(len); + memset(dialed_code, 0, len); + break; + } + x = 0; + gettimeofday(&started, NULL); + to = timeout; + while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) { + monitor_chans[0] = caller; + monitor_chans[1] = chan; + active_channel = ast_waitfor_n(monitor_chans, 2, &to); + + /* see if the timeout has been violated */ + gettimeofday(&ended,NULL); + if(ast_tvdiff_ms(&started, &ended) > timeout) { + state = AST_CONTROL_UNHOLD; + ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n"); + break; /*doh! timeout*/ + } + + if (!active_channel) { + continue; + } + + if (chan && (chan == active_channel)){ + f = ast_read(chan); + if (f == NULL) { /*doh! where'd he go?*/ + state = AST_CONTROL_HANGUP; + res = 0; + break; + } + + if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) { + if (f->subclass == AST_CONTROL_RINGING) { + state = f->subclass; + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name); + ast_indicate(caller, AST_CONTROL_RINGING); + } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { + state = f->subclass; + ast_frfree(f); + f = NULL; + break; + } else if (f->subclass == AST_CONTROL_ANSWER) { + /* This is what we are hoping for */ + state = f->subclass; + ast_frfree(f); + f = NULL; + ready=1; + break; + } else { + ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass); + } + /* else who cares */ + } + + } 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; + break; + } + state = AST_CONTROL_HANGUP; + res = 0; + break; + } + + 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; + 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 (f) { + ast_frfree(f); + } + } + } else + ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data); + } else { + ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); + switch(cause) { + case AST_CAUSE_BUSY: + state = AST_CONTROL_BUSY; + break; + case AST_CAUSE_CONGESTION: + state = AST_CONTROL_CONGESTION; + break; + } + } + + ast_indicate(caller, -1); + if (chan && ready) { + if (chan->_state == AST_STATE_UP) + state = AST_CONTROL_ANSWER; + res = 0; + } else if(chan) { + res = -1; + ast_hangup(chan); + chan = NULL; + } else { + res = -1; + } + + if (outstate) + *outstate = state; + + if (chan && res <= 0) { + if (!chan->cdr) { + chan->cdr = ast_cdr_alloc(); + } + if (chan->cdr) { + char tmp[256]; + ast_cdr_init(chan->cdr, chan); + snprintf(tmp, 256, "%s/%s", type, (char *)data); + ast_cdr_setapp(chan->cdr,"Dial",tmp); + ast_cdr_update(chan); + ast_cdr_start(chan->cdr); + ast_cdr_end(chan->cdr); + /* If the cause wasn't handled properly */ + if (ast_cdr_disposition(chan->cdr,chan->hangupcause)) + ast_cdr_failed(chan->cdr); + } else { + ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); + } + } + + return chan; +} + int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config) { /* Copy voice back and forth between the two channels. Give the peer |