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
changes/78/78/1
Jonathan Rose 12 years ago
parent 5b40420813
commit 854c4c64fe

@ -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.
*

@ -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;

@ -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;
}
/* 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;
}

@ -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 {

Loading…
Cancel
Save