From 9d8a5ceb02f6559940bd94aaf163a544ce6e6f4c Mon Sep 17 00:00:00 2001 From: Matthew Jordan <mjordan@digium.com> Date: Thu, 25 Jul 2013 02:20:23 +0000 Subject: [PATCH] Move after bridge callbacks into their own file One more major refactoring to go. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395367 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_agent_pool.c | 23 +- apps/app_dial.c | 27 +- apps/app_queue.c | 3 +- bridges/bridge_builtin_features.c | 2 +- bridges/bridge_builtin_interval_features.c | 6 +- bridges/bridge_softmix.c | 2 +- funcs/func_channel.c | 7 +- include/asterisk/bridging.h | 198 +---- include/asterisk/bridging_after.h | 244 ++++++ include/asterisk/bridging_channel.h | 211 ++--- include/asterisk/bridging_channel_internal.h | 61 +- include/asterisk/bridging_features.h | 5 +- include/asterisk/bridging_internal.h | 4 +- include/asterisk/bridging_technology.h | 18 - main/bridging.c | 846 ++----------------- main/bridging_after.c | 640 ++++++++++++++ main/bridging_basic.c | 12 +- main/bridging_channel.c | 721 +++++++++------- main/features.c | 17 +- res/parking/parking_bridge_features.c | 7 +- 20 files changed, 1580 insertions(+), 1474 deletions(-) create mode 100644 include/asterisk/bridging_after.h create mode 100644 main/bridging_after.c diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c index 956a97e3f5..a9f256e3b1 100644 --- a/apps/app_agent_pool.c +++ b/apps/app_agent_pool.c @@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/bridging.h" #include "asterisk/bridging_internal.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" #include "asterisk/config_options.h" #include "asterisk/features_config.h" #include "asterisk/astobj2.h" @@ -1055,7 +1056,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru if (!caller_bridge) { /* Reset agent. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); return; } res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan, @@ -1063,7 +1064,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru if (res) { /* Reset agent. */ ast_bridge_destroy(caller_bridge); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); return; } ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0); @@ -1159,13 +1160,13 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri if (deferred_logoff) { ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); } else if (probation_timedout) { ast_debug(1, "Agent %s: Login complete.\n", agent->username); agent_devstate_changed(agent->username); } else if (ack_timedout) { ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); } else if (wrapup_timedout) { ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username); agent_devstate_changed(agent->username); @@ -1175,7 +1176,7 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri } static void agent_after_bridge_cb(struct ast_channel *chan, void *data); -static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data); +static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data); /*! * \internal @@ -1252,7 +1253,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha } if (swap) { - res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb, + res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb, agent_after_bridge_cb_failed, chan); if (res) { ast_channel_remove_bridge_role(chan, "holding_participant"); @@ -1270,7 +1271,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha * agent will have some slightly different behavior in corner * cases. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); return 0; } @@ -1620,7 +1621,7 @@ static void agent_after_bridge_cb(struct ast_channel *chan, void *data) ao2_ref(agent, -1); } -static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data) +static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data) { struct ast_channel *chan = data; struct agent_pvt *agent; @@ -1631,7 +1632,7 @@ static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, } ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n", agent->username, ast_channel_name(chan), - ast_after_bridge_cb_reason_string(reason)); + ast_bridge_after_cb_reason_string(reason)); agent_lock(agent); agent_logout(agent); ao2_ref(agent, -1); @@ -1704,7 +1705,7 @@ static void caller_abort_agent(struct agent_pvt *agent) } /* Kick the agent out of the holding bridge to reset it. */ - ast_bridge_channel_leave_bridge_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END); ast_bridge_channel_unlock(logged); } @@ -1714,7 +1715,7 @@ static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_ch if (agent->state == AGENT_STATE_CALL_PRESENT) { ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); caller_abort_agent(agent); } diff --git a/apps/app_dial.c b/apps/app_dial.c index 0a43197b44..6d7c1abb50 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/dial.h" #include "asterisk/stasis_channels.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_after.h" #include "asterisk/features_config.h" /*** DOCUMENTATION @@ -201,7 +202,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <argument name="exten" required="false" /> <argument name="priority" required="true" /> <para>If the call is answered, transfer the calling party to - the specified <replaceable>priority</replaceable> and the called party to the specified + the specified <replaceable>priority</replaceable> and the called party to the specified <replaceable>priority</replaceable> plus one.</para> <note> <para>You cannot use any additional action post answer options in conjunction with this option.</para> @@ -290,7 +291,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <argument name="arg" multiple="true"> <para>Macro arguments</para> </argument> - <para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel + <para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel before connecting to the calling channel. Arguments can be specified to the Macro using <literal>^</literal> as a delimiter. The macro can set the variable <variable>MACRO_RESULT</variable> to specify the following actions after the macro is @@ -332,7 +333,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>With <replaceable>delete</replaceable> set to <literal>1</literal>, the introduction will always be deleted.</para> </argument> - <para>This option is a modifier for the call screening/privacy mode. (See the + <para>This option is a modifier for the call screening/privacy mode. (See the <literal>p</literal> and <literal>P</literal> options.) It specifies that no introductions are to be saved in the <directory>priv-callerintros</directory> directory.</para> @@ -353,7 +354,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <argument name="mode"> <para>With <replaceable>mode</replaceable> either not specified or set to <literal>1</literal>, the originator hanging up will cause the phone to ring back immediately.</para> - <para>With <replaceable>mode</replaceable> set to <literal>2</literal>, when the operator + <para>With <replaceable>mode</replaceable> set to <literal>2</literal>, when the operator flashes the trunk, it will ring their phone back.</para> </argument> <para>Enables <emphasis>operator services</emphasis> mode. This option only @@ -1071,7 +1072,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, /* If we are calling a single channel, and not providing ringback or music, */ /* then, make them compatible for in-band tone purpose */ if (ast_channel_make_compatible(outgoing->chan, in) < 0) { - /* If these channels can not be made compatible, + /* If these channels can not be made compatible, * there is no point in continuing. The bridge * will just fail if it gets that far. */ @@ -1765,7 +1766,7 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer, /*! \page DialPrivacy Dial Privacy scripts * \par priv-callee-options script: - * \li Dial 1 if you wish this caller to reach you directly in the future, + * \li Dial 1 if you wish this caller to reach you directly in the future, * and immediately connect to their incoming call. * \li Dial 2 if you wish to send this caller to voicemail now and forevermore. * \li Dial 3 to send this caller to the torture menus, now and forevermore. @@ -1891,7 +1892,7 @@ static int setup_privacy_args(struct privacy_args *pa, } else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val); } - + if (pa->privdb_val == AST_PRIVACY_DENY) { ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status)); @@ -2021,14 +2022,14 @@ static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_ch ast_channel_lock(chan); context = ast_strdupa(ast_channel_context(chan)); ast_channel_unlock(chan); - ast_after_bridge_set_h(peer, context); + ast_bridge_set_after_h(peer, context); } else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) { ast_channel_lock(chan); context = ast_strdupa(ast_channel_context(chan)); extension = ast_strdupa(ast_channel_exten(chan)); priority = ast_channel_priority(chan); ast_channel_unlock(chan); - ast_after_bridge_set_go_on(peer, context, extension, priority, + ast_bridge_set_after_go_on(peer, context, extension, priority, opt_args[OPT_ARG_CALLEE_GO_ON]); } } @@ -2444,7 +2445,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast /* We are on the only destination. */ ast_rtp_instance_early_bridge_make_compatible(tc, chan); } - + /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(chan, tc); ast_channel_datastore_inherit(chan, tc); @@ -2698,7 +2699,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast /* If appropriate, log that we have a destination channel and set the answer time */ if (ast_channel_name(peer)) pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer)); - + ast_channel_lock(peer); number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER"); if (ast_strlen_zero(number)) { @@ -2967,7 +2968,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer)); } setup_peer_after_bridge_goto(chan, peer, &opts, opt_args); - if (ast_after_bridge_goto_setup(peer) + if (ast_bridge_setup_after_goto(peer) || ast_pbx_start(peer)) { ast_autoservice_chan_hangup_peer(chan, peer); } @@ -2997,7 +2998,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast config.end_bridge_callback = end_bridge_callback; config.end_bridge_callback_data = chan; config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; - + if (moh) { moh = 0; ast_moh_stop(chan); diff --git a/apps/app_queue.c b/apps/app_queue.c index 8c5291a0df..6b89a70463 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -109,6 +109,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stasis_channels.h" #include "asterisk/stasis_message_router.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_after.h" /* Define, to debug reference counts on queues, without debugging reference counts on queue members */ /* #define REF_DEBUG_ONLY_QUEUES */ @@ -5293,7 +5294,7 @@ static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_ch extension = ast_strdupa(ast_channel_exten(chan)); priority = ast_channel_priority(chan); ast_channel_unlock(chan); - ast_after_bridge_set_go_on(peer, context, extension, priority, + ast_bridge_set_after_go_on(peer, context, extension, priority, opt_args[OPT_ARG_CALLEE_GO_ON]); } } diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index 1252240ac9..00c88710cd 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -483,7 +483,7 @@ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel * * bridge_channel to force the channel out of the bridge and the * core takes care of the rest. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); return 0; } diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c index 7e5291ac8e..5a02991d4e 100644 --- a/bridges/bridge_builtin_interval_features.c +++ b/bridges/bridge_builtin_interval_features.c @@ -58,7 +58,7 @@ static int bridge_features_duration_callback(struct ast_bridge *bridge, struct a ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE); } - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", ast_channel_name(bridge_channel->chan)); return -1; @@ -114,7 +114,7 @@ static int bridge_features_connect_callback(struct ast_bridge *bridge, struct as { struct ast_bridge_features_limits *limits = hook_pvt; - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { return -1; } @@ -126,7 +126,7 @@ static int bridge_features_warning_callback(struct ast_bridge *bridge, struct as { struct ast_bridge_features_limits *limits = hook_pvt; - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* If we aren't in the wait state, something more important than this warning is happening and we should skip it. */ limits_interval_playback(bridge, bridge_channel, limits, limits->warning_sound); } diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index dcf11ec6a1..503c0b9a58 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -576,7 +576,7 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri ast_mutex_unlock(&sc->lock); if (update_talking != -1) { - ast_bridge_notify_talking(bridge_channel, update_talking); + ast_bridge_channel_notify_talking(bridge_channel, update_talking); } } diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 4d098c3470..f4aa76322b 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" #include "asterisk/global_datastores.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" /*** DOCUMENTATION <function name="CHANNELS" language="en_US"> @@ -531,7 +532,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function, locked_copy_string(chan, buf, ast_print_namedgroups(&tmp_str, ast_channel_named_pickupgroups(chan)), len); } else if (!strcasecmp(data, "after_bridge_goto")) { - ast_after_bridge_goto_read(chan, buf, len); + ast_bridge_read_after_goto(chan, buf, len); } else if (!strcasecmp(data, "amaflags")) { ast_channel_lock(chan); snprintf(buf, len, "%d", ast_channel_amaflags(chan)); @@ -575,9 +576,9 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio locked_string_field_set(chan, userfield, value); else if (!strcasecmp(data, "after_bridge_goto")) { if (ast_strlen_zero(value)) { - ast_after_bridge_goto_discard(chan); + ast_bridge_discard_after_goto(chan); } else { - ast_after_bridge_set_go_on(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), value); + ast_bridge_set_after_go_on(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), value); } } else if (!strcasecmp(data, "amaflags")) { ast_channel_lock(chan); diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h index bc16ae0bc1..7e22dba648 100644 --- a/include/asterisk/bridging.h +++ b/include/asterisk/bridging.h @@ -447,7 +447,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan); * If channel specific features are enabled a pointer to the features structure * can be specified in the features parameter. */ -enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, +enum bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, @@ -884,202 +884,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, */ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, struct ast_channel *to_transfer_target); -/*! - * \brief Set channel to goto specific location after the bridge. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * \param context Context to goto after bridge. - * \param exten Exten to goto after bridge. - * \param priority Priority to goto after bridge. - * - * \note chan is locked by this function. - * - * \details Add a channel datastore to setup the goto location - * when the channel leaves the bridge and run a PBX from there. - * - * \return Nothing - */ -void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority); - -/*! - * \brief Set channel to run the h exten after the bridge. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * \param context Context to goto after bridge. - * - * \note chan is locked by this function. - * - * \details Add a channel datastore to setup the goto location - * when the channel leaves the bridge and run a PBX from there. - * - * \return Nothing - */ -void ast_after_bridge_set_h(struct ast_channel *chan, const char *context); - -/*! - * \brief Set channel to go on in the dialplan after the bridge. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * \param context Current context of the caller channel. - * \param exten Current exten of the caller channel. - * \param priority Current priority of the caller channel - * \param parseable_goto User specified goto string from dialplan. - * - * \note chan is locked by this function. - * - * \details Add a channel datastore to setup the goto location - * when the channel leaves the bridge and run a PBX from there. - * - * If parseable_goto then use the given context/exten/priority - * as the relative position for the parseable_goto. - * Else goto the given context/exten/priority+1. - * - * \return Nothing - */ -void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto); - -/*! - * \brief Setup any after bridge goto location to begin execution. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * - * \note chan is locked by this function. - * - * \details Pull off any after bridge goto location datastore and - * setup for dialplan execution there. - * - * \retval 0 on success. The goto location is set for a PBX to run it. - * \retval non-zero on error or no goto location. - * - * \note If the after bridge goto is set to run an h exten it is - * run here immediately. - */ -int ast_after_bridge_goto_setup(struct ast_channel *chan); - -/*! - * \brief Run a PBX on any after bridge goto location. - * \since 12.0.0 - * - * \param chan Channel to execute after bridge goto location. - * - * \note chan is locked by this function. - * - * \details Pull off any after bridge goto location datastore - * and run a PBX at that location. - * - * \note On return, the chan pointer is no longer valid because - * the channel has hung up. - * - * \return Nothing - */ -void ast_after_bridge_goto_run(struct ast_channel *chan); - -/*! - * \brief Discard channel after bridge goto location. - * \since 12.0.0 - * - * \param chan Channel to discard after bridge goto location. - * - * \note chan is locked by this function. - * - * \return Nothing - */ -void ast_after_bridge_goto_discard(struct ast_channel *chan); - -/*! - * \brief Read after bridge goto if it exists - * \since 12.0.0 - * - * \param chan Channel to read the after bridge goto parseable goto string from - * \param buffer Buffer to write the after bridge goto data to - * \param buf_size size of the buffer being written to - */ -void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t buf_size); - -/*! Reason the the after bridge callback will not be called. */ -enum ast_after_bridge_cb_reason { - /*! The datastore is being destroyed. Likely due to hangup. (Enum value must be zero.) */ - AST_AFTER_BRIDGE_CB_REASON_DESTROY, - /*! Something else replaced the callback with another. */ - AST_AFTER_BRIDGE_CB_REASON_REPLACED, - /*! The callback was removed because of a masquerade. (fixup) */ - AST_AFTER_BRIDGE_CB_REASON_MASQUERADE, - /*! The channel was departed from the bridge. */ - AST_AFTER_BRIDGE_CB_REASON_DEPART, - /*! Was explicitly removed by external code. */ - AST_AFTER_BRIDGE_CB_REASON_REMOVED, -}; - -/*! - * \brief After bridge callback failed. - * \since 12.0.0 - * - * \param reason Reason callback is failing. - * \param data Extra data what setup the callback wanted to pass. - * - * \note Called when the channel leaves the bridging system or - * is destroyed. - * - * \return Nothing - */ -typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data); - -/*! - * \brief After bridge callback function. - * \since 12.0.0 - * - * \param chan Channel just leaving bridging system. - * \param data Extra data what setup the callback wanted to pass. - * - * \return Nothing - */ -typedef void (*ast_after_bridge_cb)(struct ast_channel *chan, void *data); - -/*! - * \brief Discard channel after bridge callback. - * \since 12.0.0 - * - * \param chan Channel to discard after bridge callback. - * \param reason Why are we doing this. - * - * \note chan is locked by this function. - * - * \return Nothing - */ -void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason); - -/*! - * \brief Setup an after bridge callback for when the channel leaves the bridging system. - * \since 12.0.0 - * - * \param chan Channel to setup an after bridge callback on. - * \param callback Function to call when the channel leaves the bridging system. - * \param failed Function to call when it will not be calling the callback. - * \param data Extra data to pass with the callback. - * - * \note chan is locked by this function. - * - * \note failed is called when the channel leaves the bridging - * system or is destroyed. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data); - -/*! - * \brief Get a string representation of an after bridge callback reason - * \since 12.0.0 - * - * \param reason The reason to interpret to a string - * \retval NULL Unrecognized reason - * \retval non-NULL String representation of reason - */ -const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason); /*! * \brief Get a container of all channels in the bridge diff --git a/include/asterisk/bridging_after.h b/include/asterisk/bridging_after.h new file mode 100644 index 0000000000..53f30b9ad2 --- /dev/null +++ b/include/asterisk/bridging_after.h @@ -0,0 +1,244 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett <rmudgett@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief After Bridge Execution API + * + * \author Richard Mudgett <rmudgett@digium.com> + * + * See Also: + * \arg \ref AstCREDITS + */ + +#ifndef _ASTERISK_BRIDGING_AFTER_H +#define _ASTERISK_BRIDGING_AFTER_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! Reason the the after bridge callback will not be called. */ +enum ast_bridge_after_cb_reason { + /*! The datastore is being destroyed. Likely due to hangup. (Enum value must be zero.) */ + AST_BRIDGE_AFTER_CB_REASON_DESTROY, + /*! Something else replaced the callback with another. */ + AST_BRIDGE_AFTER_CB_REASON_REPLACED, + /*! The callback was removed because of a masquerade. (fixup) */ + AST_BRIDGE_AFTER_CB_REASON_MASQUERADE, + /*! The channel was departed from the bridge. */ + AST_BRIDGE_AFTER_CB_REASON_DEPART, + /*! Was explicitly removed by external code. */ + AST_BRIDGE_AFTER_CB_REASON_REMOVED, +}; + +/*! + * \brief Set channel to goto specific location after the bridge. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * \param context Context to goto after bridge. + * \param exten Exten to goto after bridge. + * \param priority Priority to goto after bridge. + * + * \note chan is locked by this function. + * + * \details Add a channel datastore to setup the goto location + * when the channel leaves the bridge and run a PBX from there. + * + * \return Nothing + */ +void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority); + +/*! + * \brief Set channel to run the h exten after the bridge. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * \param context Context to goto after bridge. + * + * \note chan is locked by this function. + * + * \details Add a channel datastore to setup the goto location + * when the channel leaves the bridge and run a PBX from there. + * + * \return Nothing + */ +void ast_bridge_set_after_h(struct ast_channel *chan, const char *context); + +/*! + * \brief Set channel to go on in the dialplan after the bridge. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * \param context Current context of the caller channel. + * \param exten Current exten of the caller channel. + * \param priority Current priority of the caller channel + * \param parseable_goto User specified goto string from dialplan. + * + * \note chan is locked by this function. + * + * \details Add a channel datastore to setup the goto location + * when the channel leaves the bridge and run a PBX from there. + * + * If parseable_goto then use the given context/exten/priority + * as the relative position for the parseable_goto. + * Else goto the given context/exten/priority+1. + * + * \return Nothing + */ +void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto); + +/*! + * \brief Setup any after bridge goto location to begin execution. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * + * \note chan is locked by this function. + * + * \details Pull off any after bridge goto location datastore and + * setup for dialplan execution there. + * + * \retval 0 on success. The goto location is set for a PBX to run it. + * \retval non-zero on error or no goto location. + * + * \note If the after bridge goto is set to run an h exten it is + * run here immediately. + */ +int ast_bridge_setup_after_goto(struct ast_channel *chan); + +/*! + * \brief Run any after bridge callback. + * \since 12.0.0 + * + * \param chan Channel to run after bridge callback. + * + * \return Nothing + */ +void ast_bridge_run_after_callback(struct ast_channel *chan); + +/*! + * \brief Run discarding any after bridge callbacks. + * \since 12.0.0 + * + * \param chan Channel to run after bridge callback. + * + * \return Nothing + */ +void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason); + +/*! + * \brief Run a PBX on any after bridge goto location. + * \since 12.0.0 + * + * \param chan Channel to execute after bridge goto location. + * + * \note chan is locked by this function. + * + * \details Pull off any after bridge goto location datastore + * and run a PBX at that location. + * + * \note On return, the chan pointer is no longer valid because + * the channel has hung up. + * + * \return Nothing + */ +void ast_bridge_run_after_goto(struct ast_channel *chan); + +/*! + * \brief Discard channel after bridge goto location. + * \since 12.0.0 + * + * \param chan Channel to discard after bridge goto location. + * + * \note chan is locked by this function. + * + * \return Nothing + */ +void ast_bridge_discard_after_goto(struct ast_channel *chan); + +/*! + * \brief Read after bridge goto if it exists + * \since 12.0.0 + * + * \param chan Channel to read the after bridge goto parseable goto string from + * \param buffer Buffer to write the after bridge goto data to + * \param buf_size size of the buffer being written to + */ +void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size); + +/*! + * \brief After bridge callback failed. + * \since 12.0.0 + * + * \param reason Reason callback is failing. + * \param data Extra data what setup the callback wanted to pass. + * + * \note Called when the channel leaves the bridging system or + * is destroyed. + * + * \return Nothing + */ +typedef void (*ast_bridge_after_cb_failed)(enum ast_bridge_after_cb_reason reason, void *data); + +/*! + * \brief After bridge callback function. + * \since 12.0.0 + * + * \param chan Channel just leaving bridging system. + * \param data Extra data what setup the callback wanted to pass. + * + * \return Nothing + */ +typedef void (*ast_bridge_after_cb)(struct ast_channel *chan, void *data); + +/*! + * \brief Setup an after bridge callback for when the channel leaves the bridging system. + * \since 12.0.0 + * + * \param chan Channel to setup an after bridge callback on. + * \param callback Function to call when the channel leaves the bridging system. + * \param failed Function to call when it will not be calling the callback. + * \param data Extra data to pass with the callback. + * + * \note chan is locked by this function. + * + * \note failed is called when the channel leaves the bridging + * system or is destroyed. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data); + +/*! + * \brief Get a string representation of an after bridge callback reason + * \since 12.0.0 + * + * \param reason The reason to interpret to a string + * \retval NULL Unrecognized reason + * \retval non-NULL String representation of reason + */ +const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_BRIDGING_H */ diff --git a/include/asterisk/bridging_channel.h b/include/asterisk/bridging_channel.h index cdbb2ec5d7..f58fba7dca 100644 --- a/include/asterisk/bridging_channel.h +++ b/include/asterisk/bridging_channel.h @@ -20,29 +20,29 @@ /*! * \file - * \brief Bridging Channel API + * \page AstBridgeChannel Bridging Channel API * * An API that act on a channel in a bridge. Note that while the * \ref ast_bridge_channel is owned by a channel, it should only be used * by members of the bridging system. The only places where this API should * be used is: - * - The \ref AstBridging API itself - * - Bridge mixing technologies - * - Bridge sub-classes + * \arg \ref AstBridging API itself + * \arg Bridge mixing technologies + * \arg Bridge sub-classes * * In general, anywhere else it is unsafe to use this API. Care should be * taken when using this API to ensure that the locking order remains * correct. The locking order must be: - * - The \ref ast_bridge - * - The \ref ast_bridge_channel - * - The \ref ast_channel + * \arg The \ref \c ast_bridge + * \arg The \ref \c ast_bridge_channel + * \arg The \ref \c ast_channel * * \author Joshua Colp <jcolp@digium.com> * \author Richard Mudgett <rmudgett@digium.com> * \author Matt Jordan <mjordan@digium.com> * * See Also: - * \ref bridging.h + * \arg \ref AstBridging * \arg \ref AstCREDITS */ @@ -56,22 +56,22 @@ extern "C" { #include "asterisk/bridging_technology.h" /*! \brief State information about a bridged channel */ -enum ast_bridge_channel_state { +enum bridge_channel_state { /*! Waiting for a signal (Channel in the bridge) */ - AST_BRIDGE_CHANNEL_STATE_WAIT = 0, + BRIDGE_CHANNEL_STATE_WAIT = 0, /*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */ - AST_BRIDGE_CHANNEL_STATE_END, + BRIDGE_CHANNEL_STATE_END, /*! Bridged channel was forced out. Don't dissolve the bridge regardless */ - AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, }; -enum ast_bridge_channel_thread_state { +enum bridge_channel_thread_state { /*! Bridge channel thread is idle/waiting. */ - AST_BRIDGE_CHANNEL_THREAD_IDLE, + BRIDGE_CHANNEL_THREAD_IDLE, /*! Bridge channel thread is writing a normal/simple frame. */ - AST_BRIDGE_CHANNEL_THREAD_SIMPLE, + BRIDGE_CHANNEL_THREAD_SIMPLE, /*! Bridge channel thread is processing a frame. */ - AST_BRIDGE_CHANNEL_THREAD_FRAME, + BRIDGE_CHANNEL_THREAD_FRAME, }; struct ast_bridge; @@ -85,7 +85,7 @@ struct ast_bridge_channel { /*! Condition, used if we want to wake up a thread waiting on the bridged channel */ ast_cond_t cond; /*! Current bridged channel state */ - enum ast_bridge_channel_state state; + enum bridge_channel_state state; /*! Asterisk channel participating in the bridge */ struct ast_channel *chan; /*! Asterisk channel we are swapping with (if swapping) */ @@ -154,7 +154,7 @@ struct ast_bridge_channel { * * \note Needs to be atomically settable. */ - enum ast_bridge_channel_thread_state activity; + enum bridge_channel_thread_state activity; }; /*! @@ -216,6 +216,24 @@ static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_ */ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel); +/*! + * \brief Lets the bridging indicate when a bridge channel has stopped or started talking. + * + * \note All DSP functionality on the bridge has been pushed down to the lowest possible + * layer, which in this case is the specific bridging technology being used. Since it + * is necessary for the knowledge of which channels are talking to make its way up to the + * application, this function has been created to allow the bridging technology to communicate + * that information with the bridging core. + * + * \param bridge_channel The bridge channel that has either started or stopped talking. + * \param started_talking set to 1 when this indicates the channel has started talking set to 0 + * when this indicates the channel has stopped talking. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking); + /*! * \brief Set bridge channel state to leave bridge (if not leaving already). * @@ -225,14 +243,14 @@ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel); * Example usage: * * \code - * ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); * \endcode * * This places the channel pointed to by bridge_channel into the - * state AST_BRIDGE_CHANNEL_STATE_END if it was - * AST_BRIDGE_CHANNEL_STATE_WAIT before. + * state BRIDGE_CHANNEL_STATE_END if it was + * BRIDGE_CHANNEL_STATE_WAIT before. */ -void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state); /*! * \brief Set bridge channel state to leave bridge (if not leaving already). @@ -243,14 +261,82 @@ void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, * Example usage: * * \code - * ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); * \endcode * * This places the channel pointed to by bridge_channel into the - * state AST_BRIDGE_CHANNEL_STATE_END if it was - * AST_BRIDGE_CHANNEL_STATE_WAIT before. + * state BRIDGE_CHANNEL_STATE_END if it was + * BRIDGE_CHANNEL_STATE_WAIT before. + */ +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state); + +/*! + * \brief Get the peer bridge channel of a two party bridge. + * \since 12.0.0 + * + * \param bridge_channel What to get the peer of. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \note This is an internal bridge function. + * + * \retval peer on success. + * \retval NULL no peer channel. + */ +struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_internal_join + * \since 12.0.0 + * + * \param bridge_channel Channel to restore + */ +void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Adjust the bridge_channel's bridge merge inhibit request count. + * \since 12.0.0 + * + * \param bridge_channel What to operate on. + * \param request Inhibit request increment. + * (Positive to add requests. Negative to remove requests.) + * + * \note This API call is meant for internal bridging operations. + * + * \retval bridge adjusted merge inhibit with reference count. + */ +struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request); + +/*! + * \internal + * \brief Update the linkedids for all channels in a bridge + * \since 12.0.0 + * + * \param bridge_channel The channel joining the bridge + * \param swap The channel being swapped out of the bridge. May be NULL. + * + * \note The bridge must be locked prior to calling this function. This should be called + * during a \ref bridge_channel_internal_push operation, typically by a sub-class of a bridge + */ +void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); + +/*! + * \internal + * \brief Update the accountcodes for a channel entering a bridge + * \since 12.0.0 + * + * This function updates the accountcode and peeraccount on channels in two-party + * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense - + * however accountcode propagation will still occur if the channel joining has an + * accountcode. + * + * \param bridge_channel The channel joining the bridge + * \param swap The channel being swapped out of the bridge. May be NULL. + * + * \note The bridge must be locked prior to calling this function. This should be called + * during a \ref bridge_channel_internal_push operation, typically by a sub-class of a bridge */ -void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); +void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); /*! * \brief Write a frame to the specified bridge_channel. @@ -497,79 +583,6 @@ int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data); -/*! - * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join - * \since 12.0.0 - * - * \param bridge_channel Channel to restore - */ -void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel); - -/*! - * \brief Get the peer bridge channel of a two party bridge. - * \since 12.0.0 - * - * \param bridge_channel What to get the peer of. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \note This is an internal bridge function. - * - * \retval peer on success. - * \retval NULL no peer channel. - */ -struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel); - -struct blind_transfer_data { - char exten[AST_MAX_EXTENSION]; - char context[AST_MAX_CONTEXT]; -}; - -/*! - * \brief Adjust the bridge_channel's bridge merge inhibit request count. - * \since 12.0.0 - * - * \param bridge_channel What to operate on. - * \param request Inhibit request increment. - * (Positive to add requests. Negative to remove requests.) - * - * \note This API call is meant for internal bridging operations. - * - * \retval bridge adjusted merge inhibit with reference count. - */ -struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request); - -/*! - * \internal - * \brief Update the linkedids for all channels in a bridge - * \since 12.0.0 - * - * \param bridge_channel The channel joining the bridge - * \param swap The channel being swapped out of the bridge. May be NULL. - * - * \note The bridge must be locked prior to calling this function. This should be called - * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge - */ -void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); - -/*! - * \internal - * \brief Update the accountcodes for a channel entering a bridge - * \since 12.0.0 - * - * This function updates the accountcode and peeraccount on channels in two-party - * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense - - * however accountcode propagation will still occur if the channel joining has an - * accountcode. - * - * \param bridge_channel The channel joining the bridge - * \param swap The channel being swapped out of the bridge. May be NULL. - * - * \note The bridge must be locked prior to calling this function. This should be called - * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge - */ -void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); - #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/bridging_channel_internal.h b/include/asterisk/bridging_channel_internal.h index cbfa20aa6b..6c3add13b0 100644 --- a/include/asterisk/bridging_channel_internal.h +++ b/include/asterisk/bridging_channel_internal.h @@ -16,6 +16,9 @@ * at the top of the source tree. */ +#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H +#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H + /*! * \file * \brief Private Bridging Channel API @@ -23,16 +26,14 @@ * \author Matt Jordan <mjordan@digium.com> * * A private API to manipulate channels in a bridge. These can be called on a channel in - * a bridge by the bridging API, but should not be called by external consumers of the - * Bridging API. + * a bridge by \ref bridging.c. These functions should not be called elsewhere, including + * by other members of the Bridging API. * * See Also: * \arg \ref AstCREDITS + * \arg \ref Ast */ -#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H -#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H - /*! * \internal * \brief Actions that can be taken on a channel in a bridge @@ -71,6 +72,18 @@ enum bridge_channel_action_type { BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING, }; +/*! + * \internal + * \brief Allocate a new ao2 ref counted bridge_channel + * \since 12.0.0 + * + * \param bridge The bridge to make the bridge_channel for + * + * \retval NULL on error + * \retval ao2 ref counted object on success + */ +struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge); + /*! * \internal * \brief Push the bridge channel into its specified bridge. @@ -83,7 +96,7 @@ enum bridge_channel_action_type { * \retval 0 on success. * \retval -1 on failure. The channel did not get pushed. */ -int bridge_channel_push(struct ast_bridge_channel *bridge_channel); +int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel); /*! * \internal @@ -96,7 +109,7 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel); * * \return Nothing */ -void bridge_channel_pull(struct ast_bridge_channel *bridge_channel); +void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel); /*! * \internal @@ -108,7 +121,7 @@ void bridge_channel_pull(struct ast_bridge_channel *bridge_channel); * it is in the bridge. It will return when the channel has been instructed to * leave the bridge. */ -void bridge_channel_join(struct ast_bridge_channel *bridge_channel); +void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel); /*! * \internal @@ -118,7 +131,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel); * \param bridge_channel The channel in the bridge * \note This function assumes that \ref bridge_channel is already locked */ -void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel); +void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel); /*! * \internal @@ -127,7 +140,7 @@ void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel); * \param bridge_channel The channel in the bridge * \note This function assumes that \ref bridge_channel is already locked */ -void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel); +void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel); /*! * \internal @@ -146,11 +159,35 @@ void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel); * \retval 0 on success. * \retval -1 on error. */ -int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, +int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee, const char *exten, const char *context, transfer_channel_cb new_channel_cb, void *user_data); -int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, +/*! + * \internal + * \brief Queue an attended transfer action on a transferee bridge channel + * + * This is only relevant for when an attended transfer is performed on a two-party + * bridge. The transferee's bridge channel will have an attended transfer bridge + * action queued onto it. + * + * \param transferee The channel to have the action queued on + * \param unbridged_chan The unbridged channel who is the target of the attended + * transfer + * + * \retval 0 on success. + * \retval -1 on error. + */ +int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee, struct ast_channel *unbridged_chan); +/*! + * \internal + * \brief Return whether or not the bridge_channel would allow optimization + * + * \retval 0 if optimization is not allowed + * \retval non-zero if optimization is allowed + */ +int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel); + #endif /* _ASTERISK_PRIVATE_BRIDGING_H */ diff --git a/include/asterisk/bridging_features.h b/include/asterisk/bridging_features.h index dacb6c698f..05fdf2587b 100644 --- a/include/asterisk/bridging_features.h +++ b/include/asterisk/bridging_features.h @@ -16,7 +16,8 @@ * at the top of the source tree. */ -/*! \file +/*! + * \file * \brief Channel Bridging API * \author Joshua Colp <jcolp@digium.com> */ @@ -75,7 +76,7 @@ enum ast_bridge_builtin_feature { * how it was imparted. * * \note Joined channels exit the bridge with - * AST_BRIDGE_CHANNEL_STATE_END_WITH_DISSOLVE. + * BRIDGE_CHANNEL_STATE_END_WITH_DISSOLVE. */ AST_BRIDGE_BUILTIN_HANGUP, /*! diff --git a/include/asterisk/bridging_internal.h b/include/asterisk/bridging_internal.h index cafa693f59..18ef56e964 100644 --- a/include/asterisk/bridging_internal.h +++ b/include/asterisk/bridging_internal.h @@ -180,8 +180,8 @@ void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request); * \param colp_update Whether to perform COLP updates. * * \details - * After a series of bridge_channel_push and - * bridge_channel_pull calls, you need to call this function + * After a series of bridge_channel_internal_push and + * bridge_channel_internal_pull calls, you need to call this function * to cause the bridge to complete restructuring for the change * in the channel makeup of the bridge. * diff --git a/include/asterisk/bridging_technology.h b/include/asterisk/bridging_technology.h index e037b7490e..534eb54174 100644 --- a/include/asterisk/bridging_technology.h +++ b/include/asterisk/bridging_technology.h @@ -202,24 +202,6 @@ int __ast_bridge_technology_register(struct ast_bridge_technology *technology, s */ int ast_bridge_technology_unregister(struct ast_bridge_technology *technology); -/*! - * \brief Lets the bridging indicate when a bridge channel has stopped or started talking. - * - * \note All DSP functionality on the bridge has been pushed down to the lowest possible - * layer, which in this case is the specific bridging technology being used. Since it - * is necessary for the knowledge of which channels are talking to make its way up to the - * application, this function has been created to allow the bridging technology to communicate - * that information with the bridging core. - * - * \param bridge_channel The bridge channel that has either started or stopped talking. - * \param started_talking set to 1 when this indicates the channel has started talking set to 0 - * when this indicates the channel has stopped talking. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking); - /*! * \brief Suspend a bridge technology from consideration * diff --git a/main/bridging.c b/main/bridging.c index dda0732365..1e1e6c763d 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -38,9 +38,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/linkedlists.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_internal.h" +#include "asterisk/bridging_channel_internal.h" #include "asterisk/bridging_basic.h" #include "asterisk/bridging_technology.h" #include "asterisk/bridging_channel.h" +#include "asterisk/bridging_after.h" #include "asterisk/stasis_bridging.h" #include "asterisk/stasis_channels.h" #include "asterisk/app.h" @@ -61,8 +64,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/parking.h" #include "asterisk/core_local.h" #include "asterisk/core_unreal.h" -#include "asterisk/bridging_internal.h" -#include "asterisk/bridging_channel_internal.h" /*! All bridges container. */ static struct ao2_container *bridges; @@ -268,7 +269,7 @@ void bridge_dissolve(struct ast_bridge *bridge) /* BUGBUG need a cause code on the bridge for the later ejected channels. */ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } /* Must defer dissolving bridge because it is already locked. */ @@ -1343,24 +1344,6 @@ static void set_bridge_peer_vars(struct ast_bridge *bridge) } } -/*! - * \internal - * \brief Notify the bridge that it has been reconfigured. - * \since 12.0.0 - * - * \param bridge Reconfigured bridge. - * \param colp_update Whether to perform COLP updates. - * - * \details - * After a series of bridge_channel_push and - * bridge_channel_pull calls, you need to call this function - * to cause the bridge to complete restructuring for the change - * in the channel makeup of the bridge. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) { if (!bridge->reconfigured) { @@ -1387,726 +1370,17 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) } } -/*! - * \internal - * \brief Close a pipe. - * \since 12.0.0 - * - * \param my_pipe What to close. - * - * \return Nothing - */ -static void pipe_close(int *my_pipe) -{ - if (my_pipe[0] > -1) { - close(my_pipe[0]); - my_pipe[0] = -1; - } - if (my_pipe[1] > -1) { - close(my_pipe[1]); - my_pipe[1] = -1; - } -} - -/*! - * \internal - * \brief Initialize a pipe as non-blocking. - * \since 12.0.0 - * - * \param my_pipe What to initialize. - * - * \retval 0 on success. - * \retval -1 on error. - */ -static int pipe_init_nonblock(int *my_pipe) -{ - int flags; - - my_pipe[0] = -1; - my_pipe[1] = -1; - if (pipe(my_pipe)) { - ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n"); - return -1; - } - flags = fcntl(my_pipe[0], F_GETFL); - if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) { - ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n", - errno, strerror(errno)); - return -1; - } - flags = fcntl(my_pipe[1], F_GETFL); - if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) { - ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n", - errno, strerror(errno)); - return -1; - } - return 0; -} - -/* Destroy elements of the bridge channel structure and the bridge channel structure itself */ -static void bridge_channel_destroy(void *obj) -{ - struct ast_bridge_channel *bridge_channel = obj; - struct ast_frame *fr; - - if (bridge_channel->callid) { - bridge_channel->callid = ast_callid_unref(bridge_channel->callid); - } - - if (bridge_channel->bridge) { - ao2_ref(bridge_channel->bridge, -1); - bridge_channel->bridge = NULL; - } - - /* Flush any unhandled wr_queue frames. */ - while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) { - ast_frfree(fr); - } - pipe_close(bridge_channel->alert_pipe); - - ast_cond_destroy(&bridge_channel->cond); -} - -static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge) +struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan) { struct ast_bridge_channel *bridge_channel; - bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy); - if (!bridge_channel) { - return NULL; - } - ast_cond_init(&bridge_channel->cond, NULL); - if (pipe_init_nonblock(bridge_channel->alert_pipe)) { - ao2_ref(bridge_channel, -1); - return NULL; - } - if (bridge) { - bridge_channel->bridge = bridge; - ao2_ref(bridge_channel->bridge, +1); - } - - return bridge_channel; -} - -struct after_bridge_cb_node { - /*! Next list node. */ - AST_LIST_ENTRY(after_bridge_cb_node) list; - /*! Desired callback function. */ - ast_after_bridge_cb callback; - /*! After bridge callback will not be called and destroy any resources data may contain. */ - ast_after_bridge_cb_failed failed; - /*! Extra data to pass to the callback. */ - void *data; - /*! Reason the after bridge callback failed. */ - enum ast_after_bridge_cb_reason reason; -}; - -struct after_bridge_cb_ds { - /*! After bridge callbacks container. */ - AST_LIST_HEAD(, after_bridge_cb_node) callbacks; -}; - -/*! - * \internal - * \brief Indicate after bridge callback failed. - * \since 12.0.0 - * - * \param node After bridge callback node. - * - * \return Nothing - */ -static void after_bridge_cb_failed(struct after_bridge_cb_node *node) -{ - if (node->failed) { - node->failed(node->reason, node->data); - node->failed = NULL; - } -} - -/*! - * \internal - * \brief Run discarding any after bridge callbacks. - * \since 12.0.0 - * - * \param after_bridge After bridge callback container process. - * \param reason Why are we doing this. - * - * \return Nothing - */ -static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_node *node; - - for (;;) { - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - if (!node) { - break; - } - if (!node->reason) { - node->reason = reason; - } - after_bridge_cb_failed(node); - ast_free(node); - } -} - -/*! - * \internal - * \brief Destroy the after bridge callback datastore. - * \since 12.0.0 - * - * \param data After bridge callback data to destroy. - * - * \return Nothing - */ -static void after_bridge_cb_destroy(void *data) -{ - struct after_bridge_cb_ds *after_bridge = data; - - after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY); - - AST_LIST_HEAD_DESTROY(&after_bridge->callbacks); - ast_free(after_bridge); -} - -/*! - * \internal - * \brief Fixup the after bridge callback datastore. - * \since 12.0.0 - * - * \param data After bridge callback data to fixup. - * \param old_chan The datastore is moving from this channel. - * \param new_chan The datastore is moving to this channel. - * - * \return Nothing - */ -static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE); -} - -static const struct ast_datastore_info after_bridge_cb_info = { - .type = "after-bridge-cb", - .destroy = after_bridge_cb_destroy, - .chan_fixup = after_bridge_cb_fixup, -}; - -/*! - * \internal - * \brief Setup/create an after bridge callback datastore container. - * \since 12.0.0 - * - * \param chan Channel to setup/create the after bridge callback container on. - * - * \retval after_bridge datastore container on success. - * \retval NULL on error. - */ -static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - struct after_bridge_cb_ds *after_bridge; - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); - if (datastore) { - return datastore->data; - } - - /* Create a new datastore. */ - datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); - if (!datastore) { - return NULL; - } - after_bridge = ast_calloc(1, sizeof(*after_bridge)); - if (!after_bridge) { - ast_datastore_free(datastore); - return NULL; - } - AST_LIST_HEAD_INIT(&after_bridge->callbacks); - datastore->data = after_bridge; - ast_channel_datastore_add(chan, datastore); - - return datastore->data; -} - -/*! - * \internal - * \brief Find an after bridge callback datastore container. - * \since 12.0.0 - * - * \param chan Channel to find the after bridge callback container on. - * - * \retval after_bridge datastore container on success. - * \retval NULL on error. - */ -static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); - if (!datastore) { - return NULL; - } - return datastore->data; -} - -/*! - * \internal - * \brief Run any after bridge callback. - * \since 12.0.0 - * - * \param chan Channel to run after bridge callback. - * - * \return Nothing - */ -static void after_bridge_callback_run(struct ast_channel *chan) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *node; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - for (;;) { - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - if (!node) { + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (bridge_channel->chan == chan) { break; } - if (node->reason) { - after_bridge_cb_failed(node); - } else { - node->failed = NULL; - node->callback(chan, node->data); - } - ast_free(node); } -} -/*! - * \internal - * \brief Run discarding any after bridge callbacks. - * \since 12.0.0 - * - * \param chan Channel to run after bridge callback. - * - * \return Nothing - */ -static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_ds *after_bridge; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - after_bridge_cb_run_discard(after_bridge, reason); -} - -void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *node; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_LAST(&after_bridge->callbacks); - if (node && !node->reason) { - node->reason = reason; - } - AST_LIST_UNLOCK(&after_bridge->callbacks); -} - -int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *new_node; - struct after_bridge_cb_node *last_node; - - /* Sanity checks. */ - ast_assert(chan != NULL); - if (!chan || !callback) { - return -1; - } - - after_bridge = after_bridge_cb_setup(chan); - if (!after_bridge) { - return -1; - } - - /* Create a new callback node. */ - new_node = ast_calloc(1, sizeof(*new_node)); - if (!new_node) { - return -1; - } - new_node->callback = callback; - new_node->failed = failed; - new_node->data = data; - - /* Put it in the container disabling any previously active one. */ - AST_LIST_LOCK(&after_bridge->callbacks); - last_node = AST_LIST_LAST(&after_bridge->callbacks); - if (last_node && !last_node->reason) { - last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED; - } - AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - return 0; -} - -const char *reason_strings[] = { - [AST_AFTER_BRIDGE_CB_REASON_DESTROY] = "Channel destroyed (hungup)", - [AST_AFTER_BRIDGE_CB_REASON_REPLACED] = "Callback was replaced", - [AST_AFTER_BRIDGE_CB_REASON_MASQUERADE] = "Channel masqueraded", - [AST_AFTER_BRIDGE_CB_REASON_DEPART] = "Channel was departed from bridge", - [AST_AFTER_BRIDGE_CB_REASON_REMOVED] = "Callback was removed", -}; - -const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason) -{ - if (reason < AST_AFTER_BRIDGE_CB_REASON_DESTROY - || AST_AFTER_BRIDGE_CB_REASON_REMOVED < reason - || !reason_strings[reason]) { - return "Unknown"; - } - - return reason_strings[reason]; -} - -struct after_bridge_goto_ds { - /*! Goto string that can be parsed by ast_parseable_goto(). */ - const char *parseable_goto; - /*! Specific goto context or default context for parseable_goto. */ - const char *context; - /*! Specific goto exten or default exten for parseable_goto. */ - const char *exten; - /*! Specific goto priority or default priority for parseable_goto. */ - int priority; - /*! TRUE if the peer should run the h exten. */ - unsigned int run_h_exten:1; - /*! Specific goto location */ - unsigned int specific:1; -}; - -/*! - * \internal - * \brief Destroy the after bridge goto datastore. - * \since 12.0.0 - * - * \param data After bridge goto data to destroy. - * - * \return Nothing - */ -static void after_bridge_goto_destroy(void *data) -{ - struct after_bridge_goto_ds *after_bridge = data; - - ast_free((char *) after_bridge->parseable_goto); - ast_free((char *) after_bridge->context); - ast_free((char *) after_bridge->exten); -} - -/*! - * \internal - * \brief Fixup the after bridge goto datastore. - * \since 12.0.0 - * - * \param data After bridge goto data to fixup. - * \param old_chan The datastore is moving from this channel. - * \param new_chan The datastore is moving to this channel. - * - * \return Nothing - */ -static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - /* There can be only one. Discard any already on the new channel. */ - ast_after_bridge_goto_discard(new_chan); -} - -static const struct ast_datastore_info after_bridge_goto_info = { - .type = "after-bridge-goto", - .destroy = after_bridge_goto_destroy, - .chan_fixup = after_bridge_goto_fixup, -}; - -/*! - * \internal - * \brief Remove channel goto location after the bridge and return it. - * \since 12.0.0 - * - * \param chan Channel to remove after bridge goto location. - * - * \retval datastore on success. - * \retval NULL on error or not found. - */ -static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - - ast_channel_lock(chan); - datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); - if (datastore && ast_channel_datastore_remove(chan, datastore)) { - datastore = NULL; - } - ast_channel_unlock(chan); - - return datastore; -} - -void ast_after_bridge_goto_discard(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - - datastore = after_bridge_goto_remove(chan); - if (datastore) { - ast_datastore_free(datastore); - } -} - -void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t buf_size) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - char *current_pos = buffer; - size_t remaining_size = buf_size; - - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); - if (!datastore) { - buffer[0] = '\0'; - return; - } - - after_bridge = datastore->data; - - if (after_bridge->parseable_goto) { - snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto); - return; - } - - if (!ast_strlen_zero(after_bridge->context)) { - snprintf(current_pos, remaining_size, "%s,", after_bridge->context); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } - - if (after_bridge->run_h_exten) { - snprintf(current_pos, remaining_size, "h,"); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } else if (!ast_strlen_zero(after_bridge->exten)) { - snprintf(current_pos, remaining_size, "%s,", after_bridge->exten); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } - - snprintf(current_pos, remaining_size, "%d", after_bridge->priority); -} - -int ast_after_bridge_goto_setup(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - int goto_failed = -1; - - /* Determine if we are going to setup a dialplan location and where. */ - if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { - /* An async goto has already setup a location. */ - ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO); - if (!ast_check_hangup(chan)) { - goto_failed = 0; - } - return goto_failed; - } - - /* Get after bridge goto datastore. */ - datastore = after_bridge_goto_remove(chan); - if (!datastore) { - return goto_failed; - } - - after_bridge = datastore->data; - if (after_bridge->run_h_exten) { - if (ast_exists_extension(chan, after_bridge->context, "h", 1, - S_COR(ast_channel_caller(chan)->id.number.valid, - ast_channel_caller(chan)->id.number.str, NULL))) { - ast_debug(1, "Running after bridge goto h exten %s,h,1\n", - ast_channel_context(chan)); - ast_pbx_h_exten_run(chan, after_bridge->context); - } - } else if (!ast_check_hangup(chan)) { - if (after_bridge->specific) { - goto_failed = ast_explicit_goto(chan, after_bridge->context, - after_bridge->exten, after_bridge->priority); - } else if (!ast_strlen_zero(after_bridge->parseable_goto)) { - char *context; - char *exten; - int priority; - - /* Option F(x) for Bridge(), Dial(), and Queue() */ - - /* Save current dialplan location in case of failure. */ - context = ast_strdupa(ast_channel_context(chan)); - exten = ast_strdupa(ast_channel_exten(chan)); - priority = ast_channel_priority(chan); - - /* Set current dialplan position to default dialplan position */ - ast_explicit_goto(chan, after_bridge->context, after_bridge->exten, - after_bridge->priority); - - /* Then perform the goto */ - goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto); - if (goto_failed) { - /* Restore original dialplan location. */ - ast_channel_context_set(chan, context); - ast_channel_exten_set(chan, exten); - ast_channel_priority_set(chan, priority); - } - } else { - /* Option F() for Bridge(), Dial(), and Queue() */ - goto_failed = ast_goto_if_exists(chan, after_bridge->context, - after_bridge->exten, after_bridge->priority + 1); - } - if (!goto_failed) { - if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) { - ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); - } - - ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n", - ast_channel_context(chan), - ast_channel_exten(chan), - ast_channel_priority(chan)); - } - } - - /* Discard after bridge goto datastore. */ - ast_datastore_free(datastore); - - return goto_failed; -} - -void ast_after_bridge_goto_run(struct ast_channel *chan) -{ - int goto_failed; - - goto_failed = ast_after_bridge_goto_setup(chan); - if (goto_failed || ast_pbx_run(chan)) { - ast_hangup(chan); - } -} - -/*! - * \internal - * \brief Set after bridge goto location of channel. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * \param run_h_exten TRUE if the h exten should be run. - * \param specific TRUE if the context/exten/priority is exactly specified. - * \param context Context to goto after bridge. - * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten) - * \param priority Priority to goto after bridge. - * \param parseable_goto User specified goto string. (Could be NULL) - * - * \details Add a channel datastore to setup the goto location - * when the channel leaves the bridge and run a PBX from there. - * - * If run_h_exten then execute the h exten found in the given context. - * Else if specific then goto the given context/exten/priority. - * Else if parseable_goto then use the given context/exten/priority - * as the relative position for the parseable_goto. - * Else goto the given context/exten/priority+1. - * - * \return Nothing - */ -static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - - /* Sanity checks. */ - ast_assert(chan != NULL); - if (!chan) { - return; - } - if (run_h_exten) { - ast_assert(run_h_exten && context); - if (!context) { - return; - } - } else { - ast_assert(context && exten && 0 < priority); - if (!context || !exten || priority < 1) { - return; - } - } - - /* Create a new datastore. */ - datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL); - if (!datastore) { - return; - } - after_bridge = ast_calloc(1, sizeof(*after_bridge)); - if (!after_bridge) { - ast_datastore_free(datastore); - return; - } - - /* Initialize it. */ - after_bridge->parseable_goto = ast_strdup(parseable_goto); - after_bridge->context = ast_strdup(context); - after_bridge->exten = ast_strdup(exten); - after_bridge->priority = priority; - after_bridge->run_h_exten = run_h_exten ? 1 : 0; - after_bridge->specific = specific ? 1 : 0; - datastore->data = after_bridge; - if ((parseable_goto && !after_bridge->parseable_goto) - || (context && !after_bridge->context) - || (exten && !after_bridge->exten)) { - ast_datastore_free(datastore); - return; - } - - /* Put it on the channel replacing any existing one. */ - ast_channel_lock(chan); - ast_after_bridge_goto_discard(chan); - ast_channel_datastore_add(chan, datastore); - ast_channel_unlock(chan); -} - -void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) -{ - __after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL); -} - -void ast_after_bridge_set_h(struct ast_channel *chan, const char *context) -{ - __after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL); -} - -void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto) -{ - char *p_goto; - - if (!ast_strlen_zero(parseable_goto)) { - p_goto = ast_strdupa(parseable_goto); - ast_replace_subargument_delimiter(p_goto); - } else { - p_goto = NULL; - } - __after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto); + return bridge_channel; } void ast_bridge_notify_masquerade(struct ast_channel *chan) @@ -2146,7 +1420,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan) * Need to update the features parameter doxygen when this * change is made to be like ast_bridge_impart(). */ -enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, +enum bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, @@ -2154,21 +1428,21 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, int pass_reference) { struct ast_bridge_channel *bridge_channel; - enum ast_bridge_channel_state state; + enum bridge_channel_state state; - bridge_channel = bridge_channel_alloc(bridge); + bridge_channel = bridge_channel_internal_alloc(bridge); if (pass_reference) { ao2_ref(bridge, -1); } if (!bridge_channel) { - state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; + state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; goto join_exit; } /* BUGBUG features cannot be NULL when passed in. When it is changed to allocated we can do like ast_bridge_impart() and allocate one. */ ast_assert(features != NULL); if (!features) { ao2_ref(bridge_channel, -1); - state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; + state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; goto join_exit; } if (tech_args) { @@ -2184,7 +1458,7 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, bridge_channel->swap = swap; bridge_channel->features = features; - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); state = bridge_channel->state; /* Cleanup all the data in the bridge channel after it leaves the bridge. */ @@ -2199,9 +1473,9 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, join_exit:; /* BUGBUG this is going to cause problems for DTMF atxfer attended bridge between B & C. Maybe an ast_bridge_join_internal() that does not do the after bridge goto for this case. */ - after_bridge_callback_run(chan); + ast_bridge_run_after_callback(chan); if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) - && !ast_after_bridge_goto_setup(chan)) { + && !ast_bridge_setup_after_goto(chan)) { /* Claim the after bridge goto is an async goto destination. */ ast_channel_lock(chan); ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO); @@ -2219,15 +1493,15 @@ static void *bridge_channel_depart_thread(void *data) ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); /* cleanup */ bridge_channel->swap = NULL; ast_bridge_features_destroy(bridge_channel->features); bridge_channel->features = NULL; - after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART); - ast_after_bridge_goto_discard(bridge_channel->chan); + ast_bridge_discard_after_callback(bridge_channel->chan, AST_BRIDGE_AFTER_CB_REASON_DEPART); + ast_bridge_discard_after_goto(bridge_channel->chan); return NULL; } @@ -2242,7 +1516,7 @@ static void *bridge_channel_ind_thread(void *data) ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); chan = bridge_channel->chan; /* cleanup */ @@ -2256,8 +1530,8 @@ static void *bridge_channel_ind_thread(void *data) ao2_ref(bridge_channel, -1); - after_bridge_callback_run(chan); - ast_after_bridge_goto_run(chan); + ast_bridge_run_after_callback(chan); + ast_bridge_run_after_goto(chan); return NULL; } @@ -2275,7 +1549,7 @@ int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struc } /* Try to allocate a structure for the bridge channel */ - bridge_channel = bridge_channel_alloc(bridge); + bridge_channel = bridge_channel_internal_alloc(bridge); if (!bridge_channel) { ast_bridge_features_destroy(features); return -1; @@ -2350,7 +1624,7 @@ int ast_bridge_depart(struct ast_channel *chan) * channel thread. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); /* Wait for the depart thread to die */ ast_debug(1, "Waiting for %p(%s) bridge thread to die.\n", @@ -2378,7 +1652,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); ast_bridge_unlock(bridge); @@ -2425,10 +1699,10 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg * Move channels from src_bridge over to dst_bridge. * * We must use AST_LIST_TRAVERSE_SAFE_BEGIN() because - * bridge_channel_pull() alters the list we are traversing. + * bridge_channel_internal_pull() alters the list we are traversing. */ AST_LIST_TRAVERSE_SAFE_BEGIN(&src_bridge->channels, bridge_channel, entry) { - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel is already leaving let it leave normally because * pulling it may delete hooks that should run for this channel. @@ -2443,13 +1717,13 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg if (kick_me) { for (idx = 0; idx < num_kick; ++idx) { if (bridge_channel == kick_me[idx]) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); break; } } } - bridge_channel_pull(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + bridge_channel_internal_pull(bridge_channel); + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel died as a result of being pulled or it was * kicked. Leave it pointing to the original bridge. @@ -2460,8 +1734,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg /* Point to new bridge.*/ bridge_channel_change_bridge(bridge_channel, dst_bridge); - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } } AST_LIST_TRAVERSE_SAFE_END; @@ -2474,9 +1748,9 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg for (idx = 0; idx < num_kick; ++idx) { bridge_channel = kick_me[idx]; ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - ast_bridge_channel_leave_bridge_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); - bridge_channel_pull(bridge_channel); + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { + ast_bridge_channel_leave_bridge_nolock(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + bridge_channel_internal_pull(bridge_channel); } ast_bridge_channel_unlock(bridge_channel); } @@ -2689,8 +1963,8 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri orig_bridge = bridge_channel->bridge; was_in_bridge = bridge_channel->in_bridge; - bridge_channel_pull(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + bridge_channel_internal_pull(bridge_channel); + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel died as a result of being pulled. Leave it * pointing to the original bridge. @@ -2703,17 +1977,17 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */ bridge_channel_change_bridge(bridge_channel, dst_bridge); - if (bridge_channel_push(bridge_channel)) { + if (bridge_channel_internal_push(bridge_channel)) { /* Try to put the channel back into the original bridge. */ if (attempt_recovery && was_in_bridge) { /* Point back to original bridge. */ bridge_channel_change_bridge(bridge_channel, orig_bridge); - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } } else { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } res = -1; } @@ -2767,7 +2041,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid); return -1; } - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel leaving bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid); return -1; @@ -2789,7 +2063,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * ast_channel_name(swap)); return -1; } - if (bridge_channel_swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel_swap->state != BRIDGE_CHANNEL_STATE_WAIT) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, swap channel %s leaving bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid, ast_channel_name(swap)); @@ -2894,12 +2168,6 @@ static int bridge_allows_optimization(struct ast_bridge *bridge) || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)); } -static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel) -{ - return bridge_channel->in_bridge - && AST_LIST_EMPTY(&bridge_channel->wr_queue); -} - /*! * \internal * \brief Lock the unreal channel stack for chan and prequalify it. @@ -2929,13 +2197,13 @@ static struct ast_bridge *optimize_lock_chan_stack(struct ast_channel *chan) return NULL; } bridge = bridge_channel->bridge; - if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_SIMPLE - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT + if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_SIMPLE + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT || ast_bridge_trylock(bridge)) { ast_bridge_channel_unlock(bridge_channel); return NULL; } - if (!bridge_channel_allows_optimization(bridge_channel) || + if (!bridge_channel_internal_allows_optimization(bridge_channel) || !bridge_allows_optimization(bridge)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); @@ -2977,15 +2245,15 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer) return NULL; } bridge = bridge_channel->bridge; - if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_IDLE - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT + if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_IDLE + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT || ast_bridge_trylock(bridge)) { ast_bridge_channel_unlock(bridge_channel); ast_channel_unlock(peer); return NULL; } if (!bridge_allows_optimization(bridge) || - !bridge_channel_allows_optimization(bridge_channel)) { + !bridge_channel_internal_allows_optimization(bridge_channel)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); ast_channel_unlock(peer); @@ -3099,7 +2367,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge, } other = ast_bridge_channel_peer(src_bridge_channel); - if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (other && other->state == BRIDGE_CHANNEL_STATE_WAIT) { ast_verb(3, "Move-swap optimizing %s <-- %s.\n", ast_channel_name(dst_bridge_channel->chan), ast_channel_name(other->chan)); @@ -3111,7 +2379,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge, } other->swap = dst_bridge_channel->chan; if (!bridge_do_move(dst_bridge, other, 1, 1)) { - ast_bridge_channel_leave_bridge(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(src_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); res = -1; if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) { pvt->callbacks->optimization_finished(pvt); @@ -3343,7 +2611,7 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - bridge_channel_suspend_nolock(bridge_channel); + bridge_channel_internal_suspend_nolock(bridge_channel); ast_bridge_unlock(bridge); @@ -3362,7 +2630,7 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - bridge_channel_unsuspend_nolock(bridge_channel); + bridge_channel_internal_unsuspend_nolock(bridge_channel); ast_bridge_unlock(bridge); @@ -4686,7 +3954,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, goto publish; } - if (bridge_channel_queue_blind_transfer(transferee, exten, context, + if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context, new_channel_cb, user_data)) { transfer_result = AST_BRIDGE_TRANSFER_FAIL; goto publish; @@ -4722,7 +3990,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge bridged_to_source = ast_bridge_channel_peer(source_bridge_channel); if (bridged_to_source - && bridged_to_source->state == AST_BRIDGE_CHANNEL_STATE_WAIT + && bridged_to_source->state == BRIDGE_CHANNEL_STATE_WAIT && !ast_test_flag(&bridged_to_source->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) { bridged_to_source->swap = swap_channel; @@ -4730,7 +3998,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge return AST_BRIDGE_TRANSFER_FAIL; } /* Must kick the source channel out of its bridge. */ - ast_bridge_channel_leave_bridge(source_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(source_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); return AST_BRIDGE_TRANSFER_SUCCESS; } else { return AST_BRIDGE_TRANSFER_INVALID; @@ -4953,7 +4221,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra } app = ast_strdupa(ast_channel_appl(chan_unbridged)); - if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) { + if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) { res = AST_BRIDGE_TRANSFER_FAIL; goto end; } diff --git a/main/bridging_after.c b/main/bridging_after.c new file mode 100644 index 0000000000..8ab4bc8422 --- /dev/null +++ b/main/bridging_after.c @@ -0,0 +1,640 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007 - 2009, Digium, Inc. + * + * Richard Mudgett <rmudgett@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief After Bridge Execution API + * + * \author Richard Mudgett <rmudgett@digium.com> + * + * See Also: + * \arg \ref AstCREDITS + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/bridging_after.h" + +struct after_bridge_cb_node { + /*! Next list node. */ + AST_LIST_ENTRY(after_bridge_cb_node) list; + /*! Desired callback function. */ + ast_bridge_after_cb callback; + /*! After bridge callback will not be called and destroy any resources data may contain. */ + ast_bridge_after_cb_failed failed; + /*! Extra data to pass to the callback. */ + void *data; + /*! Reason the after bridge callback failed. */ + enum ast_bridge_after_cb_reason reason; +}; + +struct after_bridge_cb_ds { + /*! After bridge callbacks container. */ + AST_LIST_HEAD(, after_bridge_cb_node) callbacks; +}; + +/*! + * \internal + * \brief Indicate after bridge callback failed. + * \since 12.0.0 + * + * \param node After bridge callback node. + * + * \return Nothing + */ +static void after_bridge_cb_failed(struct after_bridge_cb_node *node) +{ + if (node->failed) { + node->failed(node->reason, node->data); + node->failed = NULL; + } +} + +/*! + * \internal + * \brief Run discarding any after bridge callbacks. + * \since 12.0.0 + * + * \param after_bridge After bridge callback container process. + * \param reason Why are we doing this. + * + * \return Nothing + */ +static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_bridge_after_cb_reason reason) +{ + struct after_bridge_cb_node *node; + + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (!node->reason) { + node->reason = reason; + } + after_bridge_cb_failed(node); + ast_free(node); + } +} + +/*! + * \internal + * \brief Destroy the after bridge callback datastore. + * \since 12.0.0 + * + * \param data After bridge callback data to destroy. + * + * \return Nothing + */ +static void after_bridge_cb_destroy(void *data) +{ + struct after_bridge_cb_ds *after_bridge = data; + + after_bridge_cb_run_discard(after_bridge, AST_BRIDGE_AFTER_CB_REASON_DESTROY); + + AST_LIST_HEAD_DESTROY(&after_bridge->callbacks); + ast_free(after_bridge); +} + +static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan); + +/*! + * \internal + * \brief Fixup the after bridge callback datastore. + * \since 12.0.0 + * + * \param data After bridge callback data to fixup. + * \param old_chan The datastore is moving from this channel. + * \param new_chan The datastore is moving to this channel. + * + * \return Nothing + */ +static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; + + after_bridge = after_bridge_cb_find(new_chan); + if (!after_bridge) { + return; + } + + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_LAST(&after_bridge->callbacks); + if (node && !node->reason) { + node->reason = AST_BRIDGE_AFTER_CB_REASON_MASQUERADE; + } + AST_LIST_UNLOCK(&after_bridge->callbacks); +} + +static const struct ast_datastore_info after_bridge_cb_info = { + .type = "after-bridge-cb", + .destroy = after_bridge_cb_destroy, + .chan_fixup = after_bridge_cb_fixup, +}; + +/*! + * \internal + * \brief Find an after bridge callback datastore container. + * \since 12.0.0 + * + * \param chan Channel to find the after bridge callback container on. + * + * \retval after_bridge datastore container on success. + * \retval NULL on error. + */ +static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; + } + return datastore->data; +} + +/*! + * \internal + * \brief Setup/create an after bridge callback datastore container. + * \since 12.0.0 + * + * \param chan Channel to setup/create the after bridge callback container on. + * + * \retval after_bridge datastore container on success. + * \retval NULL on error. + */ +static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct after_bridge_cb_ds *after_bridge; + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); + if (datastore) { + return datastore->data; + } + + /* Create a new datastore. */ + datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; + } + after_bridge = ast_calloc(1, sizeof(*after_bridge)); + if (!after_bridge) { + ast_datastore_free(datastore); + return NULL; + } + AST_LIST_HEAD_INIT(&after_bridge->callbacks); + datastore->data = after_bridge; + ast_channel_datastore_add(chan, datastore); + + return datastore->data; +} + +void ast_bridge_run_after_callback(struct ast_channel *chan) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { + return; + } + + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (node->reason) { + after_bridge_cb_failed(node); + } else { + node->failed = NULL; + node->callback(chan, node->data); + } + ast_free(node); + } +} + +void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason) +{ + struct after_bridge_cb_ds *after_bridge; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { + return; + } + + after_bridge_cb_run_discard(after_bridge, reason); +} + +int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *new_node; + struct after_bridge_cb_node *last_node; + + /* Sanity checks. */ + ast_assert(chan != NULL); + if (!chan || !callback) { + return -1; + } + + after_bridge = after_bridge_cb_setup(chan); + if (!after_bridge) { + return -1; + } + + /* Create a new callback node. */ + new_node = ast_calloc(1, sizeof(*new_node)); + if (!new_node) { + return -1; + } + new_node->callback = callback; + new_node->failed = failed; + new_node->data = data; + + /* Put it in the container disabling any previously active one. */ + AST_LIST_LOCK(&after_bridge->callbacks); + last_node = AST_LIST_LAST(&after_bridge->callbacks); + if (last_node && !last_node->reason) { + last_node->reason = AST_BRIDGE_AFTER_CB_REASON_REPLACED; + } + AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + return 0; +} + +const char *reason_strings[] = { + [AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)", + [AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced", + [AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded", + [AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge", + [AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed", +}; + +const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason) +{ + if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY + || AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason + || !reason_strings[reason]) { + return "Unknown"; + } + + return reason_strings[reason]; +} + +struct after_bridge_goto_ds { + /*! Goto string that can be parsed by ast_parseable_goto(). */ + const char *parseable_goto; + /*! Specific goto context or default context for parseable_goto. */ + const char *context; + /*! Specific goto exten or default exten for parseable_goto. */ + const char *exten; + /*! Specific goto priority or default priority for parseable_goto. */ + int priority; + /*! TRUE if the peer should run the h exten. */ + unsigned int run_h_exten:1; + /*! Specific goto location */ + unsigned int specific:1; +}; + +/*! + * \internal + * \brief Destroy the after bridge goto datastore. + * \since 12.0.0 + * + * \param data After bridge goto data to destroy. + * + * \return Nothing + */ +static void after_bridge_goto_destroy(void *data) +{ + struct after_bridge_goto_ds *after_bridge = data; + + ast_free((char *) after_bridge->parseable_goto); + ast_free((char *) after_bridge->context); + ast_free((char *) after_bridge->exten); +} + +/*! + * \internal + * \brief Fixup the after bridge goto datastore. + * \since 12.0.0 + * + * \param data After bridge goto data to fixup. + * \param old_chan The datastore is moving from this channel. + * \param new_chan The datastore is moving to this channel. + * + * \return Nothing + */ +static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + /* There can be only one. Discard any already on the new channel. */ + ast_bridge_discard_after_goto(new_chan); +} + +static const struct ast_datastore_info after_bridge_goto_info = { + .type = "after-bridge-goto", + .destroy = after_bridge_goto_destroy, + .chan_fixup = after_bridge_goto_fixup, +}; + +/*! + * \internal + * \brief Remove channel goto location after the bridge and return it. + * \since 12.0.0 + * + * \param chan Channel to remove after bridge goto location. + * + * \retval datastore on success. + * \retval NULL on error or not found. + */ +static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); + if (datastore && ast_channel_datastore_remove(chan, datastore)) { + datastore = NULL; + } + ast_channel_unlock(chan); + + return datastore; +} + +void ast_bridge_discard_after_goto(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + datastore = after_bridge_goto_remove(chan); + if (datastore) { + ast_datastore_free(datastore); + } +} + +void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + char *current_pos = buffer; + size_t remaining_size = buf_size; + + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); + if (!datastore) { + buffer[0] = '\0'; + return; + } + + after_bridge = datastore->data; + + if (after_bridge->parseable_goto) { + snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto); + return; + } + + if (!ast_strlen_zero(after_bridge->context)) { + snprintf(current_pos, remaining_size, "%s,", after_bridge->context); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } + + if (after_bridge->run_h_exten) { + snprintf(current_pos, remaining_size, "h,"); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } else if (!ast_strlen_zero(after_bridge->exten)) { + snprintf(current_pos, remaining_size, "%s,", after_bridge->exten); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } + + snprintf(current_pos, remaining_size, "%d", after_bridge->priority); +} + +int ast_bridge_setup_after_goto(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + int goto_failed = -1; + + /* Determine if we are going to setup a dialplan location and where. */ + if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { + /* An async goto has already setup a location. */ + ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO); + if (!ast_check_hangup(chan)) { + goto_failed = 0; + } + return goto_failed; + } + + /* Get after bridge goto datastore. */ + datastore = after_bridge_goto_remove(chan); + if (!datastore) { + return goto_failed; + } + + after_bridge = datastore->data; + if (after_bridge->run_h_exten) { + if (ast_exists_extension(chan, after_bridge->context, "h", 1, + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL))) { + ast_debug(1, "Running after bridge goto h exten %s,h,1\n", + ast_channel_context(chan)); + ast_pbx_h_exten_run(chan, after_bridge->context); + } + } else if (!ast_check_hangup(chan)) { + if (after_bridge->specific) { + goto_failed = ast_explicit_goto(chan, after_bridge->context, + after_bridge->exten, after_bridge->priority); + } else if (!ast_strlen_zero(after_bridge->parseable_goto)) { + char *context; + char *exten; + int priority; + + /* Option F(x) for Bridge(), Dial(), and Queue() */ + + /* Save current dialplan location in case of failure. */ + context = ast_strdupa(ast_channel_context(chan)); + exten = ast_strdupa(ast_channel_exten(chan)); + priority = ast_channel_priority(chan); + + /* Set current dialplan position to default dialplan position */ + ast_explicit_goto(chan, after_bridge->context, after_bridge->exten, + after_bridge->priority); + + /* Then perform the goto */ + goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto); + if (goto_failed) { + /* Restore original dialplan location. */ + ast_channel_context_set(chan, context); + ast_channel_exten_set(chan, exten); + ast_channel_priority_set(chan, priority); + } + } else { + /* Option F() for Bridge(), Dial(), and Queue() */ + goto_failed = ast_goto_if_exists(chan, after_bridge->context, + after_bridge->exten, after_bridge->priority + 1); + } + if (!goto_failed) { + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) { + ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); + } + + ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n", + ast_channel_context(chan), + ast_channel_exten(chan), + ast_channel_priority(chan)); + } + } + + /* Discard after bridge goto datastore. */ + ast_datastore_free(datastore); + + return goto_failed; +} + +void ast_bridge_run_after_goto(struct ast_channel *chan) +{ + int goto_failed; + + goto_failed = ast_bridge_setup_after_goto(chan); + if (goto_failed || ast_pbx_run(chan)) { + ast_hangup(chan); + } +} + +/*! + * \internal + * \brief Set after bridge goto location of channel. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * \param run_h_exten TRUE if the h exten should be run. + * \param specific TRUE if the context/exten/priority is exactly specified. + * \param context Context to goto after bridge. + * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten) + * \param priority Priority to goto after bridge. + * \param parseable_goto User specified goto string. (Could be NULL) + * + * \details Add a channel datastore to setup the goto location + * when the channel leaves the bridge and run a PBX from there. + * + * If run_h_exten then execute the h exten found in the given context. + * Else if specific then goto the given context/exten/priority. + * Else if parseable_goto then use the given context/exten/priority + * as the relative position for the parseable_goto. + * Else goto the given context/exten/priority+1. + * + * \return Nothing + */ +static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + + /* Sanity checks. */ + ast_assert(chan != NULL); + if (!chan) { + return; + } + if (run_h_exten) { + ast_assert(run_h_exten && context); + if (!context) { + return; + } + } else { + ast_assert(context && exten && 0 < priority); + if (!context || !exten || priority < 1) { + return; + } + } + + /* Create a new datastore. */ + datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL); + if (!datastore) { + return; + } + after_bridge = ast_calloc(1, sizeof(*after_bridge)); + if (!after_bridge) { + ast_datastore_free(datastore); + return; + } + + /* Initialize it. */ + after_bridge->parseable_goto = ast_strdup(parseable_goto); + after_bridge->context = ast_strdup(context); + after_bridge->exten = ast_strdup(exten); + after_bridge->priority = priority; + after_bridge->run_h_exten = run_h_exten ? 1 : 0; + after_bridge->specific = specific ? 1 : 0; + datastore->data = after_bridge; + if ((parseable_goto && !after_bridge->parseable_goto) + || (context && !after_bridge->context) + || (exten && !after_bridge->exten)) { + ast_datastore_free(datastore); + return; + } + + /* Put it on the channel replacing any existing one. */ + ast_channel_lock(chan); + ast_bridge_discard_after_goto(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); +} + +void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) +{ + __after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL); +} + +void ast_bridge_set_after_h(struct ast_channel *chan, const char *context) +{ + __after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL); +} + +void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto) +{ + char *p_goto; + + if (!ast_strlen_zero(parseable_goto)) { + p_goto = ast_strdupa(parseable_goto); + ast_replace_subargument_delimiter(p_goto); + } else { + p_goto = NULL; + } + __after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto); +} diff --git a/main/bridging_basic.c b/main/bridging_basic.c index 8fcad7560d..7f9cb71fdb 100644 --- a/main/bridging_basic.c +++ b/main/bridging_basic.c @@ -36,13 +36,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/linkedlists.h" #include "asterisk/bridging.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" #include "asterisk/astobj2.h" #include "asterisk/features_config.h" #include "asterisk/pbx.h" #include "asterisk/file.h" #include "asterisk/app.h" #include "asterisk/bridging_internal.h" -#include "asterisk/bridging_channel_internal.h" #include "asterisk/dial.h" #include "asterisk/stasis_bridging.h" @@ -279,13 +279,13 @@ static int basic_hangup_hook(struct ast_bridge *bridge, struct ast_bridge_channe ast_bridge_channel_lock_bridge(bridge_channel); AST_LIST_TRAVERSE(&bridge->channels, iter, entry) { - if (iter != bridge_channel && iter->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (iter != bridge_channel && iter->state == BRIDGE_CHANNEL_STATE_WAIT) { ++bridge_count; } } if (2 <= bridge_count) { /* Just allow this channel to leave the multi-party bridge. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } ast_bridge_unlock(bridge_channel->bridge); return 0; @@ -2300,7 +2300,7 @@ static int bridge_personality_atxfer_push(struct ast_bridge *self, struct ast_br static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct attended_transfer_properties *props) { - if (self->num_channels > 1 || bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (self->num_channels > 1 || bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { return; } @@ -2794,13 +2794,13 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c if (!ast_strlen_zero(goto_on_blindxfr)) { ast_debug(1, "After transfer, transferer %s goes to %s\n", ast_channel_name(bridge_channel->chan), goto_on_blindxfr); - ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr); + ast_bridge_set_after_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr); } if (ast_bridge_transfer_blind(0, bridge_channel->chan, exten, context, blind_transfer_cb, bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS && !ast_strlen_zero(goto_on_blindxfr)) { - ast_after_bridge_goto_discard(bridge_channel->chan); + ast_bridge_discard_after_goto(bridge_channel->chan); } return 0; diff --git a/main/bridging_channel.c b/main/bridging_channel.c index 385545c2c8..284f08732d 100644 --- a/main/bridging_channel.c +++ b/main/bridging_channel.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/timing.h" #include "asterisk/bridging.h" #include "asterisk/bridging_channel.h" +#include "asterisk/bridging_after.h" #include "asterisk/bridging_channel_internal.h" #include "asterisk/bridging_internal.h" #include "asterisk/stasis_bridging.h" @@ -67,18 +68,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") */ typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen); -struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) -{ - struct ast_bridge *bridge; - - ast_bridge_channel_lock_bridge(bridge_channel); - bridge = bridge_channel->bridge; - ao2_ref(bridge, +1); - bridge_merge_inhibit_nolock(bridge, request); - ast_bridge_unlock(bridge); - return bridge; -} - void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge; @@ -101,6 +90,25 @@ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) } } +int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) +{ + struct ast_frame action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = started_talking + ? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &action); +} + +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state) +{ + ast_bridge_channel_lock(bridge_channel); + ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state); + ast_bridge_channel_unlock(bridge_channel); +} + +/*! \internal \brief Poke the bridge_channel thread */ static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) { if (!pthread_equal(pthread_self(), bridge_channel->thread)) { @@ -111,6 +119,102 @@ static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) } } +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state) +{ +/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { + return; + } + + ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", + bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, + new_state); + + /* Change the state on the bridge channel */ + bridge_channel->state = new_state; + + bridge_channel_poke(bridge_channel); +} + +struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + struct ast_bridge_channel *other = NULL; + + if (bridge_channel->in_bridge && bridge->num_channels == 2) { + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other != bridge_channel) { + break; + } + } + } + + return other; +} + +void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) +{ + /* Restore original formats of the channel as they came in */ + if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + } + } + if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + } + } +} + +struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) +{ + struct ast_bridge *bridge; + + ast_bridge_channel_lock_bridge(bridge_channel); + bridge = bridge_channel->bridge; + ao2_ref(bridge, +1); + bridge_merge_inhibit_nolock(bridge, request); + ast_bridge_unlock(bridge); + return bridge; +} + +void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +{ + struct ast_bridge_channel *other = NULL; + struct ast_bridge *bridge = bridge_channel->bridge; + const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); + + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); + } + + if (ast_strlen_zero(oldest_linkedid)) { + return; + } + + ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + ast_channel_linkedid_set(other->chan, oldest_linkedid); + } +} + void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -156,30 +260,120 @@ void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_ch } } -void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +/*! +* \internal +* \brief Handle bridge hangup event. +* \since 12.0.0 +* +* \param bridge_channel Which channel is hanging up. +* +* \return Nothing +*/ +static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel) { - struct ast_bridge_channel *other = NULL; - struct ast_bridge *bridge = bridge_channel->bridge; - const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { + /* Run any hangup hooks. */ + iter = ao2_iterator_init(features->other_hooks, 0); + for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { + int remove_me; + + if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) { continue; } - oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); + remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + if (remove_me) { + ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_unlink(features->other_hooks, hook); + } } + ao2_iterator_destroy(&iter); - if (ast_strlen_zero(oldest_linkedid)) { - return; - } + /* Default hangup action. */ + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); +} - ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { - continue; - } - ast_channel_linkedid_set(other->chan, oldest_linkedid); - } +/*! + * \internal + * \brief Write an \ref ast_frame onto the bridge channel + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param frame The frame to write onto the bridge_channel + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + ast_bridge_channel_lock_bridge(bridge_channel); +/* + * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). + * + * The tech decides if a frame needs to be pushed back for deferral. + * simple_bridge/native_bridge are likely the only techs that will do this. + */ + bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); + ast_bridge_unlock(bridge_channel->bridge); + + /* + * Claim successful write to bridge. If deferred frame + * support is added, claim successfully deferred. + */ + return 0; +} + +/*! + * \internal + * \brief Queue an action frame onto the bridge channel with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &frame); +} + +/*! + * \internal + * \brief Write an action frame onto the bridge channel with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return bridge_channel_write_frame(bridge_channel, &frame); } int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) @@ -206,7 +400,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st } ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* Drop frames on channels leaving the bridge. */ ast_bridge_channel_unlock(bridge_channel); ast_frfree(dup); @@ -218,44 +412,8 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n", bridge_channel, ast_channel_name(bridge_channel->chan)); } - ast_bridge_channel_unlock(bridge_channel); - return 0; -} - -/*! - * \brief Queue an action frame onto the bridge channel with data. - * \since 12.0.0 - * - * \param bridge_channel Which channel to queue the frame onto. - * \param action Type of bridge action frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -static int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = action, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); -} - -int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_CONTROL, - .subclass.integer = control, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); + ast_bridge_channel_unlock(bridge_channel); + return 0; } int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) @@ -279,60 +437,16 @@ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_ return not_written; } -void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) -{ - /* Restore original formats of the channel as they came in */ - if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - } - } - if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - } - } -} - -static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) -{ - ast_bridge_channel_lock_bridge(bridge_channel); -/* - * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). - * - * The tech decides if a frame needs to be pushed back for deferral. - * simple_bridge/native_bridge are likely the only techs that will do this. - */ - bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); - ast_bridge_unlock(bridge_channel->bridge); - - /* - * Claim successful write to bridge. If deferred frame - * support is added, claim successfully deferred. - */ - return 0; -} - -static int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) { struct ast_frame frame = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = action, + .frametype = AST_FRAME_CONTROL, + .subclass.integer = control, .datalen = datalen, .data.ptr = (void *) data, }; - return bridge_channel_write_frame(bridge_channel, &frame); + return ast_bridge_channel_queue_frame(bridge_channel, &frame); } int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) @@ -373,6 +487,7 @@ int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel) return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0); } +/*! \internal \brief Helper function to kick off a PBX app on a bridge_channel */ static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) { int res = 0; @@ -394,42 +509,6 @@ static int run_app_helper(struct ast_channel *chan, const char *app_name, const return res; } -/*! -* \internal -* \brief Handle bridge hangup event. -* \since 12.0.0 -* -* \param bridge_channel Which channel is hanging up. -* -* \return Nothing -*/ -static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - struct ao2_iterator iter; - - /* Run any hangup hooks. */ - iter = ao2_iterator_init(features->other_hooks, 0); - for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { - int remove_me; - - if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) { - continue; - } - remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - if (remove_me) { - ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - ao2_unlink(features->other_hooks, hook); - } - } - ao2_iterator_destroy(&iter); - - /* Default hangup action. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); -} - void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { if (moh_class) { @@ -444,7 +523,6 @@ void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const } } - struct bridge_run_app { /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ int moh_offset; @@ -471,6 +549,10 @@ static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, st data->moh_offset ? &data->app_name[data->moh_offset] : NULL); } +/*! + * \internal + * \brief Marshal an application to be executed on a bridge_channel + */ static int payload_helper_app(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { @@ -497,13 +579,13 @@ static int payload_helper_app(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { - return payload_helper_app(ast_bridge_channel_write_action_data, + return payload_helper_app(bridge_channel_write_action_data, bridge_channel, app_name, app_args, moh_class); } int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { - return payload_helper_app(ast_bridge_channel_queue_action_data, + return payload_helper_app(bridge_channel_queue_action_data, bridge_channel, app_name, app_args, moh_class); } @@ -560,6 +642,10 @@ static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, s payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL); } +/*! + * \internal + * \brief Marshal a file to be played on a bridge_channel + */ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { @@ -582,13 +668,13 @@ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { - return payload_helper_playfile(ast_bridge_channel_write_action_data, + return payload_helper_playfile(bridge_channel_write_action_data, bridge_channel, custom_play, playfile, moh_class); } int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { - return payload_helper_playfile(ast_bridge_channel_queue_action_data, + return payload_helper_playfile(bridge_channel_queue_action_data, bridge_channel, custom_play, playfile, moh_class); } @@ -618,6 +704,10 @@ static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size); } +/*! + * \internal + * \brief Marshal a custom callback function to be called on a bridge_channel + */ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { @@ -644,13 +734,13 @@ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { - return payload_helper_cb(ast_bridge_channel_write_action_data, + return payload_helper_cb(bridge_channel_write_action_data, bridge_channel, callback, payload, payload_size); } int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { - return payload_helper_cb(ast_bridge_channel_queue_action_data, + return payload_helper_cb(bridge_channel_queue_action_data, bridge_channel, callback, payload, payload_size); } @@ -661,6 +751,10 @@ struct bridge_park { char parkee_uuid[0]; }; +/*! + * \internal + * \brief Park a bridge_cahnnel + */ static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload) { ast_bridge_channel_park(bridge_channel, payload->parkee_uuid, @@ -668,6 +762,10 @@ static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struc payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL); } +/*! + * \internal + * \brief Marshal a park action onto a bridge_channel + */ static int payload_helper_park(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, @@ -694,50 +792,10 @@ static int payload_helper_park(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data) { - return payload_helper_park(ast_bridge_channel_write_action_data, + return payload_helper_park(bridge_channel_write_action_data, bridge_channel, parkee_uuid, parker_uuid, app_data); } -int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) -{ - struct ast_frame action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = started_talking - ? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &action); -} - -struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge *bridge = bridge_channel->bridge; - struct ast_bridge_channel *other = NULL; - - if (bridge_channel->in_bridge && bridge->num_channels == 2) { - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other != bridge_channel) { - break; - } - } - } - - return other; -} - -struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan) -{ - struct ast_bridge_channel *bridge_channel; - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (bridge_channel->chan == chan) { - break; - } - } - - return bridge_channel; -} - /*! * \internal * \brief Suspend a channel from a bridge. @@ -748,7 +806,7 @@ struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct * * \return Nothing */ -void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel) { bridge_channel->suspended = 1; if (bridge_channel->in_bridge) { @@ -772,7 +830,7 @@ void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) { ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_suspend_nolock(bridge_channel); + bridge_channel_internal_suspend_nolock(bridge_channel); ast_bridge_unlock(bridge_channel->bridge); } @@ -786,7 +844,7 @@ static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) * * \return Nothing */ -void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) { bridge_channel->suspended = 0; if (bridge_channel->in_bridge) { @@ -815,7 +873,7 @@ void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel) { ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_unsuspend_nolock(bridge_channel); + bridge_channel_internal_unsuspend_nolock(bridge_channel); ast_bridge_unlock(bridge_channel->bridge); } @@ -913,14 +971,15 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha } } +/*! \internal \brief Write a DTMF stream out to a channel */ static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) { - return ast_bridge_channel_write_action_data(bridge_channel, + return bridge_channel_write_action_data(bridge_channel, BRIDGE_CHANNEL_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1); } /*! - * \brief Internal function that executes a feature on a bridge channel + * \internal \brief Internal function that executes a feature on a bridge channel * \note Neither the bridge nor the bridge_channel locks should be held when entering * this function. */ @@ -1017,6 +1076,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel) } } +/*! \internal \brief Indicate that a bridge_channel is talking */ static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking) { struct ast_bridge_features *features = bridge_channel->features; @@ -1051,13 +1111,13 @@ static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0); } -static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, - struct blind_transfer_data *blind_data) -{ - ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); - bridge_channel_handle_hangup(bridge_channel); -} +/*! \brief Data specifying where a blind transfer is going to */ +struct blind_transfer_data { + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_CONTEXT]; +}; +/*! \internal \brief Execute after bridge actions on a channel when it leaves a bridge */ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data) { RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); @@ -1094,15 +1154,25 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da ast_party_connected_line_free(&connected_target); } -static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data) +/*! \internal \brief Execute logic to cleanup when after bridge fails */ +static void after_bridge_move_channel_fail(enum ast_bridge_after_cb_reason reason, void *data) { RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); ast_log(LOG_WARNING, "Unable to complete transfer: %s\n", - ast_after_bridge_cb_reason_string(reason)); + ast_bridge_after_cb_reason_string(reason)); ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); } +/*! \internal \brief Perform a blind transfer on a channel in a bridge */ +static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, + struct blind_transfer_data *blind_data) +{ + ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); + bridge_channel_handle_hangup(bridge_channel); +} + +/*! \internal \brief Perform an attended transfer on a channel in a bridge */ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel, const char *target_chan_name) { @@ -1122,11 +1192,11 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c ao2_ref(chan_bridged, +1); ast_bridge_channel_unlock(bridge_channel); - if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel, + if (ast_bridge_set_after_callback(chan_bridged, after_bridge_move_channel, after_bridge_move_channel_fail, ast_channel_ref(chan_target))) { ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); - /* Release the ref we tried to pass to ast_after_bridge_callback_set(). */ + /* Release the ref we tried to pass to ast_bridge_set_after_callback(). */ ast_channel_unref(chan_target); } bridge_channel_handle_hangup(bridge_channel); @@ -1214,7 +1284,7 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann * * \return Nothing */ -static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) +static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -1230,7 +1300,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) } switch (bridge_channel->state) { - case AST_BRIDGE_CHANNEL_STATE_END: + case BRIDGE_CHANNEL_STATE_END: /* Do we need to dissolve the bridge because this channel hung up? */ if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP) || (bridge_channel->features->usable @@ -1246,18 +1316,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) /* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */ } -/*! - * \internal - * \brief Pull the bridge channel out of its current bridge. - * \since 12.0.0 - * - * \param bridge_channel Channel to pull. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \return Nothing - */ -void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -1306,25 +1365,13 @@ void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING); } - bridge_dissolve_check(bridge_channel); + bridge_channel_dissolve_check(bridge_channel); bridge->reconfigured = 1; ast_bridge_publish_leave(bridge, bridge_channel->chan); } -/*! - * \internal - * \brief Push the bridge channel into its specified bridge. - * \since 12.0.0 - * - * \param bridge_channel Channel to push. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \retval 0 on success. - * \retval -1 on failure. The channel did not get pushed. - */ -int bridge_channel_push(struct ast_bridge_channel *bridge_channel) +int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; struct ast_bridge_channel *swap; @@ -1345,8 +1392,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel) /* Add channel to the bridge */ if (bridge->dissolved - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT - || (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT + || (swap && swap->state != BRIDGE_CHANNEL_STATE_WAIT) || bridge->v_table->push(bridge, bridge_channel, swap) || ast_bridge_channel_establish_roles(bridge_channel)) { ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n", @@ -1373,8 +1420,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel) ast_bridge_publish_enter(bridge, bridge_channel->chan); if (swap) { - ast_bridge_channel_leave_bridge(swap, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); - bridge_channel_pull(swap); + ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + bridge_channel_internal_pull(swap); } /* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */ @@ -1517,7 +1564,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe break; default: /* Write the frame to the channel. */ - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE; ast_write(bridge_channel->chan, fr); break; } @@ -1661,7 +1708,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) /* Wait for data to either come from the channel or us to be signaled */ ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { } else if (bridge_channel->suspended) { /* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */ ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n", @@ -1687,10 +1734,10 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) ast_bridge_unlock(bridge_channel->bridge); } ast_bridge_channel_lock(bridge_channel); - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_FRAME; ast_bridge_channel_unlock(bridge_channel); if (!bridge_channel->suspended - && bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + && bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { if (chan) { bridge_handle_trip(bridge_channel); } else if (-1 < outfd) { @@ -1700,7 +1747,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) bridge_channel_handle_interval(bridge_channel); } } - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_IDLE; return; } ast_bridge_channel_unlock(bridge_channel); @@ -1747,7 +1794,7 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch } /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */ -void bridge_channel_join(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) { ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan)); ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan)); @@ -1777,12 +1824,12 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) bridge_channel->bridge->callid = ast_read_threadstorage_callid(); } - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } bridge_reconfigured(bridge_channel->bridge, 1); - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* * Indicate a source change since this channel is entering the * bridge system only if the bridge technology is not MULTIMIX @@ -1795,7 +1842,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_unlock(bridge_channel->bridge); bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN); - while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + while (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* Wait for something to do. */ bridge_channel_wait(bridge_channel); } @@ -1803,7 +1850,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_channel_lock_bridge(bridge_channel); } - bridge_channel_pull(bridge_channel); + bridge_channel_internal_pull(bridge_channel); bridge_reconfigured(bridge_channel->bridge, 1); ast_bridge_unlock(bridge_channel->bridge); @@ -1836,48 +1883,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_channel_restore_formats(bridge_channel); } -void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ - ast_bridge_channel_lock(bridge_channel); - ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state); - ast_bridge_channel_unlock(bridge_channel); -} - -void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ -/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { - return; - } - - ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", - bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, - new_state); - - /* Change the state on the bridge channel */ - bridge_channel->state = new_state; - - bridge_channel_poke(bridge_channel); -} - -/*! - * \internal - * \brief Queue a blind transfer action on a transferee bridge channel - * - * This is only relevant for when a blind transfer is performed on a two-party - * bridge. The transferee's bridge channel will have a blind transfer bridge - * action queued onto it, resulting in the party being redirected to a new - * destination - * - * \param transferee The channel to have the action queued on - * \param exten The destination extension for the transferee - * \param context The destination context for the transferee - * \param hook Frame hook to attach to transferee - * - * \retval 0 on success. - * \retval -1 on error. - */ -int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, +int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee, const char *exten, const char *context, transfer_channel_cb new_channel_cb, void *user_data) { @@ -1899,11 +1905,11 @@ int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten)); ast_copy_string(blind_data.context, context, sizeof(blind_data.context)); - return ast_bridge_channel_queue_action_data(transferee_bridge_channel, + return bridge_channel_queue_action_data(transferee_bridge_channel, BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER, &blind_data, sizeof(blind_data)); } -int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, +int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee, struct ast_channel *unbridged_chan) { RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup); @@ -1920,7 +1926,114 @@ int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan), sizeof(unbridged_chan_name)); - return ast_bridge_channel_queue_action_data(transferee_bridge_channel, + return bridge_channel_queue_action_data(transferee_bridge_channel, BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER, unbridged_chan_name, sizeof(unbridged_chan_name)); } + +int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel) +{ + return bridge_channel->in_bridge + && AST_LIST_EMPTY(&bridge_channel->wr_queue); +} + +/*! + * \internal + * \brief Close a pipe. + * \since 12.0.0 + * + * \param my_pipe What to close. + * + * \return Nothing + */ +static void pipe_close(int *my_pipe) +{ + if (my_pipe[0] > -1) { + close(my_pipe[0]); + my_pipe[0] = -1; + } + if (my_pipe[1] > -1) { + close(my_pipe[1]); + my_pipe[1] = -1; + } +} + +/*! + * \internal + * \brief Initialize a pipe as non-blocking. + * \since 12.0.0 + * + * \param my_pipe What to initialize. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pipe_init_nonblock(int *my_pipe) +{ + int flags; + + my_pipe[0] = -1; + my_pipe[1] = -1; + if (pipe(my_pipe)) { + ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n"); + return -1; + } + flags = fcntl(my_pipe[0], F_GETFL); + if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n", + errno, strerror(errno)); + return -1; + } + flags = fcntl(my_pipe[1], F_GETFL); + if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n", + errno, strerror(errno)); + return -1; + } + return 0; +} + +/* Destroy elements of the bridge channel structure and the bridge channel structure itself */ +static void bridge_channel_destroy(void *obj) +{ + struct ast_bridge_channel *bridge_channel = obj; + struct ast_frame *fr; + + if (bridge_channel->callid) { + bridge_channel->callid = ast_callid_unref(bridge_channel->callid); + } + + if (bridge_channel->bridge) { + ao2_ref(bridge_channel->bridge, -1); + bridge_channel->bridge = NULL; + } + + /* Flush any unhandled wr_queue frames. */ + while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) { + ast_frfree(fr); + } + pipe_close(bridge_channel->alert_pipe); + + ast_cond_destroy(&bridge_channel->cond); +} + +struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bridge_channel; + + bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy); + if (!bridge_channel) { + return NULL; + } + ast_cond_init(&bridge_channel->cond, NULL); + if (pipe_init_nonblock(bridge_channel->alert_pipe)) { + ao2_ref(bridge_channel, -1); + return NULL; + } + if (bridge) { + bridge_channel->bridge = bridge; + ao2_ref(bridge_channel->bridge, +1); + } + + return bridge_channel; +} diff --git a/main/features.c b/main/features.c index ddbd04b468..d1b33d5b94 100644 --- a/main/features.c +++ b/main/features.c @@ -73,6 +73,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/test.h" #include "asterisk/bridging.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" #include "asterisk/stasis.h" #include "asterisk/stasis_channels.h" #include "asterisk/features_config.h" @@ -839,15 +840,15 @@ static void *bridge_call_thread(void *data) } if (tobj->return_to_pbx) { - ast_after_bridge_set_goto(tobj->chan, ast_channel_context(tobj->chan), + ast_bridge_set_after_goto(tobj->chan, ast_channel_context(tobj->chan), ast_channel_exten(tobj->chan), ast_channel_priority(tobj->chan)); - ast_after_bridge_set_goto(tobj->peer, ast_channel_context(tobj->peer), + ast_bridge_set_after_goto(tobj->peer, ast_channel_context(tobj->peer), ast_channel_exten(tobj->peer), ast_channel_priority(tobj->peer)); } ast_bridge_call(tobj->chan, tobj->peer, &tobj->bconfig); - ast_after_bridge_goto_run(tobj->chan); + ast_bridge_run_after_goto(tobj->chan); ast_free(tobj); @@ -3544,7 +3545,7 @@ static void bridge_check_monitor(struct ast_channel *chan, struct ast_channel *p */ static void bridge_failed_peer_goto(struct ast_channel *chan, struct ast_channel *peer) { - if (ast_after_bridge_goto_setup(peer) + if (ast_bridge_setup_after_goto(peer) || ast_pbx_start(peer)) { ast_autoservice_chan_hangup_peer(chan, peer); } @@ -4399,7 +4400,7 @@ static int action_bridge(struct mansession *s, const struct message *m) return 0; } - ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL); + ast_bridge_set_after_go_on(chana, chana_context, chana_exten, chana_priority, NULL); if (ast_bridge_add_channel(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1, xfer_cfg_a ? xfer_cfg_a->xfersound : NULL)) { snprintf(buf, sizeof(buf), "Unable to add Channel1 to bridge: %s", ast_channel_name(chana)); astman_send_error(s, m, buf); @@ -4407,7 +4408,7 @@ static int action_bridge(struct mansession *s, const struct message *m) return 0; } - ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL); + ast_bridge_set_after_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL); if (ast_bridge_add_channel(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2, xfer_cfg_b ? xfer_cfg_b->xfersound : NULL)) { snprintf(buf, sizeof(buf), "Unable to add Channel2 to bridge: %s", ast_channel_name(chanb)); astman_send_error(s, m, buf); @@ -5046,7 +5047,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) extension = ast_strdupa(ast_channel_exten(chan)); priority = ast_channel_priority(chan); ast_channel_unlock(chan); - ast_after_bridge_set_go_on(current_dest_chan, context, extension, priority, + ast_bridge_set_after_go_on(current_dest_chan, context, extension, priority, opt_args[OPT_ARG_CALLEE_GO_ON]); } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { ast_channel_lock(current_dest_chan); @@ -5054,7 +5055,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) extension = ast_strdupa(ast_channel_exten(current_dest_chan)); priority = ast_channel_priority(current_dest_chan); ast_channel_unlock(current_dest_chan); - ast_after_bridge_set_goto(current_dest_chan, context, extension, priority); + ast_bridge_set_after_goto(current_dest_chan, context, extension, priority); } if (ast_bridge_features_init(&chan_features)) { diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c index ced288caf0..15d4980d2b 100644 --- a/res/parking/parking_bridge_features.c +++ b/res/parking/parking_bridge_features.c @@ -34,7 +34,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/bridging.h" #include "asterisk/bridging_internal.h" -#include "asterisk/bridging_channel_internal.h" #include "asterisk/bridging_channel.h" #include "asterisk/bridging_features.h" #include "asterisk/features.h" @@ -404,7 +403,7 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg user->resolution = PARK_TIMEOUT; ao2_unlock(user); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); /* Set parking timeout channel variables */ snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space); @@ -492,14 +491,14 @@ void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *pa if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) { /* If say_parking_space is called with a non-numeric string, we have a problem. */ ast_assert(0); - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); return; } ast_say_digits(bridge_channel->chan, numeric_value, "", ast_channel_language(bridge_channel->chan)); if (hangup_after) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } } -- GitLab