[asterisk-commits] jrose: branch jrose/bridge_projects r384814 - in /team/jrose/bridge_projects:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 5 11:19:14 CDT 2013


Author: jrose
Date: Fri Apr  5 11:19:10 2013
New Revision: 384814

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=384814
Log:
April 5 update

Added:
    team/jrose/bridge_projects/res/parking/parking_bridge_features.c   (with props)
Modified:
    team/jrose/bridge_projects/bridges/bridge_builtin_features.c
    team/jrose/bridge_projects/include/asterisk/parking.h
    team/jrose/bridge_projects/main/bridging.c
    team/jrose/bridge_projects/main/bridging_roles.c
    team/jrose/bridge_projects/main/parking.c
    team/jrose/bridge_projects/res/parking/parking_applications.c
    team/jrose/bridge_projects/res/parking/parking_bridge.c
    team/jrose/bridge_projects/res/parking/parking_controller.c
    team/jrose/bridge_projects/res/parking/res_parking.h
    team/jrose/bridge_projects/res/res_parking.c

Modified: team/jrose/bridge_projects/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/bridges/bridge_builtin_features.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/bridges/bridge_builtin_features.c (original)
+++ team/jrose/bridge_projects/bridges/bridge_builtin_features.c Fri Apr  5 11:19:10 2013
@@ -48,6 +48,7 @@
 #include "asterisk/app.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/pbx.h"
+#include "asterisk/parking.h"
 
 /*!
  * \brief Helper function that presents dialtone and grabs extension
@@ -163,6 +164,28 @@
 	return "default";
 }
 
+/* XXX move this to parking.h */
+static struct ast_exten *get_parking_exten(const char *exten_str, struct ast_channel *chan, const char *context)
+{
+	struct ast_exten *exten;
+	struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+	const char *app_at_exten;
+
+	ast_debug(4, "Checking if %s@%s is a parking exten\n", exten_str, context);
+	exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
+		E_MATCH);
+	if (!exten) {
+		return NULL;
+	}
+
+	app_at_exten = ast_get_extension_app(exten);
+	if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
+		return NULL;
+	}
+
+	return exten;
+}
+
 /*! \brief Internal built in feature for blind transfers */
 static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
@@ -170,6 +193,7 @@
 	struct ast_channel *chan = NULL;
 	struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
 	const char *context;
+	struct ast_exten *park_exten;
 
 /* BUGBUG the peer needs to be put on hold for the transfer. */
 	ast_channel_lock(bridge_channel->chan);
@@ -179,6 +203,16 @@
 
 	/* Grab the extension to transfer to */
 	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+		return 0;
+	}
+
+	/* Parking blind transfer override - phase this out for something more general purpose in the future. */
+	park_exten = get_parking_exten(exten, bridge_channel->chan, context);
+	if (park_exten) {
+		/* We are transfering the transferee to a parking lot. */
+		if (ast_park_blind_xfer(bridge, bridge_channel, ast_get_extension_app_data(park_exten))) {
+			ast_log(LOG_ERROR, "%s attempted to transfer to park application and failed.\n", ast_channel_name(bridge_channel->chan));
+		};
 		return 0;
 	}
 

Modified: team/jrose/bridge_projects/include/asterisk/parking.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/include/asterisk/parking.h?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/include/asterisk/parking.h (original)
+++ team/jrose/bridge_projects/include/asterisk/parking.h Fri Apr  5 11:19:10 2013
@@ -24,6 +24,8 @@
  */
 
 #include "asterisk/stringfields.h"
+
+#define PARK_APPLICATION "Park"
 
 /*!
  * \brief Defines the type of parked call message being published
@@ -98,3 +100,32 @@
  * \retval a pointer to the parked call message type
  */
 struct stasis_message_type *ast_parked_call_type(void);
+
+/*!
+ * \brief install a callback for handling blind transfers to a parking extension
+ * \since 12
+ *
+ * \param parking_func Function to use for transfers to 'Park' applications
+ */
+void ast_install_park_blind_xfer_func(int (*park_blind_xfer_func)(struct ast_bridge *bridge, struct ast_bridge_channel *parker,
+		const char *app_data));
+
+/*!
+ * \brief uninstall a callback for handling blind transfers to a parking extension
+ * \since 12
+ */
+void ast_uninstall_park_blind_xfer_func(void);
+
+/*!
+ * \brief use the installed park blind xfer func
+ * \since 12
+ *
+ * \param bridge Bridge being transferred from
+ * \param bridge_channel Bridge channel initiating the transfer
+ * \param app_data arguments to the park application
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_park_blind_xfer(struct ast_bridge *bridge, struct ast_bridge_channel *parker,
+		const char *app_data);

Modified: team/jrose/bridge_projects/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/bridging.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/main/bridging.c (original)
+++ team/jrose/bridge_projects/main/bridging.c Fri Apr  5 11:19:10 2013
@@ -2628,6 +2628,10 @@
 
 	ast_bridge_channel_pull(bridge_channel);
 
+	/* While we are pulled we need to re-establish the channel roles */
+	ast_bridge_channel_clear_roles(bridge_channel);
+	ast_bridge_channel_establish_roles(bridge_channel);
+
 	/* Point to new bridge. */
 	ao2_ref(bridge_dst, +1);
 	ast_bridge_channel_lock(bridge_channel);

Modified: team/jrose/bridge_projects/main/bridging_roles.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/bridging_roles.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/main/bridging_roles.c (original)
+++ team/jrose/bridge_projects/main/bridging_roles.c Fri Apr  5 11:19:10 2013
@@ -41,11 +41,14 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_roles.h"
+#include "asterisk/stringfields.h"
 
 struct bridge_role_option {
 	AST_LIST_ENTRY(bridge_role_option) list;
-	char option[AST_ROLE_LEN];
-	char value[1];
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(option);
+		AST_STRING_FIELD(value);
+	);
 };
 
 struct bridge_role {
@@ -71,6 +74,7 @@
 {
 	struct bridge_role_option *role_option;
 	while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
+		ast_string_field_free_memory(role_option);
 		ast_free(role_option);
 	}
 	ast_free(role);
@@ -290,13 +294,18 @@
 		value = "";
 	}
 
-	role_option = ast_calloc(1, sizeof(*role_option) + strlen(value));
+	role_option = ast_calloc(1, sizeof(*role_option));
 	if (!role_option) {
 		return -1;
 	}
 
-	ast_copy_string(role_option->option, option, sizeof(role_option->option));
-	strcpy(role_option->value, value);
+	if (ast_string_field_init(role_option, 32)) {
+		ast_free(role_option);
+		return -1;
+	}
+
+	ast_string_field_set(role_option, option, option);
+	ast_string_field_set(role_option, value, value);
 
 	AST_LIST_INSERT_TAIL(&role->options, role_option, list);
 
@@ -358,8 +367,8 @@
 	role_option = get_role_option(role, option);
 
 	if (role_option) {
-		/* We need to clear the option out and recreate it. There is no way to do this yet. Implement it later. XXX */
-		return -1;
+		ast_string_field_set(role_option, value, value);
+		return 0;
 	}
 
 	setup_bridge_role_option(role, option, value);

Modified: team/jrose/bridge_projects/main/parking.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/parking.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/main/parking.c (original)
+++ team/jrose/bridge_projects/main/parking.c Fri Apr  5 11:19:10 2013
@@ -28,6 +28,9 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/pbx.h"
+#include "asterisk/bridging.h"
 #include "asterisk/parking.h"
 #include "asterisk/channel.h"
 
@@ -36,6 +39,10 @@
 
 /*! \brief Topic for parking lots */
 static struct stasis_topic *parking_topic;
+
+/*! \brief Funcion Callback for handling blind transfers to park applications */
+static int (*ast_park_blind_xfer_func)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+		const char *app_data) = NULL;
 
 void ast_parking_stasis_init(void)
 {
@@ -71,7 +78,10 @@
 	ast_string_field_free_memory(park_obj);
 }
 
-struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_call_event_type event_type, struct ast_channel_snapshot *parkee_snapshot, struct ast_channel_snapshot *parker_snapshot, const char *parkinglot, unsigned int parkingspace, unsigned long int timeout, unsigned long int duration)
+struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_call_event_type event_type,
+		struct ast_channel_snapshot *parkee_snapshot, struct ast_channel_snapshot *parker_snapshot,
+		const char *parkinglot, unsigned int parkingspace, unsigned long int timeout,
+		unsigned long int duration)
 {
 	RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
 
@@ -101,3 +111,30 @@
 	ao2_ref(payload, +1);
 	return payload;
 }
+
+void ast_install_park_blind_xfer_func(int (*park_blind_xfer_func)(struct ast_bridge *bridge, struct ast_bridge_channel *parker,
+	const char *app_data))
+{
+	ast_park_blind_xfer_func = park_blind_xfer_func;
+}
+
+void ast_uninstall_park_blind_xfer_func(void)
+{
+	ast_park_blind_xfer_func = NULL;
+}
+
+int ast_park_blind_xfer(struct ast_bridge *bridge, struct ast_bridge_channel *parker,
+		const char *app_data)
+{
+	static int warned = 0;
+	if (ast_park_blind_xfer_func) {
+		return ast_park_blind_xfer_func(bridge, parker, app_data);
+	}
+
+	if (warned++ % 10 == 0) {
+		ast_verb(3, "%s attempted to blind transfer to a parking extension, but no parking blind transfer function is loaded.\n",
+			ast_channel_name(parker->chan));
+	}
+
+	return -1;
+}

Modified: team/jrose/bridge_projects/res/parking/parking_applications.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/res/parking/parking_applications.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/res/parking/parking_applications.c (original)
+++ team/jrose/bridge_projects/res/parking/parking_applications.c Fri Apr  5 11:19:10 2013
@@ -170,19 +170,10 @@
 	return 0;
 }
 
-int park_app_exec(struct ast_channel *chan, const char *data)
-{
-	RAII_VAR(struct parking_lot_state *, lot, NULL, ao2_cleanup);
-	RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
-
-	struct ast_bridge_features chan_features;
+static int park_app_parse_data(const char *data, int *disable_announce, int *use_ringing, int *randomize, int *time_limit, char **comeback_override, char **lot_name)
+{
+	char *parse;
 	struct ast_flags flags = { 0 };
-	char *parse;
-	int time_limit = -1;
-	char *comeback_override = NULL;
-
-	int res;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(lot_name);
@@ -197,111 +188,136 @@
 		char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
 		ast_app_parse_options(park_opts, &flags, opts, args.options);
 		if (ast_test_flag(&flags, MUXFLAG_TIMEOUT_OVERRIDE)) {
-			if (apply_option_timeout(&time_limit, opts[OPT_ARG_TIMEOUT])) {
+			if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
 				return -1;
 			}
 		}
+
 		if (ast_test_flag(&flags, MUXFLAG_COMEBACK_OVERRIDE)) {
-			comeback_override = ast_strdupa(opts[OPT_ARG_COMEBACK]);
-		}
-	}
-
-	if (ast_strlen_zero(args.lot_name)) {
-		lot = channel_find_parking_lot_state(chan);
+			*comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
+		}
+
+		if (ast_test_flag(&flags, MUXFLAG_NOANNOUNCE)) {
+			*disable_announce = 1;
+		}
+
+		if (ast_test_flag(&flags, MUXFLAG_RINGING)) {
+			*use_ringing = 1;
+		}
+
+		if (ast_test_flag(&flags, MUXFLAG_RANDOMIZE)) {
+			*randomize = 1;
+		}
+	}
+
+	if (!ast_strlen_zero(args.lot_name)) {
+		*lot_name = ast_strdup(args.lot_name);
+	}
+
+	return 0;
+}
+
+int park_common_setup(struct parking_lot_state **lot, struct ast_bridge **parking_bridge, struct parked_user **pu,
+		struct ast_channel *parker, struct ast_channel *parkee, const char *app_data)
+{
+	int silence_announcements = 0;
+	int use_ringing = 0;
+	int randomize = 0;
+	int time_limit = -1;
+	RAII_VAR(char *, comeback_override, NULL, ast_free);
+	RAII_VAR(char *, lot_name, NULL, ast_free);
+
+	/* parse the app data if we have it */
+	if (app_data) {
+		park_app_parse_data(app_data, &silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name);
+	}
+
+	/* Now find out which parking lot we want to park it into. */
+	if (ast_strlen_zero(lot_name)) {
+		*lot = channel_find_parking_lot_state(parker);
 	} else {
-		lot = parking_lot_state_find_by_name(args.lot_name);
-	}
-
-	if (!lot) {
+		*lot = parking_lot_state_find_by_name(lot_name);
+	}
+
+	if (!*lot) {
 		ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
-		return -1;
-	}
-
-	ao2_lock(lot);
-	parking_bridge = parking_lot_state_get_bridge(lot);
-	ao2_unlock(lot);
-
-	if (!parking_bridge) {
+		ast_stream_and_wait(parker, "pbx-parkingfailed", "");
+		return -1;
+	}
+
+	ao2_lock(*lot);
+	*parking_bridge = parking_lot_state_get_bridge(*lot);
+	ao2_unlock(*lot);
+
+	if (!*parking_bridge) {
 		ast_log(LOG_ERROR, "Could not acquire holding bridge to park into\n");
-		return -1;
-	}
+		ast_stream_and_wait(parker, "pbx-parkingfailed", "");
+		return -1;
+	}
+
+	*pu = generate_parked_user(*lot, parkee, parker, randomize, time_limit);
+
+	if (!*pu) {
+		ast_log(LOG_ERROR, "Failed to create parked user for channel %s, can not park.\n", ast_channel_name(parkee));
+		ast_stream_and_wait(parker, "pbx-parkingfailed", "");
+		return -1;
+	}
+
+	/* Announce parking space to the parking channel */
+	if (!silence_announcements) {
+		ast_say_digits(parker, (*pu)->parking_space, "", ast_channel_language(parker));
+	}
+
+	/* Apply relevant bridge roles and such to the parking channel */
+	parking_channel_set_roles(parkee, *lot, use_ringing);
+
+	/* If we need to go somewhere special after timeout, set special comeback arguments */
+	if (!ast_strlen_zero(comeback_override)) {
+		ast_copy_string((*pu)->comeback, comeback_override, sizeof((*pu)->comeback));
+	}
+
+	return 0;
+}
+
+int park_app_exec(struct ast_channel *chan, const char *data)
+{
+	RAII_VAR(struct parking_lot_state *, lot, NULL, ao2_cleanup);
+	RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
+
+	struct ast_bridge_features chan_features;
+	int res;
 
 	/* answer the channel if needed */
 	if (ast_channel_state(chan) != AST_STATE_UP) {
 		ast_answer(chan);
 	}
 
-	pu = generate_parked_user(lot, chan, chan, ast_test_flag(&flags, MUXFLAG_RANDOMIZE), time_limit);
-
-	if (!pu) {
-		ast_log(LOG_ERROR, "Failed to create parked user for channel %s, can not park.\n", ast_channel_name(chan));
-		return -1;
-	}
-
-	/* If we aren't in silence mode, go ahead and announce the parking space */
-	if (!ast_test_flag(&flags, MUXFLAG_NOANNOUNCE)) {
-		ast_say_digits(chan, pu->parking_space, "", ast_channel_language(chan));
-	}
-
+	/* Handle all the common aprking setup stuff */
+	if (park_common_setup(&lot, &parking_bridge, &pu, chan, chan, data)) {
+		return -1;
+	}
+
+	/* Now for the fun part... park it! */
 	ast_bridge_features_init(&chan_features);
-
-	/* Set limits and apply channel roles */
-	parking_set_duration(&chan_features, pu, time_limit);
-	parking_channel_set_roles(chan, lot, ast_test_flag(&flags, MUXFLAG_RINGING));
-
-	/* Alright, now wait in the holding bridge until we get removed from it for some reason. */
-	reset_parked_time(pu);
 	ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
 
 	/*
 	 * If the bridge was broken for a hangup that isn't real, then
 	 * don't run the h extension, because the channel isn't really
-	 * hung up.  This should really only happen with
-	 * AST_SOFTHANGUP_ASYNCGOTO.
+	 * hung up.  This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
 	 */
 	res = -1;
+
 	ast_channel_lock(chan);
 	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
 		res = 0;
 	}
 	ast_channel_unlock(chan);
 
-	/* At this point the channel is no longer in the bridge. Start breaking things down. */
 	ast_bridge_features_cleanup(&chan_features);
 
-	switch (pu->resolution) {
-	case PARK_UNSET:
-		/* If the parked channel exits without a resolution set, it gave up. Issue a ParkedCallGiveUp message and exit early. */
-		pu->resolution = PARK_ABANDON;
-		publish_parked_call(pu, PARKED_CALL_GIVEUP);
-		unpark_parked_user(pu);
-		return res;
-	case PARK_ABANDON:
-	case PARK_FORCED:
-	case PARK_ANSWERED:
-		return res;
-	case PARK_TIMEOUT:
-		/* If we timed out, we need to do some fancy stuff when we re-enter the PBX */
-		break;
-	}
-
-	unpark_parked_user(pu);
-
-	/* Figure out if we need to go somewhere special now that the timeout has occured */
-	if (ast_test_flag(&flags, MUXFLAG_COMEBACK_OVERRIDE)) {
-		if (ast_parseable_goto(chan, comeback_override)) {
-			return -1;
-		}
-	} else {
-		if (comeback_goto(pu, lot)) {
-			return -1;
-		}
-	}
-
-	/* Since this is executed from pbx, it'll automatically bump the priority when we return. We can address that by reducing it by 1 before returning. Smooth. */
-	ast_channel_priority_set(chan, ast_channel_priority(chan) - 1);
-
-	return ast_check_hangup_locked(chan) ? -1 : 0;
+	return res;
 }
 
 int parked_call_app_exec(struct ast_channel *chan, const char *data)
@@ -333,11 +349,13 @@
 
 	if (!lot) {
 		ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
+		ast_stream_and_wait(chan, "pbx-invalidpark", "");
 		return -1;
 	}
 
 	if (!ast_strlen_zero(args.parking_space)) {
 		if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
+			ast_stream_and_wait(chan, "pbx-invalidpark", "");
 			ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
 			return -1;
 		}

Modified: team/jrose/bridge_projects/res/parking/parking_bridge.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/res/parking/parking_bridge.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/res/parking/parking_bridge.c (original)
+++ team/jrose/bridge_projects/res/parking/parking_bridge.c Fri Apr  5 11:19:10 2013
@@ -31,7 +31,9 @@
 struct ast_bridge_parking
 {
 	struct ast_bridge base;
+
 	/* private stuff for parking */
+	struct parking_lot_state *lot;
 };
 
 /*!
@@ -41,13 +43,12 @@
  *
  * \param self Bridge to operate upon.
  *
- * \note XXX Stub
+ * \note XXX Stub... and it might go unused.
  *
  * \return Nothing
  */
 static void bridge_parking_destroy(struct ast_bridge_parking *self)
 {
-	ast_log(LOG_NOTICE, "destroying bridge\n");
 	ast_bridge_base_v_table.destroy(&self->base);
 }
 
@@ -61,7 +62,7 @@
  * \param swap Bridge channel to swap places with if not NULL.
  *
  * \note On entry, self is already locked.
- * \note XXX Stub
+ * \note XXX Stub... and it might go unused.
  *
  * \retval TRUE if can push this channel into the bridge.
  */
@@ -80,13 +81,61 @@
  * \param swap Bridge channel to swap places with if not NULL
  *
  * \note On entry, self is already locked
- * \note XXX Stub
  *
  * \retval 0 on success
  * \retval -1 on failure
  */
 static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 {
+	struct parked_user *pu;
+	struct ao2_iterator iter;
+
+	/* Swaps for parking bridges should only occur as a result of local channel optimization */
+	if (swap) {
+		pu = swap->bridge_pvt;
+		if (!pu) {
+			/* This should be impossible since the only way a channel can enter in the first place is if it has a parked user associated with it */
+			return -1;
+		}
+
+		/* Give the swap channel's parked user to the incoming channel */
+		pu->chan = bridge_channel->chan;
+		bridge_channel->bridge_pvt = pu;
+		swap->bridge_pvt = NULL;
+
+		/* XXX Add a parked call swap message type to relay information about parked channel swaps */
+
+		ao2_ref(pu, -1);
+		return 0;
+	}
+
+	/* We should already have a parked user set for the channel when we push */
+	for (iter = ao2_iterator_init(self->lot->parked_user_list, 0); (pu = ao2_iterator_next(&iter)); ao2_ref(pu, -1)) {
+		if (bridge_channel->chan != pu->chan) {
+			continue;
+		}
+		break;
+	}
+	ao2_iterator_destroy(&iter);
+
+	if (!pu) {
+		/* This should be impossible since the parked user for the channel should be generated before it enters if it's not swapping. */
+		return -1;
+	}
+
+	/* Apply parking duration limits */
+	if (bridge_channel->features) {
+		reset_parked_time(pu);
+		parking_set_duration(bridge_channel->features, pu);
+	}
+
+	/* Set this to the bridge pvt so that we don't have to refind the parked user associated with this bridge channel again. */
+	bridge_channel->bridge_pvt = pu;
+
+	/* Generate ParkedCall Stasis Message */
+	publish_parked_call(pu, PARKED_CALL);
+
+	ao2_ref(pu, -1);
 	return 0;
 }
 
@@ -99,13 +148,54 @@
  * \param bridge_channel Bridge channel to pull.
  *
  * \note On entry, self is already locked.
- * \note XXX Stub
  *
  * \return Nothing
  */
 static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
 {
+	struct parked_user *pu = bridge_channel->bridge_pvt;
 	ast_bridge_base_v_table.pull(&self->base, bridge_channel);
+
+	/* This should only happen if the exiting channel was swapped out */
+	if (!pu) {
+		return;
+	}
+
+	/* If we got here without the resolution being set, that's because the call was hung up for some reason without
+	 * timing out or being picked up. There may be some forcible park removals later, but the resolution should be
+	 * handled in those cases */
+	ao2_lock(pu);
+	if (pu->resolution == PARK_UNSET) {
+		pu->resolution = PARK_ABANDON;
+	}
+	ao2_unlock(pu);
+
+	switch (pu->resolution) {
+	case PARK_UNSET:
+		/* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
+		   isn't allowed to be changed when it isn't currently PARK_UNSET. */
+		return;
+	case PARK_ABANDON:
+		/* Since the call was abandoned without additional handling, we need to issue the give up event and unpark the user. */
+		publish_parked_call(pu, PARKED_CALL_GIVEUP);
+		unpark_parked_user(pu);
+		return;
+	case PARK_FORCED:
+		/* PARK_FORCED is currently unused, but it is expected that it would be handled similar to PARK_ANSWERED.
+		 * There is currently no event related to forced parked calls either */
+		return;
+	case PARK_ANSWERED:
+		/* If answered or forced, the channel should be pulled from the bridge as part of that process and unlinked from
+		 * the parking lot afterwards. */
+		publish_parked_call(pu, PARKED_CALL_UNPARKED);
+		return;
+	case PARK_TIMEOUT:
+		/* Timeout is similar to abandon because it simply sets the bridge state to end and doesn't
+		 * actually pull the channel. Because of that, unpark should happen in here. */
+		publish_parked_call(pu, PARKED_CALL_TIMEOUT);
+		unpark_parked_user(pu);
+		return;
+	}
 }
 
 /*!
@@ -117,7 +207,7 @@
  * \param bridge_channel Bridge channel that was masqueraded.
  *
  * \note On entry, self is already locked.
- * \note XXX Stub
+ * \note XXX Stub... and it will probably go unused.
  *
  * \return Nothing
  */
@@ -141,6 +231,15 @@
 		return NULL;
 	}
 
+	/* If no lot is defined for the bridge, then we aren't allowing the bridge to be initialized. */
+	if (!bridge_lot) {
+		ao2_ref(self, -1);
+		return NULL;
+	}
+
+	/* It doesn't need to be a reference since the bridge only lives as long as the parking lot state lives. */
+	self->lot = bridge_lot;
+
 	return &self->base;
 }
 
@@ -149,7 +248,12 @@
 	void *bridge;
 
 	bridge = ast_bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
+
 	bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM);
+	if (!bridge) {
+		return NULL;
+	}
+
 	bridge = ast_bridge_parking_init(bridge, bridge_lot);
 
 	return bridge;

Added: team/jrose/bridge_projects/res/parking/parking_bridge_features.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/res/parking/parking_bridge_features.c?view=auto&rev=384814
==============================================================================
--- team/jrose/bridge_projects/res/parking/parking_bridge_features.c (added)
+++ team/jrose/bridge_projects/res/parking/parking_bridge_features.c Fri Apr  5 11:19:10 2013
@@ -1,0 +1,146 @@
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "res_parking.h"
+#include "asterisk/utils.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/logger.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_features.h"
+#include "asterisk/features.h"
+#include "asterisk/say.h"
+
+static int park_feature_helper(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, const char *app_data)
+{
+	struct ast_bridge_channel *other;
+
+	RAII_VAR(struct parking_lot_state *, lot, NULL, ao2_cleanup);
+	RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
+
+	/* If this is the only channel in this bridge then this feature shouldn't be at all usable. */
+	if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) {
+		return 0;
+	}
+
+	/* Find the channel we want to park */
+	other = AST_LIST_FIRST(&bridge->channels);
+	if (other == bridge_channel) {
+		other = AST_LIST_LAST(&bridge->channels);
+	}
+
+	/* Handle all the common parking setup stuff */
+	if (park_common_setup(&lot, &parking_bridge, &pu, bridge_channel->chan, other->chan, app_data)) {
+		return 0;
+	}
+
+	/* Now for the fun part... park it! */
+	if (ast_bridge_move(bridge, parking_bridge, other->chan)) {
+		ast_log(LOG_ERROR, "Failed to move %s into the parking bridge for %s\n",
+			ast_channel_name(other->chan), lot->name);
+		return 0;
+	}
+
+	/* The parker parked the call successfully, so it leaves the bridge now. */
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+
+	return 0;
+}
+
+static int feature_park(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	return park_feature_helper(bridge, bridge_channel, NULL);
+}
+
+static void parking_duration_cb_destroyer(void *hook_pvt)
+{
+	struct parked_user *user = hook_pvt;
+	ao2_ref(user, -1);
+}
+
+/*! \internal
+ * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
+ *
+ * \param bridge Which bridge the channel was parked in
+ * \param bridge_channel bridge channel this interval hook is being executed on
+ * \param hook_pvt A pointer to the parked_user struct associated with the channel is stuffed in here
+ */
+static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct parked_user *user = hook_pvt;
+	struct ast_channel *chan = user->chan;
+	char *peername;
+	char *dash;
+	char parking_space[AST_MAX_EXTENSION];
+
+	/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
+	   to deal with this, lock the parked user, check and set resolution. */
+	ao2_lock(user);
+	if (user->resolution != PARK_UNSET) {
+		/* Abandon timeout since something else has resolved the parked user before we got to it. */
+		ao2_unlock(user);
+		return -1;
+	}
+
+	user->resolution = PARK_TIMEOUT;
+	ao2_unlock(user);
+
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+
+	/* Set parking timeout channel variables */
+	snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
+	pbx_builtin_setvar_helper(chan, "PARKING_SPACE", parking_space);
+	pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
+	pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot_state->name);
+
+	peername = ast_strdupa(user->parker->name);
+	dash = strrchr(peername, '-');
+	if (dash) {
+		*dash = '\0';
+	}
+
+	pbx_builtin_setvar_helper(chan, "PARKER", peername);
+
+	/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
+	if (!ast_strlen_zero(user->comeback)) {
+		ast_async_parseable_goto(chan, user->comeback);
+	} else {
+		comeback_goto(user, user->lot_state);
+	}
+
+	return -1;
+}
+
+void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user)
+{
+	unsigned int time_limit;
+
+	time_limit = user->time_limit * 1000;
+
+	if (!time_limit) {
+		/* There is no duration limit that we need to apply. */
+		return;
+	}
+
+	/* The interval hook is going to need a reference to the parked_user */
+	ao2_ref(user, +1);
+
+	if (ast_bridge_interval_hook(features, time_limit,
+		parking_duration_callback, user, parking_duration_cb_destroyer, 1)) {
+		ast_log(LOG_ERROR, "Failed to apply duration limits to the parking call.\n");
+	}
+}
+
+void unload_parking_bridge_features(void)
+{
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_PARKCALL);
+	ast_uninstall_park_blind_xfer_func();
+}
+
+int load_parking_bridge_features(void)
+{
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park, NULL);
+	ast_install_park_blind_xfer_func(park_feature_helper);
+	return 0;
+}

Propchange: team/jrose/bridge_projects/res/parking/parking_bridge_features.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/jrose/bridge_projects/res/parking/parking_bridge_features.c
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Rev URL"

Propchange: team/jrose/bridge_projects/res/parking/parking_bridge_features.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/jrose/bridge_projects/res/parking/parking_controller.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/res/parking/parking_controller.c?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/res/parking/parking_controller.c (original)
+++ team/jrose/bridge_projects/res/parking/parking_controller.c Fri Apr  5 11:19:10 2013
@@ -70,61 +70,6 @@
 	struct parked_user *user;
 };
 
-/*! \internal
- * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
- *
- * \param bridge Which bridge the channel was parked in
- * \param bridge_channel bridge channel this interval hook is being executed on
- * \param hook_pvt A pointer to the parked_user struct associated with the channel is stuffed in here
- */
-static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	struct parked_user *user = hook_pvt;
-	struct ast_channel *chan = user->chan;
-	char *peername;
-	char *dash;
-	char parking_space[AST_MAX_EXTENSION];
-
-	/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
-	   to deal with this, lock the parked user, check and set resolution. */
-	ao2_lock(user);
-	if (user->resolution != PARK_UNSET) {
-		/* Abandon timeout since something else has resolved the parked user before we got to it. */
-		ao2_unlock(user);
-		return -1;
-	}
-
-	user->resolution = PARK_TIMEOUT;
-	ao2_unlock(user);
-
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-
-	/* Set parking timeout channel variables */
-	snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
-	pbx_builtin_setvar_helper(chan, "PARKING_SPACE", parking_space);
-	pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
-	pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot_state->name);
-
-	peername = ast_strdupa(user->parker->name);
-	dash = strrchr(peername, '-');
-	if (dash) {
-		*dash = '\0';
-	}
-
-	pbx_builtin_setvar_helper(chan, "PARKER", peername);
-
-	/* Set the resolution for the user to timeout */
-	publish_parked_call(user, PARKED_CALL_TIMEOUT);
-
-	return -1;
-}
-
-static void parking_duration_cb_destroyer(void *hook_pvt)
-{
-	struct parked_user *user = hook_pvt;
-	ao2_ref(user, -1);
-}
-
 int unpark_parked_user(struct parked_user *pu)
 {
 	if (pu->lot_state) {
@@ -135,30 +80,6 @@
 	}
 
 	return -1;
-}
-
-void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user, int override_time)
-{
-	unsigned int time_limit;
-
-	if (override_time >= 0) {
-		time_limit = override_time * 1000;
-	} else {
-		time_limit = user->lot_state ? user->lot_state->parkingtime * 1000 : 0;
-	}
-
-	if (!time_limit) {
-		/* There is no duration limit that we need to apply. */
-		return;
-	}
-
-	/* The interval hook is going to need a reference to the parked_user */
-	ao2_ref(user, +1);
-
-	if (ast_bridge_interval_hook(features, time_limit,
-		parking_duration_callback, user, parking_duration_cb_destroyer, 1)) {
-		ast_log(LOG_ERROR, "Failed to apply duration limits to the parking call.\n");
-	}
 }
 
 int parking_lot_state_get_space(struct parking_lot_state *lot, int target_override)
@@ -253,7 +174,7 @@
 
 	ao2_lock(user);
 	if (user->resolution != PARK_UNSET) {
-		/* Abandon  since something else has resolved the parked user before we got to it. */
+		/* Abandon. Something else has resolved the parked user before we got to it. */
 		ao2_unlock(user);
 		return NULL;
 	}
@@ -262,8 +183,6 @@
 	user->resolution = PARK_ANSWERED;
 	ao2_unlock(user);
 
-	publish_parked_call(user, PARKED_CALL_UNPARKED);
-
 	parking_lot_state_remove_if_unused(user->lot_state);
 	user->lot_state = NULL;
 
@@ -275,7 +194,6 @@
 static void destroy_parked_user(void *obj)
 {
 	struct parked_user *pu = obj;
-	ast_log(LOG_NOTICE, "Destroy parked user\n");
 	ao2_cleanup(pu->parker);
 }
 
@@ -295,17 +213,6 @@
 	}
 }
 
-/*! \internal
- * \brief store context, extension and priority
- * \param chan, context, extension, priority
- */
-static void set_c_e_p(struct ast_channel *chan, const char *context, const char *extension, int priority)
-{
-	ast_channel_context_set(chan, context);
-	ast_channel_exten_set(chan, extension);
-	ast_channel_priority_set(chan, priority);
-}
-
 int comeback_goto(struct parked_user *pu, struct parking_lot_state *lot_state)
 {
 	struct ast_channel *chan = pu->chan;
@@ -316,7 +223,7 @@
 
 	if (lot_state->comebacktoorigin) {
 		if (ast_exists_extension(chan, PARK_DIAL_CONTEXT, peername, 1, NULL)) {
-			set_c_e_p(chan, PARK_DIAL_CONTEXT, peername, 1);
+			ast_async_goto(chan, PARK_DIAL_CONTEXT, peername, 1);
 		} else {
 			ast_log(LOG_ERROR, "Can not start %s at %s,%s,1 because extension does not exist. Terminating call.\n",
 				ast_channel_name(chan), PARK_DIAL_CONTEXT, peername);
@@ -325,21 +232,21 @@
 	}
 
 	if (ast_exists_extension(chan, lot_state->comebackcontext, peername, 1, NULL)) {
-		set_c_e_p(chan, lot_state->comebackcontext, peername, 1);
+		ast_async_goto(chan, lot_state->comebackcontext, peername, 1);
 		return 0;
 	}
 
 	if (ast_exists_extension(chan, lot_state->comebackcontext, "s", 1, NULL)) {
 		ast_verb(2, "Could not start %s at %s,%s,1. Using 's@%s' instead.\n", ast_channel_name(chan),
 			lot_state->comebackcontext, peername, lot_state->comebackcontext);
-		set_c_e_p(chan, lot_state->comebackcontext, "s", 1);
+		ast_async_goto(chan, lot_state->comebackcontext, "s", 1);
 		return 0;
 	}
 
 	ast_verb(2, "Can not start %s at %s,%s,1 and exten 's@%s' does not exist. Using 's at default'\n",
 		ast_channel_name(chan),
 		lot_state->comebackcontext, peername, lot_state->comebackcontext);
-	set_c_e_p(chan, "default", "s", 1);
+	ast_async_goto(chan, "default", "s", 1);
 
 	return 0;
 }
@@ -398,14 +305,10 @@
 	new_parked_user->time_limit = (time_limit >= 0) ? time_limit : lot_state->parkingtime;
 	new_parked_user->parker = ast_channel_snapshot_create(parker);
 	if (!new_parked_user->parker) {
-		ast_log(LOG_ERROR, "Allocation error\n");
 		ao2_ref(new_parked_user, -1);
 		ao2_unlock(lot_state);
 		return NULL;
 	}
-
-	/* Generate ParkedCall Stasis Message */
-	publish_parked_call(new_parked_user, PARKED_CALL);
 
 	/* Insert into the parking lot's parked user list. We can unlock the lot now. */
 	ao2_link(lot_state->parked_user_list, new_parked_user);

Modified: team/jrose/bridge_projects/res/parking/res_parking.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/res/parking/res_parking.h?view=diff&rev=384814&r1=384813&r2=384814
==============================================================================
--- team/jrose/bridge_projects/res/parking/res_parking.h (original)
+++ team/jrose/bridge_projects/res/parking/res_parking.h Fri Apr  5 11:19:10 2013
@@ -118,8 +118,7 @@
 	struct ast_channel_snapshot *parker;    /*!< Snapshot of the channel that parked the call at the time of parking */
 	struct timeval start;                   /*!< When the call was parked */
 	int parking_space;                      /*!< Which parking space is used */
-	char context[AST_MAX_CONTEXT];          /*!< Where to go on parking timeout (context) */
-	char exten[AST_MAX_CONTEXT];            /*!< Where to go on parking timeout (extension) */
+	char comeback[AST_MAX_CONTEXT];         /*!< Where to go on parking timeout */
 	unsigned int time_limit;                /*!< How long this specific channel may remain in the parking lot before timing out */
 	struct parking_lot_state *lot_state;    /*!< 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 */
@@ -223,9 +222,8 @@
  *
  * \param features The ast_bridge_features we are establishing the interval hook on
  * \param user The parked_user receiving the timeout duration limits
- * \param override_time If >= 0, use this instead of the user's timeout inherited from its lot
- */
-void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user, int override_time);
+ */
+void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user);
 
 /*!
  * \since 12
@@ -308,7 +306,7 @@
 
 /*!
  * \since 12
- * \brief Pull a parked user out of its parking lot.
+ * \brief Pull a parked user out of its parking lot. Use this when you don't want to use the parked user afterwards.
  * \param user The parked user being pulled.
  *
  * \retval 0 on success
@@ -327,6 +325,30 @@
 
 /*!
  * \since 12
+ * \brief This sets up a parked user for the channel being parked as well as prepares the parking bridge if necessary.
+ *        it also handles the setup of all parking options available to the parking application.
+ *
+ * \param lot pointer to a parking lot state struct pointer. This will be set to the necessary parking lot
+ *            based on either app_data or channel variables.
+ * \param parking_bridge pointer to an ast_bridge struct pointer. Set to the parking lot's parking bridge.

[... 105 lines stripped ...]



More information about the asterisk-commits mailing list