summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--include/asterisk/features.h30
-rw-r--r--main/bridging.c116
-rw-r--r--main/features.c214
-rw-r--r--res/parking/parking_applications.c56
-rw-r--r--res/parking/parking_bridge.c4
-rw-r--r--res/parking/parking_bridge_features.c4
-rw-r--r--res/parking/parking_controller.c9
-rw-r--r--res/parking/parking_manager.c89
-rw-r--r--res/parking/res_parking.h14
10 files changed, 283 insertions, 258 deletions
diff --git a/CHANGES b/CHANGES
index ea1bef47e..cea403804 100644
--- a/CHANGES
+++ b/CHANGES
@@ -330,6 +330,11 @@ Parking
* The AMI command 'ParkedCalls' will now accept a 'ParkingLot' argument which
can be used to get a list of parked calls only for a specific parking lot.
+ * The AMI command 'Park' has had the argument 'Channel2' renamed to
+ 'TimeoutChannel'. 'TimeoutChannel' is no longer a required argument.
+ 'Channel2' can still be used as the argument name, but it is deprecated
+ and the 'TimeoutChannel' argument will be used if both are present.
+
* The ParkAndAnnounce application is now provided through res_parking instead
of through the separate app_parkandannounce module.
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index 9b586506f..b3f5e6c74 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -17,7 +17,7 @@
*/
/*! \file
- * \brief Call Parking and Pickup API
+ * \brief Call Parking and Pickup API
* Includes code and algorithms from the Zapata library.
*/
@@ -26,6 +26,7 @@
#include "asterisk/pbx.h"
#include "asterisk/linkedlists.h"
+#include "asterisk/bridging.h"
#define FEATURE_MAX_LEN 11
#define FEATURE_APP_LEN 64
@@ -168,6 +169,33 @@ void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval st
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
/*!
+ * \brief Add an arbitrary channel to a bridge
+ * \since 12.0.0
+ *
+ * The channel that is being added to the bridge can be in any state: unbridged,
+ * bridged, answered, unanswered, etc. The channel will be added asynchronously,
+ * meaning that when this function returns once the channel has been added to
+ * the bridge, not once the channel has been removed from the bridge.
+ *
+ * In addition, a tone can optionally be played to the channel once the
+ * channel is placed into the bridge.
+ *
+ * \note When this function returns, there is no guarantee that the channel that
+ * was passed in is valid any longer. Do not attempt to operate on the channel
+ * after this function returns.
+ *
+ * \param bridge Bridge to which the channel should be added
+ * \param chan The channel to add to the bridge
+ * \param features Features for this channel in the bridge
+ * \param play_tone Indicates if a tone should be played to the channel
+ * \param xfersound Sound that should be used to indicate transfer with play_tone
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan,
+ struct ast_bridge_features *features, int play_tone, const char *xfersound);
+
+/*!
* \brief Test if a channel can be picked up.
*
* \param chan Channel to test if can be picked up.
diff --git a/main/bridging.c b/main/bridging.c
index 0530424ff..b0c27966a 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -528,45 +528,66 @@ static void bridge_dissolve(struct ast_bridge *bridge)
/*!
* \internal
- * \brief Check if a bridge should dissolve and do it.
+ * \brief Determine whether a bridge channel leaving the bridge will cause it to dissolve or not.
* \since 12.0.0
*
- * \param bridge_channel Channel causing the check.
+ * \param bridge_channel Channel causing the check
+ * \param bridge The bridge we are concerned with
*
- * \note On entry, bridge_channel->bridge is already locked.
+ * \note the bridge should be locked prior to calling this function
*
- * \return Nothing
+ * \retval 0 if the channel leaving shouldn't cause the bridge to dissolve
+ * \retval non-zero if the channel should cause the bridge to dissolve
*/
-static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
+static int bridge_check_will_dissolve(struct ast_bridge_channel *bridge_channel, struct ast_bridge *bridge, int assume_end_state)
{
- struct ast_bridge *bridge = bridge_channel->bridge;
-
if (bridge->dissolved) {
- return;
+ /* The bridge is already dissolved. Don't try to dissolve it again. */
+ return 0;
}
if (!bridge->num_channels
&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
/* Last channel leaving the bridge turns off the lights. */
- bridge_dissolve(bridge);
- return;
+ return 1;
}
- switch (bridge_channel->state) {
+ switch (assume_end_state ? AST_BRIDGE_CHANNEL_STATE_END : bridge_channel->state) {
case AST_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
&& ast_test_flag(&bridge_channel->features->feature_flags,
AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) {
- bridge_dissolve(bridge);
- return;
+ return 1;
}
+
break;
default:
break;
}
-/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
+ /* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Check if a bridge should dissolve and do it.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel causing the check.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_bridge *bridge = bridge_channel->bridge;
+
+ if (bridge_check_will_dissolve(bridge_channel, bridge, 0)) {
+ bridge_dissolve(bridge);
+ }
}
/*!
@@ -4300,6 +4321,73 @@ int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge
return res;
}
+int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan,
+ struct ast_bridge_features *features, int play_tone, const char *xfersound)
+{
+ RAII_VAR(struct ast_bridge *, chan_bridge, NULL, ao2_cleanup);
+ struct ast_channel *bridge_chan = NULL;
+
+ ast_channel_lock(chan);
+ chan_bridge = ast_channel_get_bridge(chan);
+ ast_channel_unlock(chan);
+
+ if (chan_bridge) {
+ RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+ int hangup = 0;
+
+ /* Simply moving the channel from the bridge won't perform the dissolve check
+ * so we need to manually check here to see if we should dissolve after moving. */
+ ao2_lock(chan_bridge);
+ if ((bridge_channel = ast_channel_get_bridge_channel(chan))) {
+ hangup = bridge_check_will_dissolve(bridge_channel, chan_bridge, 1);
+ }
+
+ if (ast_bridge_move(bridge, chan_bridge, chan, NULL, 1)) {
+ ao2_unlock(chan_bridge);
+ return -1;
+ }
+
+ if (hangup) {
+ bridge_dissolve(chan_bridge);
+ }
+ ao2_unlock(chan_bridge);
+
+ } else {
+ /* Slightly less easy case. We need to yank channel A from
+ * where he currently is and impart him into our bridge.
+ */
+ bridge_chan = ast_channel_yank(chan);
+ if (!bridge_chan) {
+ ast_log(LOG_WARNING, "Could not gain control of channel %s\n", ast_channel_name(chan));
+ return -1;
+ }
+ if (ast_channel_state(bridge_chan) != AST_STATE_UP) {
+ ast_answer(bridge_chan);
+ }
+ if (ast_bridge_impart(bridge, bridge_chan, NULL, features, 1)) {
+ ast_log(LOG_WARNING, "Could not add %s to the bridge\n", ast_channel_name(chan));
+ return -1;
+ }
+ }
+
+ if (play_tone && !ast_strlen_zero(xfersound)) {
+ struct ast_channel *play_chan = bridge_chan ?: chan;
+ RAII_VAR(struct ast_bridge_channel *, play_bridge_channel, NULL, ao2_cleanup);
+
+ ast_channel_lock(play_chan);
+ play_bridge_channel = ast_channel_get_bridge_channel(play_chan);
+ ast_channel_unlock(play_chan);
+
+ if (!play_bridge_channel) {
+ ast_log(LOG_WARNING, "Unable to play tone for channel %s. Unable to get bridge channel\n",
+ ast_channel_name(play_chan));
+ } else {
+ ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, xfersound, NULL);
+ }
+ }
+ return 0;
+}
+
struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
{
struct ast_bridge *bridge = bridge_channel->bridge;
diff --git a/main/features.c b/main/features.c
index c26e2deee..2bb8ffd44 100644
--- a/main/features.c
+++ b/main/features.c
@@ -254,29 +254,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variablelist>
</description>
</application>
- <manager name="Park" language="en_US">
- <synopsis>
- Park a channel.
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Channel" required="true">
- <para>Channel name to park.</para>
- </parameter>
- <parameter name="Channel2" required="true">
- <para>Channel to return to if timeout.</para>
- </parameter>
- <parameter name="Timeout">
- <para>Number of milliseconds to wait before callback.</para>
- </parameter>
- <parameter name="Parkinglot">
- <para>Specify in which parking lot to park the channel.</para>
- </parameter>
- </syntax>
- <description>
- <para>Park a channel.</para>
- </description>
- </manager>
<manager name="Bridge" language="en_US">
<synopsis>
Bridge two channels already in the PBX.
@@ -4344,87 +4321,6 @@ static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast
return CLI_SUCCESS;
}
-/*!
- * \internal
- * \brief Add an arbitrary channel to a bridge
- *
- * The channel that is being added to the bridge can be in any state: unbridged,
- * bridged, answered, unanswered, etc. The channel will be added asynchronously,
- * meaning that when this function returns once the channel has been added to
- * the bridge, not once the channel has been removed from the bridge.
- *
- * In addition, a tone can optionally be played to the channel once the
- * channel is placed into the bridge.
- *
- * \note When this function returns, there is no guarantee that the channel that
- * was passed in is valid any longer. Do not attempt to operate on the channel
- * after this function returns.
- *
- * \param bridge Bridge to which the channel should be added
- * \param chan The channel to add to the bridge
- * \param features Features for this channel in the bridge
- * \param play_tone Indicates if a tone should be played to the channel
- * \retval 0 Success
- * \retval -1 Failure
- */
-static int add_to_bridge(struct ast_bridge *bridge, struct ast_channel *chan,
- struct ast_bridge_features *features, int play_tone)
-{
- RAII_VAR(struct ast_bridge *, chan_bridge, NULL, ao2_cleanup);
- RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
- struct ast_channel *bridge_chan = NULL;
- const char *tone = NULL;
-
- ast_channel_lock(chan);
- chan_bridge = ast_channel_get_bridge(chan);
- xfer_cfg = ast_get_chan_features_xfer_config(chan);
- if (!xfer_cfg) {
- ast_log(LOG_ERROR, "Unable to determine what tone to play to channel.\n");
- } else {
- tone = ast_strdupa(xfer_cfg->xfersound);
- }
- ast_channel_unlock(chan);
-
- if (chan_bridge) {
- if (ast_bridge_move(bridge, chan_bridge, chan, NULL, 1)) {
- return -1;
- }
- } else {
- /* Slightly less easy case. We need to yank channel A from
- * where he currently is and impart him into our bridge.
- */
- bridge_chan = ast_channel_yank(chan);
- if (!bridge_chan) {
- ast_log(LOG_WARNING, "Could not gain control of channel %s\n", ast_channel_name(chan));
- return -1;
- }
- if (ast_channel_state(bridge_chan) != AST_STATE_UP) {
- ast_answer(bridge_chan);
- }
- if (ast_bridge_impart(bridge, bridge_chan, NULL, features, 1)) {
- ast_log(LOG_WARNING, "Could not add %s to the bridge\n", ast_channel_name(chan));
- return -1;
- }
- }
-
- if (play_tone && !ast_strlen_zero(tone)) {
- struct ast_channel *play_chan = bridge_chan ?: chan;
- RAII_VAR(struct ast_bridge_channel *, play_bridge_channel, NULL, ao2_cleanup);
-
- ast_channel_lock(play_chan);
- play_bridge_channel = ast_channel_get_bridge_channel(play_chan);
- ast_channel_unlock(play_chan);
-
- if (!play_bridge_channel) {
- ast_log(LOG_WARNING, "Unable to play tone for channel %s. Unable to get bridge channel\n",
- ast_channel_name(play_chan));
- } else {
- ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, tone, NULL);
- }
- }
- return 0;
-}
-
enum play_tone_action {
PLAYTONE_NONE = 0,
PLAYTONE_CHANNEL1 = (1 << 0),
@@ -4478,6 +4374,8 @@ static int action_bridge(struct mansession *s, const struct message *m)
int chanb_priority;
struct ast_bridge *bridge;
char buf[256];
+ RAII_VAR(struct ast_features_xfer_config *, xfer_cfg_a, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_features_xfer_config *, xfer_cfg_b, NULL, ao2_cleanup);
/* make sure valid channels were specified */
if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) {
@@ -4492,6 +4390,10 @@ static int action_bridge(struct mansession *s, const struct message *m)
astman_send_error(s, m, buf);
return 0;
}
+
+ xfer_cfg_a = ast_get_chan_features_xfer_config(chana);
+ xfer_cfg_b = ast_get_chan_features_xfer_config(chanb);
+
ast_channel_lock(chana);
chana_name = ast_strdupa(ast_channel_name(chana));
chana_exten = ast_strdupa(ast_channel_exten(chana));
@@ -4525,7 +4427,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
}
ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL);
- if (add_to_bridge(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1)) {
+ 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);
ast_bridge_destroy(bridge);
@@ -4533,7 +4435,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
}
ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL);
- if (add_to_bridge(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2)) {
+ 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);
ast_bridge_destroy(bridge);
@@ -4572,100 +4474,6 @@ static struct ast_cli_entry cli_features[] = {
};
/*!
- * \brief Create manager event for parked calls
- * \param s
- * \param m
- *
- * Get channels involved in park, create event.
- * \return Always 0
- *
- * \note ADSI is not compatible with this AMI action for the
- * same reason ch2 can no longer announce the parking space.
- */
-static int manager_park(struct mansession *s, const struct message *m)
-{
- const char *channel = astman_get_header(m, "Channel");
- const char *channel2 = astman_get_header(m, "Channel2");
- const char *timeout = astman_get_header(m, "Timeout");
- const char *parkinglotname = astman_get_header(m, "Parkinglot");
- char buf[BUFSIZ];
- int res = 0;
- struct ast_channel *ch1, *ch2;
- struct ast_park_call_args args = {
- /*
- * Don't say anything to ch2 since AMI is a third party parking
- * a call and we will likely crash if we do.
- *
- * XXX When the AMI action was originally implemented, the
- * parking space was announced to ch2. Unfortunately, grabbing
- * the ch2 lock and holding it while the announcement is played
- * was not really a good thing to do to begin with since it
- * could hold up the system. Also holding the lock is no longer
- * possible with a masquerade.
- *
- * Restoring the announcement to ch2 is not easily doable for
- * the following reasons:
- *
- * 1) The AMI manager is not the thread processing ch2.
- *
- * 2) ch2 could be the same as ch1, bridged to ch1, or some
- * random uninvolved channel.
- */
- .flags = AST_PARK_OPT_SILENCE,
- };
-
- if (ast_strlen_zero(channel)) {
- astman_send_error(s, m, "Channel not specified");
- return 0;
- }
-
- if (ast_strlen_zero(channel2)) {
- astman_send_error(s, m, "Channel2 not specified");
- return 0;
- }
-
- if (!ast_strlen_zero(timeout)) {
- if (sscanf(timeout, "%30d", &args.timeout) != 1) {
- astman_send_error(s, m, "Invalid timeout value.");
- return 0;
- }
- }
-
- if (!(ch1 = ast_channel_get_by_name(channel))) {
- snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
- astman_send_error(s, m, buf);
- return 0;
- }
-
- if (!(ch2 = ast_channel_get_by_name(channel2))) {
- snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
- astman_send_error(s, m, buf);
- ast_channel_unref(ch1);
- return 0;
- }
-
- if (!ast_strlen_zero(parkinglotname)) {
- args.parkinglot = find_parkinglot(parkinglotname);
- }
-
- res = masq_park_call(ch1, ch2, &args);
- if (!res) {
- ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
- astman_send_ack(s, m, "Park successful");
- } else {
- astman_send_error(s, m, "Park failure");
- }
-
- if (args.parkinglot) {
- parkinglot_unref(args.parkinglot);
- }
- ch1 = ast_channel_unref(ch1);
- ch2 = ast_channel_unref(ch2);
-
- return 0;
-}
-
-/*!
* The presence of this datastore on the channel indicates that
* someone is attemting to pickup or has picked up the channel.
* The purpose is to prevent a race between two channels
@@ -5112,6 +4920,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
struct ast_bridge_features chan_features;
struct ast_bridge_features *peer_features;
struct ast_bridge *bridge;
+ RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(dest_chan);
@@ -5251,7 +5060,9 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
goto done;
}
- if (add_to_bridge(bridge, current_dest_chan, peer_features, ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE))) {
+ xfer_cfg = ast_get_chan_features_xfer_config(current_dest_chan);
+
+ if (ast_bridge_add_channel(bridge, current_dest_chan, peer_features, ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE), xfer_cfg ? xfer_cfg->xfersound : NULL)) {
ast_bridge_features_destroy(peer_features);
ast_bridge_features_cleanup(&chan_features);
ast_bridge_destroy(bridge);
@@ -5788,7 +5599,6 @@ int ast_features_init(void)
return -1;
}
res |= ast_register_application2(app_bridge, bridge_exec, NULL, NULL, NULL);
- res |= ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
res |= ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
res |= ast_devstate_prov_add("Park", metermaidstate);
diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c
index 097329b93..2b921259f 100644
--- a/res/parking/parking_applications.c
+++ b/res/parking/parking_applications.c
@@ -375,25 +375,13 @@ void get_park_common_datastore_data(struct ast_channel *parkee, char **parker_uu
ast_channel_unlock(parkee);
}
-struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
- int *silence_announcements)
+struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
+ const char *lot_name, const char *comeback_override,
+ int use_ringing, int randomize, int time_limit, int silence_announcements)
{
- int use_ringing = 0;
- int randomize = 0;
- int time_limit = -1;
- char *lot_name;
-
struct ast_bridge *parking_bridge;
- RAII_VAR(char *, comeback_override, NULL, ast_free);
- RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
- if (app_data) {
- park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg);
- }
-
- lot_name = lot_name_app_arg;
-
/* If the name of the parking lot isn't specified in the arguments, find it based on the channel. */
if (ast_strlen_zero(lot_name)) {
ast_channel_lock(parker);
@@ -412,16 +400,34 @@ struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_chan
parking_bridge = parking_lot_get_bridge(lot);
ao2_unlock(lot);
- if (parking_bridge) {
- /* Apply relevant bridge roles and such to the parking channel */
- parking_channel_set_roles(parkee, lot, use_ringing);
- setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
- silence_announcements ? *silence_announcements : 0);
- return parking_bridge;
+ if (!parking_bridge) {
+ return NULL;
+ }
+
+ /* Apply relevant bridge roles and such to the parking channel */
+ parking_channel_set_roles(parkee, lot, use_ringing);
+ setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
+ silence_announcements);
+ return parking_bridge;
+}
+
+struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
+ int *silence_announcements)
+{
+ int use_ringing = 0;
+ int randomize = 0;
+ int time_limit = -1;
+
+ RAII_VAR(char *, comeback_override, NULL, ast_free);
+ RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
+
+ if (app_data) {
+ park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg);
}
- /* Couldn't get the parking bridge. Epic failure. */
- return NULL;
+ return park_common_setup(parkee, parker, lot_name_app_arg, comeback_override, use_ringing,
+ randomize, time_limit, silence_announcements ? *silence_announcements : 0);
+
}
/* XXX BUGBUG - determining the parker when transferred to deep park priority
@@ -452,7 +458,7 @@ int park_app_exec(struct ast_channel *chan, const char *data)
ast_channel_unlock(chan);
/* Handle the common parking setup stuff */
- if (!(parking_bridge = park_common_setup(chan, chan, data, &silence_announcements))) {
+ if (!(parking_bridge = park_application_setup(chan, chan, data, &silence_announcements))) {
if (!silence_announcements && !blind_transfer) {
ast_stream_and_wait(chan, "pbx-parkingfailed", "");
}
@@ -753,7 +759,7 @@ int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
}
/* Handle the common parking setup stuff */
- if (!(parking_bridge = park_common_setup(chan, chan, data, &silence_announcements))) {
+ if (!(parking_bridge = park_application_setup(chan, chan, data, &silence_announcements))) {
return 0;
}
diff --git a/res/parking/parking_bridge.c b/res/parking/parking_bridge.c
index ac9b32508..60d05ed59 100644
--- a/res/parking/parking_bridge.c
+++ b/res/parking/parking_bridge.c
@@ -106,6 +106,9 @@ static struct parked_user *generate_parked_user(struct parking_lot *lot, struct
return NULL;
}
+ ast_channel_lock(chan);
+ ast_copy_string(new_parked_user->blindtransfer, S_OR(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"), ""), AST_CHANNEL_NAME);
+ ast_channel_unlock(chan);
if (use_random_space) {
preferred_space = ast_random() % (lot->cfg->parking_stop - lot->cfg->parking_start + 1);
@@ -126,7 +129,6 @@ static struct parked_user *generate_parked_user(struct parking_lot *lot, struct
}
}
-
/* We need to keep the lot locked between parking_lot_get_space and actually placing it in the lot. Or until we decide not to. */
ao2_lock(lot);
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index 3c0120762..f0cf0ae44 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -328,7 +328,7 @@ static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const
return;
}
- if (!(parking_bridge = park_common_setup(bridge_channel->chan, parker, app_data, NULL))) {
+ if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) {
publish_parked_call_failure(bridge_channel->chan);
return;
}
@@ -426,7 +426,7 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
- peername = ast_strdupa(user->parker->name);
+ peername = ast_strdupa(S_OR(user->blindtransfer, user->parker->name));
channel_name_to_dial_string(peername);
peername_flat = ast_strdupa(user->parker->name);
diff --git a/res/parking/parking_controller.c b/res/parking/parking_controller.c
index 8f2433b29..2764f50d4 100644
--- a/res/parking/parking_controller.c
+++ b/res/parking/parking_controller.c
@@ -244,15 +244,8 @@ int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
{
struct ast_channel *chan = pu->chan;
char *peername;
- const char *blindtransfer;
- ast_channel_lock(chan);
- if ((blindtransfer = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"))) {
- blindtransfer = ast_strdupa(blindtransfer);
- }
- ast_channel_unlock(chan);
-
- peername = blindtransfer ? ast_strdupa(blindtransfer) : ast_strdupa(pu->parker->name);
+ peername = ast_strdupa(S_OR(pu->blindtransfer, pu->parker->name));
/* Flatten the peername so that it can be used for performing the timeout PBX operations */
flatten_peername(peername);
diff --git a/res/parking/parking_manager.c b/res/parking/parking_manager.c
index 444c5a54f..5a2b3f6fd 100644
--- a/res/parking/parking_manager.c
+++ b/res/parking/parking_manager.c
@@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/features.h"
#include "asterisk/manager.h"
+#include "asterisk/bridging.h"
/*** DOCUMENTATION
<manager name="Parkinglots" language="en_US">
@@ -64,6 +65,33 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>List parked calls.</para>
</description>
</manager>
+ <manager name="Park" language="en_US">
+ <synopsis>
+ Park a channel.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Channel" required="true">
+ <para>Channel name to park.</para>
+ </parameter>
+ <parameter name="TimeoutChannel" required="false">
+ <para>Channel name to use when constructing the dial string that will be dialed if the parked channel times out.</para>
+ </parameter>
+ <parameter name="Timeout" required="false">
+ <para>Overrides the timeout of the parking lot for this park action. Specified in milliseconds, but will be converted to
+ seconds. Use a value of 0 to nullify the timeout.
+ </para>
+ </parameter>
+ <parameter name="Parkinglot" required="false">
+ <para>The parking lot to use when parking the channel</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Park an arbitrary channel with optional arguments for specifying the parking lot used, how long
+ the channel should remain parked, and what dial string to use as the parker if the call times out.
+ </para>
+ </description>
+ </manager>
<managerEvent language="en_US" name="ParkedCall">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a channel is parked.</synopsis>
@@ -498,6 +526,61 @@ static int manager_parking_lot_list(struct mansession *s, const struct message *
return RESULT_SUCCESS;
}
+static int manager_park(struct mansession *s, const struct message *m)
+{
+ const char *channel = astman_get_header(m, "Channel");
+ const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
+ const char *timeout = astman_get_header(m, "Timeout");
+ const char *parkinglot = astman_get_header(m, "Parkinglot");
+ char buf[BUFSIZ];
+ int timeout_override = -1;
+
+ RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(timeout)) {
+ if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) {
+ astman_send_error(s, m, "Invalid Timeout value.");
+ return 0;
+ }
+
+ if (timeout_override > 0) {
+ /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
+ timeout_override = MAX(1, timeout_override / 1000);
+ }
+ }
+
+ if (!(chan = ast_channel_get_by_name(channel))) {
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+
+ ast_channel_lock(chan);
+ if (!ast_strlen_zero(timeout_channel)) {
+ pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", timeout_channel);
+ }
+ ast_channel_unlock(chan);
+
+ if (!(parking_bridge = park_common_setup(chan, chan, parkinglot, NULL, 0, 0, timeout_override, 0))) {
+ astman_send_error(s, m, "Park action failed\n");
+ return 0;
+ }
+
+ if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
+ astman_send_error(s, m, "Park action failed\n");
+ return 0;
+ }
+
+ astman_send_ack(s, m, "Park successful\n");
+ return 0;
+}
+
void publish_parked_call_failure(struct ast_channel *parkee)
{
RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
@@ -588,9 +671,9 @@ int load_parking_manager(void)
{
int res;
- res = ast_manager_register_xml_core("Parkinglots", 0, manager_parking_lot_list);
- res |= ast_manager_register_xml_core("ParkedCalls", 0, manager_parking_status);
- /* TODO Add a 'Park' manager action */
+ res = ast_manager_register_xml_core("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list);
+ res |= ast_manager_register_xml_core("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status);
+ res |= ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
parking_manager_enable_stasis();
return res ? -1 : 0;
}
diff --git a/res/parking/res_parking.h b/res/parking/res_parking.h
index cee93a6ce..a026be41a 100644
--- a/res/parking/res_parking.h
+++ b/res/parking/res_parking.h
@@ -105,8 +105,9 @@ struct parked_user {
struct timeval start; /*!< When the call was parked */
int parking_space; /*!< Which parking space is used */
char comeback[AST_MAX_CONTEXT]; /*!< Where to go on parking timeout */
+ char blindtransfer[AST_CHANNEL_NAME]; /*!< What the BLINDTRANSFER variable was at the time of entry */
unsigned int time_limit; /*!< How long this specific channel may remain in the parking lot before timing out */
- struct parking_lot *lot; /*!< Which parking lot the user is parked to */
+ struct parking_lot *lot; /*!< Which parking lot the user is parked to */
enum park_call_resolution resolution; /*!< How did the parking session end? If the call is in a bridge, lock parked_user before checking/setting */
};
@@ -335,6 +336,15 @@ void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type
/*!
* \since 12.0.0
+ * \brief Setup a parked call on a parking bridge without needing to parse appdata
+ *
+ */
+struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
+ const char *lot_name, const char *comeback_override,
+ int use_ringing, int randomize, int time_limit, int silence_announcements);
+
+/*!
+ * \since 12.0.0
* \brief Function to prepare a channel for parking by determining which parking bridge should
* be used, setting up a park common datastore so that the parking bridge will have access
* to necessary parking information when joining, and applying various bridge roles to the
@@ -351,7 +361,7 @@ void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type
*
* \note ao2_cleanup this reference when you are done using it or you'll cause leaks.
*/
-struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
+struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker,
const char *app_data, int *silence_announcements);
struct park_common_datastore {