mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-21 01:01:02 +00:00
res_parking: Add Parking manager action to the new parking system
(closes issue ASTERISK-21641) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2573/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@392915 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
5
CHANGES
5
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.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -167,6 +168,33 @@ void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval st
|
||||
/*! \brief Bridge a call, optionally allowing redirection */
|
||||
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.
|
||||
*
|
||||
|
||||
136
main/bridging.c
136
main/bridging.c
@@ -526,6 +526,50 @@ static void bridge_dissolve(struct ast_bridge *bridge)
|
||||
ast_bridge_queue_action(bridge, &action);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \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 The bridge we are concerned with
|
||||
*
|
||||
* \note the bridge should be locked prior to calling this function
|
||||
*
|
||||
* \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 int bridge_check_will_dissolve(struct ast_bridge_channel *bridge_channel, struct ast_bridge *bridge, int assume_end_state)
|
||||
{
|
||||
if (bridge->dissolved) {
|
||||
/* 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. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
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))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Check if a bridge should dissolve and do it.
|
||||
@@ -541,32 +585,9 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct ast_bridge *bridge = bridge_channel->bridge;
|
||||
|
||||
if (bridge->dissolved) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bridge->num_channels
|
||||
&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
|
||||
/* Last channel leaving the bridge turns off the lights. */
|
||||
if (bridge_check_will_dissolve(bridge_channel, bridge, 0)) {
|
||||
bridge_dissolve(bridge);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -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;
|
||||
|
||||
214
main/features.c
214
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);
|
||||
@@ -4571,100 +4473,6 @@ static struct ast_cli_entry cli_features[] = {
|
||||
AST_CLI_DEFINE(handle_features_reload, "Reloads configured 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.
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/* Couldn't get the parking bridge. Epic failure. */
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
@@ -333,6 +334,15 @@ void publish_parked_call_failure(struct ast_channel *parkee);
|
||||
*/
|
||||
void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type 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
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user