static int bridgeadd_exec(struct ast_channel *chan, const char *data) { struct ast_channel *c_ref; struct ast_bridge_features chan_features; struct ast_bridge *bridge; char *c_name; /* Answer the channel if needed */ if (ast_channel_state(chan) != AST_STATE_UP) { ast_answer(chan); } if (!(c_ref = ast_channel_get_by_name_prefix(data, strlen(data)))) { ast_log(LOG_WARNING, "Channel %s not found\n", data); return -1; } c_name = ast_strdupa(ast_channel_name(c_ref)); ast_channel_lock(c_ref); bridge = ast_channel_get_bridge(c_ref); ast_channel_unlock(c_ref); ast_channel_unref(c_ref); if (!bridge) { ast_log(LOG_WARNING, "Channel %s is not in a bridge\n", c_name); return -1; } ast_verb(3, "%s is joining %s in bridge %s\n", ast_channel_name(chan), c_name, bridge->uniqueid); if (ast_bridge_features_init(&chan_features) || ast_bridge_join(bridge, chan, NULL, &chan_features, NULL, 0)) { ast_log(LOG_WARNING, "%s failed to join %s in bridge %s\n", ast_channel_name(chan), c_name, bridge->uniqueid); ast_bridge_features_cleanup(&chan_features); ao2_cleanup(bridge); return -1; } ast_bridge_features_cleanup(&chan_features); ao2_cleanup(bridge); return 0; }
/*! \brief Internal built in feature for attended transfers */ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { char exten[AST_MAX_EXTENSION] = ""; struct ast_channel *chan = NULL; struct ast_bridge *attended_bridge = NULL; struct ast_bridge_features caller_features, called_features; enum ast_bridge_channel_state attended_bridge_result; struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt; const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context); /* Grab the extension to transfer to */ if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Get a channel that is the destination we wish to call */ if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Create a bridge to use to talk to the person we are calling */ if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) { ast_hangup(chan); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */ ast_bridge_features_init(&called_features); ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE); /* This is how this is going down, we are imparting the channel we called above into this bridge first */ ast_bridge_impart(attended_bridge, chan, NULL, &called_features); /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */ ast_bridge_features_init(&caller_features); ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP, (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"), attended_threeway_transfer, NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"), attended_abort_transfer, NULL); /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */ attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features); /* Since the above returned the caller features structure is of no more use */ ast_bridge_features_cleanup(&caller_features); /* Drop the channel we are transferring to out of the above bridge since it has ended */ if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) { /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */ if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) { /* We want to impart them upon the bridge and just have us return to it as normal */ ast_bridge_impart(bridge, chan, NULL, NULL); } else { ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL); } } else { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); } /* Now that all channels are out of it we can destroy the bridge and the called features structure */ ast_bridge_features_cleanup(&called_features); ast_bridge_destroy(attended_bridge); return 0; }
int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, unsigned int flags) { int res; struct ast_bridge *bridge; struct ast_bridge_features chan_features; struct ast_bridge_features *peer_features; /* Setup features. */ res = ast_bridge_features_init(&chan_features); peer_features = ast_bridge_features_new(); if (res || !peer_features) { ast_bridge_features_destroy(peer_features); ast_bridge_features_cleanup(&chan_features); bridge_failed_peer_goto(chan, peer); return -1; } if (pre_bridge_setup(chan, peer, config, &chan_features, peer_features)) { ast_bridge_features_destroy(peer_features); ast_bridge_features_cleanup(&chan_features); bridge_failed_peer_goto(chan, peer); return -1; } /* Create bridge */ bridge = ast_bridge_basic_new(); if (!bridge) { ast_bridge_features_destroy(peer_features); ast_bridge_features_cleanup(&chan_features); bridge_failed_peer_goto(chan, peer); return -1; } ast_bridge_basic_set_flags(bridge, flags); /* Put peer into the bridge */ if (ast_bridge_impart(bridge, peer, NULL, peer_features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT | AST_BRIDGE_IMPART_INHIBIT_JOIN_COLP)) { ast_bridge_destroy(bridge, 0); ast_bridge_features_cleanup(&chan_features); bridge_failed_peer_goto(chan, peer); return -1; } /* Join bridge */ ast_bridge_join(bridge, chan, NULL, &chan_features, NULL, AST_BRIDGE_JOIN_PASS_REFERENCE | AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP); /* * If the bridge was broken for a hangup that isn't real, then * don't run the h extension, because the channel isn't really * hung up. This should really only happen with * AST_SOFTHANGUP_ASYNCGOTO. */ res = -1; ast_channel_lock(chan); if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { res = 0; } ast_channel_unlock(chan); ast_bridge_features_cleanup(&chan_features); if (res && config->end_bridge_callback) { config->end_bridge_callback(config->end_bridge_callback_data); } return res; }