Index: res/res_features.c =================================================================== --- res/res_features.c (revision 48105) +++ res/res_features.c (working copy) @@ -214,7 +214,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) @@ -784,82 +784,158 @@ 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) { - 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 || newchan->_state != AST_STATE_UP || !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; - } + 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 (check_compat(transferee, newchan)) - return -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; + } - ast_indicate(transferee, AST_CONTROL_UNHOLD); + 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 || newchan->_state != AST_STATE_UP || !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; - } + 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, "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; - xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 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 (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 ((f = ast_read(xferchan))) - ast_frfree(f); + if(!newchan) { + /* 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",xferto,transferer_real_context); + if (ast_stream_and_wait(transferee, "beeperr", "")) + return -1; + return FEATURE_RETURN_SUCCESS; + } + snprintf(xferto, sizeof(xferto), "%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), + xferto, atxfernoanswertimeout, &outstate, transferee->cid.cid_num, transferee->cid.cid_name, 0); + } + if (!newchan) { + return -1; + } - newchan->_state = AST_STATE_UP; - ast_clear_flag(newchan, AST_FLAGS_ALL); - newchan->_softhangup = 0; + /* newchan is up, we should prepare transferee and bridge them */ + if (check_compat(transferee, newchan)) + return -1; + ast_indicate(transferee, AST_CONTROL_UNHOLD); - tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj)); - if (!tobj) { - ast_hangup(xferchan); - ast_hangup(newchan); + 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, "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 ? */ + + } else { + /* Transferee hanged up */ + finishup(transferee); 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 ? */ } @@ -1115,7 +1191,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; @@ -1149,7 +1225,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; @@ -1204,32 +1280,35 @@ } 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 (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 (*dialed_code && !strcmp(dialed_code, disconnect_code)) { - /* Caller Canceled the call */ - state = AST_CONTROL_UNHOLD; - ast_frfree(f); - f = NULL; - break; - } } } if (f)