res_parking: Add Parking manager action to the new parking system
authorJonathan Rose <jrose@digium.com>
Tue, 25 Jun 2013 22:28:22 +0000 (22:28 +0000)
committerJonathan Rose <jrose@digium.com>
Tue, 25 Jun 2013 22:28:22 +0000 (22:28 +0000)
(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
include/asterisk/features.h
main/bridging.c
main/features.c
res/parking/parking_applications.c
res/parking/parking_bridge.c
res/parking/parking_bridge_features.c
res/parking/parking_controller.c
res/parking/parking_manager.c
res/parking/res_parking.h

diff --git a/CHANGES b/CHANGES
index ea1bef4..cea4038 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -330,6 +330,11 @@ Parking
  * The AMI command 'ParkedCalls' will now accept a 'ParkingLot' argument which
    can be used to get a list of parked calls only for a specific parking lot.
 
+ * The AMI command 'Park' has had the argument 'Channel2' renamed to
+   'TimeoutChannel'. 'TimeoutChannel' is no longer a required argument.
+   'Channel2' can still be used as the argument name, but it is deprecated
+   and the 'TimeoutChannel' argument will be used if both are present.
+
  * The ParkAndAnnounce application is now provided through res_parking instead
    of through the separate app_parkandannounce module.
 
index 9b58650..b3f5e6c 100644 (file)
@@ -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.
index 0530424..b0c2796 100644 (file)
@@ -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;
index c26e2de..2bb8ffd 100644 (file)
@@ -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);
index 097329b..2b92125 100644 (file)
@@ -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;
        }
 
index ac9b325..60d05ed 100644 (file)
@@ -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);
 
index 3c01207..f0cf0ae 100644 (file)
@@ -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);
index 8f2433b..2764f50 100644 (file)
@@ -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);
index 444c5a5..5a2b3f6 100644 (file)
@@ -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;
 }
index cee93a6..a026be4 100644 (file)
@@ -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 {