diff options
author | Jonathan Rose <jrose@digium.com> | 2013-06-25 22:28:22 +0000 |
---|---|---|
committer | Jonathan Rose <jrose@digium.com> | 2013-06-25 22:28:22 +0000 |
commit | 854c4c64fe2851312b1e13857dcd18e743e07594 (patch) | |
tree | 80d042e06f38f95d4f1fd2adc07658b2cbd46038 | |
parent | 5b40420813318e08b9186d41aaf1d1aaff8d61e1 (diff) |
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
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | include/asterisk/features.h | 30 | ||||
-rw-r--r-- | main/bridging.c | 116 | ||||
-rw-r--r-- | main/features.c | 214 | ||||
-rw-r--r-- | res/parking/parking_applications.c | 56 | ||||
-rw-r--r-- | res/parking/parking_bridge.c | 4 | ||||
-rw-r--r-- | res/parking/parking_bridge_features.c | 4 | ||||
-rw-r--r-- | res/parking/parking_controller.c | 9 | ||||
-rw-r--r-- | res/parking/parking_manager.c | 89 | ||||
-rw-r--r-- | res/parking/res_parking.h | 14 |
10 files changed, 283 insertions, 258 deletions
@@ -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 { |