-#if 0
-/*!
- * \internal
- * \brief Play file to specified channels.
- *
- * \param left Channel on left to play file.
- * \param right Channel on right to play file.
- * \param which Play file on indicated channels: which < 0 play left, which == 0 play both, which > 0 play right
- * \param msg Descriptive name of message type being played.
- * \param audiofile Audio file to play to channels.
- *
- * \note Plays file to the indicated channels in turn so please
- * don't use this for very long messages.
- *
- * \retval 0 on success.
- * \retval -1 on error. (Couldn't play file, channel hung up,...)
- */
-static int play_message_to_chans(struct ast_channel *left, struct ast_channel *right, int which, const char *msg, const char *audiofile)
-{
- /* First play the file to the left channel if requested. */
- if (which <= 0 && play_message_on_chan(left, right, msg, audiofile)) {
- return -1;
- }
-
- /* Then play the file to the right channel if requested. */
- if (which >= 0 && play_message_on_chan(right, left, msg, audiofile)) {
- return -1;
- }
-
- return 0;
-}
-#endif
-
-#if 0
-/*!
- * \brief Play message to both caller and callee in bridged call, plays synchronously, autoservicing the
- * other channel during the message, so please don't use this for very long messages
- */
-static int play_message_in_bridged_call(struct ast_channel *caller_chan, struct ast_channel *callee_chan, const char *audiofile)
-{
- return play_message_to_chans(caller_chan, callee_chan, 0, "automon message",
- audiofile);
-}
-#endif
-
-#if 0
-/*!
- * \brief Monitor a channel by DTMF
- * \param chan channel requesting monitor
- * \param peer channel to be monitored
- * \param config
- * \param code
- * \param sense feature options
- *
- * \param data
- * Check monitor app enabled, setup channels, both caller/callee chans not null
- * get TOUCH_MONITOR variable for filename if exists, exec monitor app.
- * \retval AST_FEATURE_RETURN_SUCCESS on success.
- * \retval -1 on error.
- */
-static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
-{
- char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
- int x = 0;
- size_t len;
- struct ast_channel *caller_chan, *callee_chan;
- const char *automon_message_start = NULL;
- const char *automon_message_stop = NULL;
- const char *touch_format = NULL;
- const char *touch_monitor = NULL;
- const char *touch_monitor_prefix = NULL;
- struct ast_app *monitor_app;
-
- monitor_app = pbx_findapp("Monitor");
- if (!monitor_app) {
- ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
- return -1;
- }
-
- set_peers(&caller_chan, &callee_chan, peer, chan, sense);
-
- /* Find extra messages */
- automon_message_start = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_START");
- automon_message_stop = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_STOP");
-
- if (!ast_strlen_zero(courtesytone)) { /* Play courtesy tone if configured */
- if(play_message_in_bridged_call(caller_chan, callee_chan, courtesytone) == -1) {
- return -1;
- }
- }
-
- if (ast_channel_monitor(callee_chan)) {
- ast_verb(4, "User hit '%s' to stop recording call.\n", code);
- if (!ast_strlen_zero(automon_message_stop)) {
- play_message_in_bridged_call(caller_chan, callee_chan, automon_message_stop);
- }
- ast_channel_monitor(callee_chan)->stop(callee_chan, 1);
- return AST_FEATURE_RETURN_SUCCESS;
- }
-
- touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
- touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
- touch_monitor_prefix = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_PREFIX");
-
- if (!touch_format)
- touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
-
- if (!touch_monitor)
- touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
-
- if (!touch_monitor_prefix)
- touch_monitor_prefix = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_PREFIX");
-
- if (touch_monitor) {
- len = strlen(touch_monitor) + 50;
- args = ast_alloca(len);
- touch_filename = ast_alloca(len);
- snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
- snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
- } else {
- caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(caller_chan)->id.number.valid,
- ast_channel_caller(caller_chan)->id.number.str, ast_channel_name(caller_chan)));
- callee_chan_id = ast_strdupa(S_COR(ast_channel_caller(callee_chan)->id.number.valid,
- ast_channel_caller(callee_chan)->id.number.str, ast_channel_name(callee_chan)));
- len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
- args = ast_alloca(len);
- touch_filename = ast_alloca(len);
- snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, callee_chan_id);
- snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
- }
-
- for(x = 0; x < strlen(args); x++) {
- if (args[x] == '/')
- args[x] = '-';
- }
-
- ast_verb(4, "User hit '%s' to record call. filename: %s\n", code, args);
-
- pbx_exec(callee_chan, monitor_app, args);
- pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
- pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
-
- if (!ast_strlen_zero(automon_message_start)) { /* Play start message for both channels */
- play_message_in_bridged_call(caller_chan, callee_chan, automon_message_start);
- }
-
- return AST_FEATURE_RETURN_SUCCESS;
-}
-#endif
-
-#if 0
-static int builtin_automixmonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
-{
- char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
- int x = 0;
- size_t len;
- struct ast_channel *caller_chan, *callee_chan;
- const char *mixmonitor_spy_type = "MixMonitor";
- const char *touch_format;
- const char *touch_monitor;
- struct ast_app *mixmonitor_app;
- int count = 0;
-
- mixmonitor_app = pbx_findapp("MixMonitor");
- if (!mixmonitor_app) {
- ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
- return -1;
- }
-
- set_peers(&caller_chan, &callee_chan, peer, chan, sense);
-
- if (!ast_strlen_zero(courtesytone)) {
- if (ast_autoservice_start(callee_chan))
- return -1;
- ast_autoservice_ignore(callee_chan, AST_FRAME_DTMF_END);
- if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
- ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
- ast_autoservice_stop(callee_chan);
- return -1;
- }
- if (ast_autoservice_stop(callee_chan))
- return -1;
- }
-
- ast_channel_lock(callee_chan);
- count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
- ast_channel_unlock(callee_chan);
-
- /* This means a mixmonitor is attached to the channel, running or not is unknown. */
- if (count > 0) {
- ast_verb(3, "User hit '%s' to stop recording call.\n", code);
-
- /* Make sure they are running */
- ast_channel_lock(callee_chan);
- count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
- ast_channel_unlock(callee_chan);
- if (count > 0) {
- struct ast_app *stopmixmonitor_app;
-
- stopmixmonitor_app = pbx_findapp("StopMixMonitor");
- if (!stopmixmonitor_app) {
- ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
- return -1;
- }
- pbx_exec(callee_chan, stopmixmonitor_app, "");
- return AST_FEATURE_RETURN_SUCCESS;
- }
-
- ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n");
- }
-
- touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
- touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");
-
- if (!touch_format)
- touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");
-
- if (!touch_monitor)
- touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");
-
- if (touch_monitor) {
- len = strlen(touch_monitor) + 50;
- args = ast_alloca(len);
- touch_filename = ast_alloca(len);
- snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
- snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
- } else {
- caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(caller_chan)->id.number.valid,
- ast_channel_caller(caller_chan)->id.number.str, ast_channel_name(caller_chan)));
- callee_chan_id = ast_strdupa(S_COR(ast_channel_caller(callee_chan)->id.number.valid,
- ast_channel_caller(callee_chan)->id.number.str, ast_channel_name(callee_chan)));
- len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
- args = ast_alloca(len);
- touch_filename = ast_alloca(len);
- snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
- snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
- }
-
- for( x = 0; x < strlen(args); x++) {
- if (args[x] == '/')
- args[x] = '-';
- }
-
- ast_verb(3, "User hit '%s' to record call. filename: %s\n", code, touch_filename);
-
- pbx_exec(callee_chan, mixmonitor_app, args);
- pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
- pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
- return AST_FEATURE_RETURN_SUCCESS;
-}
-#endif
-
-#if 0
-static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
-{
- ast_verb(4, "User hit '%s' to disconnect call.\n", code);
- return AST_FEATURE_RETURN_HANGUP;
-}
-#endif
-
-#if 0
-/*!
- * \brief Find the context for the transfer
- * \param transferer
- * \param transferee
- *
- * Grab the TRANSFER_CONTEXT, if fails try grabbing macrocontext.
- * \return a context string
- */
-static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
-{
- const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
- if (ast_strlen_zero(s)) {
- s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
- }
- if (ast_strlen_zero(s)) { /* Use the non-macro context to transfer the call XXX ? */
- s = ast_channel_macrocontext(transferer);
- }
- if (ast_strlen_zero(s)) {
- s = ast_channel_context(transferer);
- }
- return s;
-}
-#endif
-
-#if 0
-/*!
- * \brief make channels compatible
- * \param c
- * \param newchan
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
-{
- if (ast_channel_make_compatible(c, newchan) < 0) {
- ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
- ast_channel_name(c), ast_channel_name(newchan));
- ast_autoservice_chan_hangup_peer(c, newchan);
- return -1;
- }
- return 0;
-}
-#endif
-
-#if 0
-/*!
- * \internal
- * \brief Builtin attended transfer failed cleanup.
- * \since 10.0
- *
- * \param transferee Party A in the transfer.
- * \param transferer Party B in the transfer.
- * \param connected_line Saved connected line info about party A.
- *
- * \note The connected_line data is freed.
- *
- * \return Nothing
- */
-static void atxfer_fail_cleanup(struct ast_channel *transferee, struct ast_channel *transferer, struct ast_party_connected_line *connected_line)
-{
- finishup(transferee);
-
- /*
- * Restore party B connected line info about party A.
- *
- * Party B was the caller to party C and is the last known mode
- * for party B.
- */
- if (ast_channel_connected_line_sub(transferee, transferer, connected_line, 0) &&
- ast_channel_connected_line_macro(transferee, transferer, connected_line, 1, 0)) {
- ast_channel_update_connected_line(transferer, connected_line, NULL);
- }
- ast_party_connected_line_free(connected_line);
-}
-#endif
-
-#if 0
-/*!
- * \brief Attended transfer
- * \param chan transfered user
- * \param peer person transfering call
- * \param config
- * \param code
- * \param sense feature options
- *
- * \param data
- * Get extension to transfer to, if you cannot generate channel (or find extension)
- * return to host channel. After called channel answered wait for hangup of transferer,
- * bridge call between transfer peer (taking them off hold) to attended transfer channel.
- *
- * \return -1 on failure
- */
-static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
-{
- struct ast_channel *transferer;/* Party B */
- struct ast_channel *transferee;/* Party A */
- struct ast_exten *park_exten;
- const char *chan1_attended_sound;
- const char *chan2_attended_sound;
- const char *transferer_real_context;
- char xferto[256] = "";
- int res;
- int outstate=0;
- struct ast_channel *newchan;
- struct ast_channel *xferchan;
- struct ast_bridge_thread_obj *tobj;
- struct ast_bridge_config bconfig;
- int l;
- struct ast_party_connected_line connected_line;
- struct ast_datastore *features_datastore;
- struct ast_dial_features *dialfeatures;
- char *transferer_tech;
- char *transferer_name;
- char *transferer_name_orig;
- char *dash;
- RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
-
- ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", ast_channel_name(chan), ast_channel_name(peer), sense);
- set_peers(&transferer, &transferee, peer, chan, sense);
- transferer_real_context = real_ctx(transferer, transferee);
-
- /* Start autoservice on transferee while we talk to the transferer */
- ast_autoservice_start(transferee);
- ast_indicate(transferee, AST_CONTROL_HOLD);
-
- /* Transfer */
- res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
- if (res < 0) {
- finishup(transferee);
- return -1;
- }
- if (res > 0) { /* If they've typed a digit already, handle it */
- xferto[0] = (char) res;
- }
-
- ast_channel_lock(transferer);
- xfer_cfg = ast_get_chan_features_xfer_config(transferer);
- ast_channel_unlock(transferer);
-
- /* XXX All accesses to the xfer_cfg structure after this point are not thread-safe,
- * but I don't care because this is dead code.
- */
-
- /* this is specific of atxfer */
- res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, xfer_cfg->transferdigittimeout);
- if (res < 0) { /* hangup or error, (would be 0 for invalid and 1 for valid) */
- finishup(transferee);
- return -1;
- }
- l = strlen(xferto);
- if (res == 0) {
- if (l) {
- ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
- xferto, transferer_real_context);
- } else {
- /* Does anyone care about this case? */
- ast_log(LOG_WARNING, "No digits dialed for atxfer.\n");
- }
- ast_stream_and_wait(transferer, "pbx-invalid", "");
- finishup(transferee);
- return AST_FEATURE_RETURN_SUCCESS;
- }
-
- park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
- if (park_exten) {
- /* We are transfering the transferee to a parking lot. */
- return xfer_park_call_helper(transferee, transferer, park_exten);
- }
-
- /*
- * Append context to dialed transfer number.
- *
- * NOTE: The local channel needs the /n flag so party C will use
- * the feature flags set by the dialplan when calling that
- * party.
- */
- snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);
-
- /* If we are performing an attended transfer and we have two channels involved then
- copy sound file information to play upon attended transfer completion */
- chan1_attended_sound = pbx_builtin_getvar_helper(transferer, "ATTENDED_TRANSFER_COMPLETE_SOUND");
- chan2_attended_sound = pbx_builtin_getvar_helper(transferee, "ATTENDED_TRANSFER_COMPLETE_SOUND");
- if (!ast_strlen_zero(chan1_attended_sound)) {
- pbx_builtin_setvar_helper(transferer, "BRIDGE_PLAY_SOUND", chan1_attended_sound);
- }
- if (!ast_strlen_zero(chan2_attended_sound)) {
- pbx_builtin_setvar_helper(transferee, "BRIDGE_PLAY_SOUND", chan2_attended_sound);
- }
-
- /* Extract redial transferer information from the channel name. */
- transferer_name_orig = ast_strdupa(ast_channel_name(transferer));
- transferer_name = ast_strdupa(transferer_name_orig);
- transferer_tech = strsep(&transferer_name, "/");
- dash = strrchr(transferer_name, '-');
- if (dash) {
- /* Trim off channel name sequence/serial number. */
- *dash = '\0';
- }
-
- /* Stop autoservice so we can monitor all parties involved in the transfer. */
- if (ast_autoservice_stop(transferee) < 0) {
- ast_indicate(transferee, AST_CONTROL_UNHOLD);
- return -1;
- }
-
- /* Save connected line info for party B about party A in case transfer fails. */
- ast_party_connected_line_init(&connected_line);
- ast_channel_lock(transferer);
- ast_party_connected_line_copy(&connected_line, ast_channel_connected(transferer));
- ast_channel_unlock(transferer);
- connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-
- /* Dial party C */
- newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer,
- transferee, "Local", ast_channel_nativeformats(transferer), xferto,
- xfer_cfg->atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
- ast_debug(2, "Dial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate);
-
- if (!ast_check_hangup(transferer)) {
- /* Transferer (party B) is up */
- ast_debug(1, "Actually doing an attended transfer.\n");
-
- /* Start autoservice on transferee while the transferer deals with party C. */
- ast_autoservice_start(transferee);
-
- ast_indicate(transferer, -1);
- if (!newchan) {
- /* any reason besides user requested cancel and busy triggers the failed sound */
- switch (outstate) {
- case AST_CONTROL_UNHOLD:/* Caller requested cancel or party C answer timeout. */
- case AST_CONTROL_BUSY:
- case AST_CONTROL_CONGESTION:
- if (ast_stream_and_wait(transferer, xfer_cfg->xfersound, "")) {
- ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
- }
- break;
- default:
- if (ast_stream_and_wait(transferer, xfer_cfg->xferfailsound, "")) {
- ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
- }
- break;
- }
- atxfer_fail_cleanup(transferee, transferer, &connected_line);
- return AST_FEATURE_RETURN_SUCCESS;
- }
-
- if (check_compat(transferer, newchan)) {
- if (ast_stream_and_wait(transferer, xfer_cfg->xferfailsound, "")) {
- ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
- }
- atxfer_fail_cleanup(transferee, transferer, &connected_line);
- return AST_FEATURE_RETURN_SUCCESS;
- }
- 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);
-
- /*
- * Let party B and C talk as long as they want while party A
- * languishes in autoservice listening to MOH.
- */
- ast_bridge_call(transferer, newchan, &bconfig);
-
- if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
- ast_autoservice_chan_hangup_peer(transferer, newchan);
- if (ast_stream_and_wait(transferer, xfer_cfg->xfersound, "")) {
- ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
- }
- atxfer_fail_cleanup(transferee, transferer, &connected_line);
- return AST_FEATURE_RETURN_SUCCESS;
- }
-
- /* Transferer (party B) is confirmed hung up at this point. */
- if (check_compat(transferee, newchan)) {
- finishup(transferee);
- ast_party_connected_line_free(&connected_line);
- 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);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
- } else if (!ast_check_hangup(transferee)) {
- /* Transferer (party B) has hung up at this point. Doing blonde transfer. */
- ast_debug(1, "Actually doing a blonde transfer.\n");
-
- if (!newchan && !xfer_cfg->atxferdropcall) {
- /* Party C is not available, try to call party B back. */
- unsigned int tries = 0;
-
- if (ast_strlen_zero(transferer_name) || ast_strlen_zero(transferer_tech)) {
- ast_log(LOG_WARNING,
- "Transferer channel name: '%s' cannot be used for callback.\n",
- transferer_name_orig);
- ast_indicate(transferee, AST_CONTROL_UNHOLD);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- tries = 0;
- for (;;) {
- /* Try to get party B back. */
- ast_debug(1, "We're trying to callback %s/%s\n",
- transferer_tech, transferer_name);
- newchan = feature_request_and_dial(transferer, transferer_name_orig,
- transferee, transferee, transferer_tech,
- ast_channel_nativeformats(transferee), transferer_name,
- xfer_cfg->atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
- ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n",
- !!newchan, outstate);
- if (newchan) {
- /*
- * We have recalled party B (newchan). We need to give this
- * call leg the same feature flags as the original party B call
- * leg.
- */
- ast_channel_lock(transferer);
- features_datastore = ast_channel_datastore_find(transferer,
- &dial_features_info, NULL);
- if (features_datastore && (dialfeatures = features_datastore->data)) {
- struct ast_flags my_features = { 0 };
- struct ast_flags peer_features = { 0 };
-
- ast_copy_flags(&my_features, &dialfeatures->my_features,
- AST_FLAGS_ALL);
- ast_copy_flags(&peer_features, &dialfeatures->peer_features,
- AST_FLAGS_ALL);
- ast_channel_unlock(transferer);
- add_features_datastore(newchan, &my_features, &peer_features);
- } else {
- ast_channel_unlock(transferer);
- }
- break;
- }
- if (ast_check_hangup(transferee)) {
- break;
- }
-
- ++tries;
- if (xfer_cfg->atxfercallbackretries <= tries) {
- /* No more callback tries remaining. */
- break;
- }
-
- if (xfer_cfg->atxferloopdelay) {
- /* Transfer failed, sleeping */
- ast_debug(1, "Sleeping for %d ms before retrying atxfer.\n",
- xfer_cfg->atxferloopdelay);
- ast_safe_sleep(transferee, xfer_cfg->atxferloopdelay);
- if (ast_check_hangup(transferee)) {
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
- }
-
- /* Retry dialing party C. */
- ast_debug(1, "We're retrying to call %s/%s\n", "Local", xferto);
- newchan = feature_request_and_dial(transferer, transferer_name_orig,
- transferer, transferee, "Local",
- ast_channel_nativeformats(transferee), xferto,
- xfer_cfg->atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
- ast_debug(2, "Redial party C result: newchan:%d, outstate:%d\n",
- !!newchan, outstate);
- if (newchan || ast_check_hangup(transferee)) {
- break;
- }
- }
- }
- ast_indicate(transferee, AST_CONTROL_UNHOLD);
- if (!newchan) {
- /* No party C or could not callback party B. */
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- /* newchan is up, we should prepare transferee and bridge them */
- if (ast_check_hangup(newchan)) {
- ast_autoservice_chan_hangup_peer(transferee, newchan);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
- if (check_compat(transferee, newchan)) {
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
- } else {
- /*
- * Both the transferer and transferee have hungup. If newchan
- * is up, hang it up as it has no one to talk to.
- */
- ast_debug(1, "Everyone is hungup.\n");
- ast_hangup(newchan);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- /* Initiate the channel transfer of party A to party C (or recalled party B). */
- xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
- if (!xferchan) {
- ast_autoservice_chan_hangup_peer(transferee, newchan);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- /* Give party A a momentary ringback tone during transfer. */
- ast_channel_visible_indication_set(xferchan, AST_CONTROL_RINGING);
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(xferchan), ast_channel_readformat(transferee));
- ast_format_copy(ast_channel_writeformat(xferchan), ast_channel_writeformat(transferee));
-
- if (ast_channel_masquerade(xferchan, transferee)) {
- ast_hangup(xferchan);
- ast_autoservice_chan_hangup_peer(transferee, newchan);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- dash = strrchr(xferto, '@');
- if (dash) {
- /* Trim off the context. */
- *dash = '\0';
- }
- ast_explicit_goto(xferchan, transferer_real_context, xferto, 1);
- ast_channel_state_set(xferchan, AST_STATE_UP);
- ast_clear_flag(ast_channel_flags(xferchan), AST_FLAGS_ALL);
-
- /* Do the masquerade manually to make sure that is is completed. */
- ast_do_masquerade(xferchan);
-
- ast_channel_state_set(newchan, AST_STATE_UP);
- ast_clear_flag(ast_channel_flags(newchan), AST_FLAGS_ALL);
- tobj = ast_calloc(1, sizeof(*tobj));
- if (!tobj) {
- ast_hangup(xferchan);
- ast_hangup(newchan);
- ast_party_connected_line_free(&connected_line);
- return -1;
- }
-
- tobj->chan = newchan;
- tobj->peer = xferchan;
- tobj->bconfig = *config;
-
- ast_channel_lock(newchan);
- features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL);
- if (features_datastore && (dialfeatures = features_datastore->data)) {
- ast_copy_flags(&tobj->bconfig.features_callee, &dialfeatures->my_features,
- AST_FLAGS_ALL);
- }
- ast_channel_unlock(newchan);
-
- ast_channel_lock(xferchan);
- features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL);
- if (features_datastore && (dialfeatures = features_datastore->data)) {
- ast_copy_flags(&tobj->bconfig.features_caller, &dialfeatures->my_features,
- AST_FLAGS_ALL);
- }
- ast_channel_unlock(xferchan);
-
- if (tobj->bconfig.end_bridge_callback_data_fixup) {
- tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
- }
-
- /*
- * xferchan is transferee, and newchan is the transfer target
- * So...in a transfer, who is the caller and who is the callee?
- *
- * When the call is originally made, it is clear who is caller and callee.
- * When a transfer occurs, it is my humble opinion that the transferee becomes
- * the caller, and the transfer target is the callee.
- *
- * The problem is that these macros were set with the intention of the original
- * caller and callee taking those roles. A transfer can totally mess things up,
- * to be technical. What sucks even more is that you can't effectively change
- * the macros in the dialplan during the call from the transferer to the transfer
- * target because the transferee is stuck with whatever role he originally had.
- *
- * I think the answer here is just to make sure that it is well documented that
- * during a transfer, the transferee is the "caller" and the transfer target
- * is the "callee."
- *
- * This means that if party B calls party A, and party B transfers party A to
- * party C, then A has switched roles for the call. Now party A will have the
- * caller macro called on his channel instead of the callee macro.
- *
- * Luckily, the method by which the party B to party C bridge is
- * launched above ensures that the transferee is the "chan" on
- * the bridge and the transfer target is the "peer," so my idea
- * for the roles post-transfer does not require extensive code
- * changes.
- */
-
- /* Transfer party C connected line to party A */
- ast_channel_lock(transferer);
- /*
- * Due to a limitation regarding when callerID is set on a Local channel,
- * we use the transferer's connected line information here.
- */
- ast_party_connected_line_copy(&connected_line, ast_channel_connected(transferer));
- ast_channel_unlock(transferer);
- connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
- if (ast_channel_connected_line_sub(newchan, xferchan, &connected_line, 0) &&
- ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
- ast_channel_update_connected_line(xferchan, &connected_line, NULL);
- }
-
- /* Transfer party A connected line to party C */
- ast_channel_lock(xferchan);
- ast_connected_line_copy_from_caller(&connected_line, ast_channel_caller(xferchan));
- ast_channel_unlock(xferchan);
- connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
- if (ast_channel_connected_line_sub(xferchan, newchan, &connected_line, 0) &&
- ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
- ast_channel_update_connected_line(newchan, &connected_line, NULL);
- }
-
- if (ast_stream_and_wait(newchan, xfer_cfg->xfersound, ""))
- ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
- bridge_call_thread_launch(tobj);
-
- ast_party_connected_line_free(&connected_line);
- return -1;/* The transferee is masqueraded and the original bridged channels can be hungup. */
-}
-#endif
-