[asterisk-commits] jrose: trunk r392915 - in /trunk: ./ include/asterisk/ main/ res/parking/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jun 25 17:28:27 CDT 2013


Author: jrose
Date: Tue Jun 25 17:28:22 2013
New Revision: 392915

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392915
Log:
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/

Modified:
    trunk/CHANGES
    trunk/include/asterisk/features.h
    trunk/main/bridging.c
    trunk/main/features.c
    trunk/res/parking/parking_applications.c
    trunk/res/parking/parking_bridge.c
    trunk/res/parking/parking_bridge_features.c
    trunk/res/parking/parking_controller.c
    trunk/res/parking/parking_manager.c
    trunk/res/parking/res_parking.h

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue Jun 25 17:28:22 2013
@@ -329,6 +329,11 @@
 
  * 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.

Modified: trunk/include/asterisk/features.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/features.h?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/include/asterisk/features.h (original)
+++ trunk/include/asterisk/features.h Tue Jun 25 17:28:22 2013
@@ -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 @@
 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.

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Tue Jun 25 17:28:22 2013
@@ -528,45 +528,66 @@
 
 /*!
  * \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;
-
+ * \brief Determine whether a bridge channel leaving the bridge will cause it to dissolve or not.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel causing the check
+ * \param bridge The bridge we are concerned with
+ *
+ * \note the bridge should be locked prior to calling this function
+ *
+ * \retval 0 if the channel leaving shouldn't cause the bridge to dissolve
+ * \retval non-zero if the channel should cause the bridge to dissolve
+ */
+static int bridge_check_will_dissolve(struct ast_bridge_channel *bridge_channel, struct ast_bridge *bridge, int assume_end_state)
+{
 	if (bridge->dissolved) {
-		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;
-	}
-
-	switch (bridge_channel->state) {
+		return 1;
+	}
+
+	switch (assume_end_state ? AST_BRIDGE_CHANNEL_STATE_END : bridge_channel->state) {
 	case AST_BRIDGE_CHANNEL_STATE_END:
 		/* Do we need to dissolve the bridge because this channel hung up? */
 		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
 			|| (bridge_channel->features->usable
 				&& ast_test_flag(&bridge_channel->features->feature_flags,
 					AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) {
-			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 @@
 	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;

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Tue Jun 25 17:28:22 2013
@@ -254,29 +254,6 @@
 			</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 @@
 	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 @@
 	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 @@
 		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 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 };
 
 /*!
- * \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 @@
 	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 @@
 		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 @@
 		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);

Modified: trunk/res/parking/parking_applications.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_applications.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/parking_applications.c (original)
+++ trunk/res/parking/parking_applications.c Tue Jun 25 17:28:22 2013
@@ -375,24 +375,12 @@
 	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)
-{
-	int use_ringing = 0;
-	int randomize = 0;
-	int time_limit = -1;
-	char *lot_name;
-
+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)
+{
 	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)) {
@@ -412,16 +400,34 @@
 	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;
-	}
-
-	/* Couldn't get the parking bridge. Epic failure. */
-	return NULL;
+	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);
+	}
+
+	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 @@
 	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 @@
 	}
 
 	/* 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;
 	}
 

Modified: trunk/res/parking/parking_bridge.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_bridge.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/parking_bridge.c (original)
+++ trunk/res/parking/parking_bridge.c Tue Jun 25 17:28:22 2013
@@ -106,6 +106,9 @@
 		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);
@@ -125,7 +128,6 @@
 			}
 		}
 	}
-
 
 	/* 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);

Modified: trunk/res/parking/parking_bridge_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_bridge_features.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/parking_bridge_features.c (original)
+++ trunk/res/parking/parking_bridge_features.c Tue Jun 25 17:28:22 2013
@@ -328,7 +328,7 @@
 		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 @@
 	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);

Modified: trunk/res/parking/parking_controller.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_controller.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/parking_controller.c (original)
+++ trunk/res/parking/parking_controller.c Tue Jun 25 17:28:22 2013
@@ -244,15 +244,8 @@
 {
 	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);

Modified: trunk/res/parking/parking_manager.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_manager.c?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/parking_manager.c (original)
+++ trunk/res/parking/parking_manager.c Tue Jun 25 17:28:22 2013
@@ -37,6 +37,7 @@
 #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 @@
 			<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 @@
 	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 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;
 }

Modified: trunk/res/parking/res_parking.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/res_parking.h?view=diff&rev=392915&r1=392914&r2=392915
==============================================================================
--- trunk/res/parking/res_parking.h (original)
+++ trunk/res/parking/res_parking.h Tue Jun 25 17:28:22 2013
@@ -105,8 +105,9 @@
 	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 */
 };
 
@@ -332,6 +333,15 @@
  * \param event_type What parked call event type is provoking this message
  */
 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
@@ -351,7 +361,7 @@
  *
  * \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 {




More information about the asterisk-commits mailing list