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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 6 17:33:15 CST 2013


Author: jrose
Date: Wed Mar  6 17:33:11 2013
New Revision: 382551

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382551
Log:
Bridging Roles and consumption by the holding bridge

Adds a system of setting up roles on a channel which can then be consumed by
bridging technologies to control calls on the bridge. Additional Roles
caller and callee still need to go in before the issue is complete though.

(issue ASTERISK-21054)
Review: https://reviewboard.asterisk.org/r/2360/

Added:
    team/jrose/bridge_projects/apps/app_bridgewait.c   (with props)
    team/jrose/bridge_projects/bridges/bridge_holding.c   (with props)
    team/jrose/bridge_projects/include/asterisk/bridging_roles.h   (with props)
    team/jrose/bridge_projects/main/bridging_roles.c   (with props)
Modified:
    team/jrose/bridge_projects/include/asterisk/bridging.h
    team/jrose/bridge_projects/main/bridging.c

Added: team/jrose/bridge_projects/apps/app_bridgewait.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/apps/app_bridgewait.c?view=auto&rev=382551
==============================================================================
--- team/jrose/bridge_projects/apps/app_bridgewait.c (added)
+++ team/jrose/bridge_projects/apps/app_bridgewait.c Wed Mar  6 17:33:11 2013
@@ -1,0 +1,233 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Mark Spencer <markster at digium.com>
+ *
+ * Author: Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Application to place the channel into a holding Bridge
+ *
+ * \author Jonathan Rose <jrose at digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/features.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/bridging.h"
+#include "asterisk/musiconhold.h"
+
+/*** DOCUMENTATION
+	<application name="BridgeWait" language="en_US">
+		<synopsis>
+			Put a call into the holding bridge.
+		</synopsis>
+		<syntax>
+			<parameter name="options">
+				<optionlist>
+					<option name="A">
+						<para>The channel will join the holding bridge as an
+						announcer</para>
+					</option>
+					<option name="m">
+						<argument name="class" required="false" />
+						<para>Play music on hold to the entering channel while it is
+						on hold. If the <emphasis>class</emphasis> is included, then
+						that class of music on hold will take priority over the
+						channel default.</para>
+					</option>
+					<option name="r">
+						<para>Play a ringing tone to the entering channel while it is
+						on hold.</para>
+					</option>
+					<option name="S">
+						<argument name="duration" required="true" />
+						<para>Automatically end the hold and return to the PBX after
+						<emphasis>duration</emphasis> seconds.</para>
+					</option>
+				</optionlist>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This application places the incoming channel into a holding bridge.
+			The channel will then wait in the holding bridge until some
+			event occurs which removes it from the holding bridge.</para>
+		</description>
+	</application>
+ ***/
+
+static char *app = "BridgeWait";
+static struct ast_bridge *holding_bridge;
+
+AST_MUTEX_DEFINE_STATIC(bridgewait_lock);
+
+enum bridgewait_flags {
+	MUXFLAG_PLAYMOH = (1 << 0),
+	MUXFLAG_RINGING = (1 << 1),
+	MUXFLAG_TIMEOUT = (1 << 2),
+	MUXFLAG_ANNOUNCER = (1 << 3),
+};
+
+enum bridgewait_args {
+	OPT_ARG_MOHCLASS,
+	OPT_ARG_TIMEOUT,
+	OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */
+};
+
+AST_APP_OPTIONS(bridgewait_opts, {
+	AST_APP_OPTION('A', MUXFLAG_ANNOUNCER),
+	AST_APP_OPTION('r', MUXFLAG_RINGING),
+	AST_APP_OPTION_ARG('m', MUXFLAG_PLAYMOH, OPT_ARG_MOHCLASS),
+	AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
+});
+
+static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
+{
+	struct ast_bridge_features_limits hold_limits;
+
+	if (ast_strlen_zero(duration_arg)) {
+		ast_log(LOG_ERROR, "No duration value provided for the timeout ('S') option.\n");
+		return -1;
+	}
+
+	if (ast_bridge_features_limits_construct(&hold_limits)) {
+		ast_log(LOG_ERROR, "Could not construct duration limits. Bridge canceled.\n");
+		return -1;
+	}
+
+	if (sscanf(duration_arg, "%u", &(hold_limits.duration)) != 1 || hold_limits.duration == 0) {
+		ast_log(LOG_ERROR, "Duration value provided for the timeout ('S') option must be greater than 0\n");
+		ast_bridge_features_limits_destroy(&hold_limits);
+		return -1;
+	}
+
+	/* Limits struct holds time as milliseconds, so muliply 1000x */
+	hold_limits.duration *= 1000;
+	ast_bridge_features_set_limits(features, &hold_limits);
+	ast_bridge_features_limits_destroy(&hold_limits);
+
+	return 0;
+}
+
+static void apply_option_moh(struct ast_channel *chan, char *class_arg)
+{
+	ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
+	ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
+}
+
+static void apply_option_ringing(struct ast_channel *chan)
+{
+	ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
+}
+
+static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features)
+{
+	if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
+		if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
+			return -1;
+		}
+	}
+
+	if (ast_test_flag(flags, MUXFLAG_ANNOUNCER)) {
+		/* Announcer specific stuff */
+		ast_channel_add_bridge_role(chan, "announcer");
+	} else {
+		/* Non Announcer specific stuff */
+		ast_channel_add_bridge_role(chan, "holding_participant");
+
+		if (ast_test_flag(flags, MUXFLAG_PLAYMOH)) {
+			apply_option_moh(chan, opts[OPT_ARG_MOHCLASS]);
+		} else if (ast_test_flag(flags, MUXFLAG_RINGING)) {
+			apply_option_ringing(chan);
+		}
+	}
+
+	return 0;
+}
+
+static int bridgewait_exec(struct ast_channel *chan, const char *data)
+{
+	struct ast_bridge_features chan_features;
+	struct ast_flags flags = { 0 };
+	char *parse;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(options);
+		AST_APP_ARG(other);		/* Any remaining unused arguments */
+	);
+
+	ast_mutex_lock(&bridgewait_lock);
+	if (!holding_bridge) {
+		holding_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_HOLDING, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM);
+	}
+	ast_mutex_unlock(&bridgewait_lock);
+
+	if (!holding_bridge) {
+		ast_log(LOG_ERROR, "Could not create holding bridge for '%s'.\n", ast_channel_name(chan));
+		return -1;
+	}
+
+	parse = ast_strdupa(data);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	ast_bridge_features_init(&chan_features);
+
+	if (args.options) {
+		char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+		ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
+		if (process_options(chan, &flags, opts, &chan_features)) {
+			ast_bridge_features_cleanup(&chan_features);
+			return -1;
+		}
+	}
+
+	ast_bridge_join(holding_bridge, chan, NULL, &chan_features, NULL, 0);
+
+	ast_bridge_features_cleanup(&chan_features);
+	return ast_check_hangup_locked(chan) ? -1 : 0;
+}
+
+static int unload_module(void)
+{
+	ao2_cleanup(holding_bridge);
+	holding_bridge = NULL;
+
+	return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+	return ast_register_application_xml(app, bridgewait_exec);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");

Propchange: team/jrose/bridge_projects/apps/app_bridgewait.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/jrose/bridge_projects/apps/app_bridgewait.c
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Rev URL"

Propchange: team/jrose/bridge_projects/apps/app_bridgewait.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/jrose/bridge_projects/bridges/bridge_holding.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/bridges/bridge_holding.c?view=auto&rev=382551
==============================================================================
--- team/jrose/bridge_projects/bridges/bridge_holding.c (added)
+++ team/jrose/bridge_projects/bridges/bridge_holding.c Wed Mar  6 17:33:11 2013
@@ -1,0 +1,315 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Bridging technology for storing channels in a bridge for
+ *        the purpose of holding, parking, queues, and other such
+ *        states where a channel may need to be in a bridge but not
+ *        actually communicating with anything.
+ *
+ * \author Jonathan Rose <jrose at digium.com>
+ *
+ * \ingroup bridges
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+#include "asterisk/musiconhold.h"
+
+enum role_flags {
+	HOLDING_ROLE_PARTICIPANT = (1 << 0),
+	HOLDING_ROLE_ANNOUNCER = (1 << 1),
+};
+
+enum idle_modes {
+	IDLE_MODE_NONE = 0,
+	IDLE_MODE_MOH,
+	IDLE_MODE_RINGING,
+};
+
+/*! \brief Structure which contains per-channel role information */
+struct holding_channel {
+	struct ast_flags holding_roles;
+	enum idle_modes idle_mode;
+};
+
+static void participant_stop_hold_audio(struct ast_bridge_channel *bridge_channel)
+{
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+	if (!hc) {
+		return;
+	}
+
+	switch (hc->idle_mode) {
+	case IDLE_MODE_MOH:
+		ast_moh_stop(bridge_channel->chan);
+		break;
+	case IDLE_MODE_RINGING:
+		ast_indicate(bridge_channel->chan, -1);
+		break;
+	case IDLE_MODE_NONE:
+		break;
+	}
+}
+
+static void participant_reaction_announcer_join(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_channel *chan;
+	chan = bridge_channel->chan;
+	participant_stop_hold_audio(bridge_channel);
+	if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+		ast_log(LOG_WARNING, "Could not make participant %s compatible.\n", ast_channel_name(chan));
+	}
+}
+
+/* This should only be called on verified holding_participants. */
+static void participant_start_hold_audio(struct ast_bridge_channel *bridge_channel)
+{
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+	const char *moh_class;
+
+	if (!hc) {
+		return;
+	}
+
+	switch(hc->idle_mode) {
+	case IDLE_MODE_MOH:
+		moh_class = ast_bridge_channel_get_role_option(bridge_channel, "holding_participant", "moh_class");
+		ast_moh_start(bridge_channel->chan, ast_strlen_zero(moh_class) ? NULL : moh_class, NULL);
+		break;
+	case IDLE_MODE_RINGING:
+		ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
+		break;
+	case IDLE_MODE_NONE:
+		break;
+	}
+}
+
+static void handle_participant_join(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *announcer_channel)
+{
+	struct ast_channel *us = bridge_channel->chan;
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+	const char *idle_mode = ast_bridge_channel_get_role_option(bridge_channel, "holding_participant", "idle_mode");
+
+
+	if (!hc) {
+		return;
+	}
+
+	if (ast_strlen_zero(idle_mode)) {
+		hc->idle_mode = IDLE_MODE_NONE;
+	} else if (!strcmp(idle_mode, "musiconhold")) {
+		hc->idle_mode = IDLE_MODE_MOH;
+	} else if (!strcmp(idle_mode, "ringing")) {
+		hc->idle_mode = IDLE_MODE_RINGING;
+	} else {
+		ast_debug(2, "channel %s idle mode '%s' doesn't match any expected idle mode\n", ast_channel_name(us), idle_mode);
+	}
+
+	/* If the announcer channel isn't present, we need to set up ringing, music on hold, or whatever. */
+	if (!announcer_channel) {
+		participant_start_hold_audio(bridge_channel);
+		return;
+	}
+
+	/* If it is present though, we need to establish compatability. */
+	if (ast_set_write_format_by_id(us, AST_FORMAT_SLINEAR)) {
+		ast_log(LOG_WARNING, "Could not make participant %s compatible.\n", ast_channel_name(us));
+	}
+}
+
+static int holding_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_channel *other_channel;
+	struct ast_bridge_channel *announcer_channel;
+	struct holding_channel *hc;
+	struct ast_channel *us = bridge_channel->chan; /* The joining channel */
+
+	if (!(hc = ast_calloc(1, sizeof(*hc)))) {
+		return -1;
+	}
+
+	bridge_channel->bridge_pvt = hc;
+
+	/* The bridge pvt holds the announcer channel if we have one. */
+	announcer_channel = bridge->bridge_pvt;
+
+	if (ast_bridge_channel_has_role(bridge_channel, "announcer")) {
+		/* If another announcer already exists, scrap the holding channel struct so we know to ignore it in the future */
+		if (announcer_channel) {
+			bridge_channel->bridge_pvt = NULL;
+			ast_free(hc);
+			ast_log(LOG_WARNING, "A second announcer channel %s attempted to enter a holding bridge.\n",
+				ast_channel_name(announcer_channel->chan));
+			return -1;
+		}
+
+		bridge->bridge_pvt = bridge_channel;
+		ast_set_flag(&hc->holding_roles, HOLDING_ROLE_ANNOUNCER);
+
+		/* The announcer should always be made compatible with signed linear */
+		if (ast_set_read_format_by_id(us, AST_FORMAT_SLINEAR)) {
+			ast_log(LOG_ERROR, "Could not make announcer %s compatible.\n", ast_channel_name(us));
+		}
+
+		/* Make everyone compatible. While we are at it we should stop music on hold and ringing. */
+		AST_LIST_TRAVERSE(&bridge->channels, other_channel, entry) {
+			/* Skip the reaction if we are the channel in question */
+			if (bridge_channel == other_channel) {
+				continue;
+			}
+			participant_reaction_announcer_join(other_channel);
+		}
+
+		return 0;
+	}
+
+	/* If the entering channel isn't an announcer then we need to setup it's properties and put it in its holding state if necessary */
+	ast_set_flag(&hc->holding_roles, HOLDING_ROLE_PARTICIPANT);
+	handle_participant_join(bridge_channel, announcer_channel);
+	return 0;
+}
+
+static void participant_reaction_announcer_leave(struct ast_bridge_channel *bridge_channel)
+{
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+
+	if (!hc) {
+		/* We are dealing with a channel that failed to join properly. Skip it. */
+		return;
+	}
+
+	ast_bridge_channel_restore_formats(bridge_channel);
+	if (ast_test_flag(&hc->holding_roles, HOLDING_ROLE_PARTICIPANT)) {
+		participant_start_hold_audio(bridge_channel);
+	}
+}
+
+static void holding_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_channel *other_channel;
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+
+	if (!hc) {
+		return;
+	}
+
+	if (!ast_test_flag(&hc->holding_roles, HOLDING_ROLE_ANNOUNCER)) {
+		/* It's not an announcer so nothing needs to react to its departure. Just free the bridge_pvt. */
+		if (!bridge->bridge_pvt) {
+			/* Since no announcer is in the channel, we may be playing MOH/ringing. Stop that. */
+			participant_stop_hold_audio(bridge_channel);
+		}
+		ast_free(hc);
+		bridge_channel->bridge_pvt = NULL;
+		return;
+	}
+
+	/* When the announcer leaves, the other channels should reset their formats and go back to moh/ringing */
+	AST_LIST_TRAVERSE(&bridge->channels, other_channel, entry) {
+		participant_reaction_announcer_leave(other_channel);
+	}
+
+	/* Since the announcer is leaving, we should clear the bridge_pvt pointing to it */
+	bridge->bridge_pvt = NULL;
+
+	ast_free(hc);
+	bridge_channel->bridge_pvt = NULL;
+}
+
+static enum ast_bridge_write_result holding_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	struct ast_bridge_channel *other;
+	struct holding_channel *hc = bridge_channel->bridge_pvt;
+
+	/* If there is no bridge_pvt, then the channel failed to allocate one when it joined and is borked. Don't listen to him. */
+	if (!hc) {
+		return AST_BRIDGE_WRITE_FAILED;
+	}
+
+	/* If we aren't an announcer, we never have any business writing anything. */
+	if (!ast_test_flag(&hc->holding_roles, HOLDING_ROLE_ANNOUNCER)) {
+		return AST_BRIDGE_WRITE_FAILED;
+	}
+
+	/* Ok, so we are the announcer and there are one or more people available to receive our writes. Let's do it. */
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		struct hc *other_hc = other->bridge_pvt;
+		/* Skip writes on channels without the bridge pvt struct. */
+		if (!other_hc) {
+			continue;
+		}
+
+		if ((bridge_channel != other) && (other->state == AST_BRIDGE_CHANNEL_STATE_WAIT)) {
+			if (!other->suspended) {
+				ast_write(other->chan, frame);
+			}
+		}
+	}
+
+	return AST_BRIDGE_WRITE_SUCCESS;
+}
+
+static struct ast_bridge_technology holding_bridge = {
+	.name = "holding_bridge",
+	.capabilities = AST_BRIDGE_CAPABILITY_HOLDING,
+	.preference = AST_BRIDGE_PREFERENCE_MEDIUM,
+	.write = holding_bridge_write,
+	.join = holding_bridge_join,
+	.thread_loop = ast_bridge_thread_generic,
+	.leave = holding_bridge_leave,
+};
+
+static int unload_module(void)
+{
+	ast_format_cap_destroy(holding_bridge.format_capabilities);
+	return ast_bridge_technology_unregister(&holding_bridge);
+}
+
+static int load_module(void)
+{
+	if (!(holding_bridge.format_capabilities = ast_format_cap_alloc())) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+	ast_format_cap_add_all_by_type(holding_bridge.format_capabilities, AST_FORMAT_TYPE_AUDIO);
+	ast_format_cap_add_all_by_type(holding_bridge.format_capabilities, AST_FORMAT_TYPE_VIDEO);
+	ast_format_cap_add_all_by_type(holding_bridge.format_capabilities, AST_FORMAT_TYPE_TEXT);
+
+	return ast_bridge_technology_register(&holding_bridge);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Holding bridge module");
+

Propchange: team/jrose/bridge_projects/bridges/bridge_holding.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/jrose/bridge_projects/bridges/bridge_holding.c
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Rev URL"

Propchange: team/jrose/bridge_projects/bridges/bridge_holding.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/jrose/bridge_projects/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/include/asterisk/bridging.h?view=diff&rev=382551&r1=382550&r2=382551
==============================================================================
--- team/jrose/bridge_projects/include/asterisk/bridging.h (original)
+++ team/jrose/bridge_projects/include/asterisk/bridging.h Wed Mar  6 17:33:11 2013
@@ -63,10 +63,13 @@
 #endif
 
 #include "asterisk/bridging_features.h"
+#include "asterisk/bridging_roles.h"
 #include "asterisk/dsp.h"
 
 /*! \brief Capabilities for a bridge technology */
 enum ast_bridge_capability {
+	/*! Bridge technology can service calls on hold */
+	AST_BRIDGE_CAPABILITY_HOLDING = (1 << 0),
 	/*! Bridge should natively bridge two channels if possible */
 	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 1),
 	/*! Bridge is only capable of mixing 2 channels */
@@ -157,8 +160,14 @@
 	/*! Technology optimization parameters used by bridging technologies capable of
 	 *  optimizing based upon talk detection. */
 	struct ast_bridge_tech_optimizations tech_args;
+	/*! Copy of read format used by chan before join */
+	struct ast_format read_format;
+	/*! Copy of write format used by chan before join */
+	struct ast_format write_format;
 	/*! Call ID associated with bridge channel */
 	struct ast_callid *callid;
+	/*! A clone of the roles living on chan when the bridge channel joins the bridge. This may require some opacification */
+	struct bridge_roles_datastore *bridge_roles;
 	/*! Linked list information */
 	AST_LIST_ENTRY(ast_bridge_channel) entry;
 	/*! Queue of actions to perform on the channel. */
@@ -634,6 +643,14 @@
 int ast_bridge_channel_queue_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action);
 
 /*!
+ * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to restore
+ */
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
+
+/*!
  * \brief Adjust the internal mixing sample rate of a bridge
  * used during multimix mode.
  *

Added: team/jrose/bridge_projects/include/asterisk/bridging_roles.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/include/asterisk/bridging_roles.h?view=auto&rev=382551
==============================================================================
--- team/jrose/bridge_projects/include/asterisk/bridging_roles.h (added)
+++ team/jrose/bridge_projects/include/asterisk/bridging_roles.h Wed Mar  6 17:33:11 2013
@@ -1,0 +1,135 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Channel Bridging Roles API
+ * \author Jonathan Rose <jrose at digium.com>
+ */
+
+#ifndef _ASTERISK_BRIDGING_ROLES_H
+#define _ASTERISK_BRIDGING_ROLES_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/linkedlists.h"
+
+#define AST_ROLE_LEN 32
+
+/*!
+ * \brief Adds a bridge role to a channel
+ *
+ * \param chan Acquirer of the requested role
+ * \param role_name Name of the role being attached
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name);
+
+/*!
+ * \brief Removes a bridge role from a channel
+ *
+ * \param chan Channel the role is being removed from
+ * \param role_name Name of the role being removed
+ */
+void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name);
+
+/*!
+ * \brief Set a role option on a channel
+ * \param channel Channel receiving the role option
+ * \param role_name Role the role option is applied to
+ * \param option Name of the option
+ * \param value Value of the option
+ *
+ * \param 0 on success
+ * \retval -1 on failure
+ */
+int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value);
+
+/*!
+ * \brief Check to see if a bridge channel inherited a specific role from its channel
+ *
+ * \param bridge_channel The bridge channel being checked
+ * \param role_name Name of the role being checked
+ *
+ * \retval 0 The bridge channel does not have the requested role
+ * \retval 1 The bridge channel does have the requested role
+ *
+ * \note Before a bridge_channel can effectively check roles against a bridge, ast_bridge_roles_bridge_channel_establish_roles
+ *       should be called on the bridge_channel so that roles and their respective role options can be copied from the channel
+ *       datastore into the bridge_channel roles list. Otherwise this function will just return 0 because the list will be NULL.
+ */
+int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name);
+
+/*!
+ * \brief Retrieve the value of a requested role option from a bridge channel
+ *
+ * \param bridge_channel The bridge channel we are retrieving the option from
+ * \param role_name Name of the role the option will be retrieved from
+ * \param option Name of the option we are retrieving the value of
+ *
+ * \retval NULL If either the role does not exist on the bridge_channel or the role does exist but the option has not been set
+ * \retval The value of the option
+ *
+ * \note See ast_bridge_roles_channel_set_role_option note about the need to call ast_bridge_roles_bridge_channel_establish_roles.
+ *
+ * \note The returned character pointer is only valid as long as the bridge_channel is guaranteed to be alive and hasn't had
+ *       ast_bridge_roles_bridge_channel_clear_roles called against it (as this will free all roles and role options in the bridge
+ *       channel). If you need this value after one of these destruction events occurs, you must make a local copy while it is
+ *       still valid.
+ */
+const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option);
+
+/*!
+ * \brief Clone the roles from a bridge_channel's attached ast_channel onto the bridge_channel's role list
+ *
+ * \param bridge_channel The bridge channel that we are preparing
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \details
+ * This function should always be called when the bridge_channel binds to an ast_channel at some point before the bridge_channel
+ * joins or is imparted onto a bridge. Failure to do so will result in an empty role list. While the list remains established,
+ * changes to roles on the ast_channel will not propogate to the bridge channel and roles can not be re-established on the bridge
+ * channel without first clearing the roles with ast_bridge_roles_bridge_channel_clear_roles.
+ */
+int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Clear all roles from a bridge_channel's role list
+ *
+ * \param bridge_channel the bridge channel that we are scrubbing
+ *
+ * \details
+ * If roles are already established on a bridge channel, ast_bridge_roles_bridge_channel_establish_roles will fail unconditionally
+ * without changing any roles. In order to update a bridge channel's roles, they must first be cleared from the bridge channel using
+ * this function.
+ *
+ * \note
+ * ast_bridge_roles_bridge_channel_clear_roles also serves as the destructor for the role list of a bridge channel.
+ */
+void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BRIDGING_ROLES_H */

Propchange: team/jrose/bridge_projects/include/asterisk/bridging_roles.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/jrose/bridge_projects/include/asterisk/bridging_roles.h
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Rev URL"

Propchange: team/jrose/bridge_projects/include/asterisk/bridging_roles.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=382551&r1=382550&r2=382551
==============================================================================
--- team/jrose/bridge_projects/main/bridging.c (original)
+++ team/jrose/bridge_projects/main/bridging.c Wed Mar  6 17:33:11 2013
@@ -41,6 +41,7 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_technology.h"
+#include "asterisk/bridging_roles.h"
 #include "asterisk/app.h"
 #include "asterisk/file.h"
 #include "asterisk/module.h"
@@ -209,6 +210,31 @@
 	ast_bridge_channel_poke(bridge_channel);
 	ao2_unlock(bridge_channel);
 	return 0;
+}
+
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
+{
+	/* Restore original formats of the channel as they came in */
+	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning bridge channel %p(%s) to read format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->read_format));
+		if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) {
+			ast_debug(1, "Bridge failed to return bridge channel %p(%s) to read format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->read_format));
+		}
+	}
+	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning bridge channel %p(%s) to write format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->write_format));
+		if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) {
+			ast_debug(1, "Bridge failed to return bridge channel %p(%s) to write format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->write_format));
+		}
+	}
 }
 
 void ast_bridge_poke(struct ast_bridge *bridge)
@@ -1624,10 +1650,9 @@
 static void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_frame *action;
-	struct ast_format formats[2];
-
-	ast_format_copy(&formats[0], ast_channel_readformat(bridge_channel->chan));
-	ast_format_copy(&formats[1], ast_channel_writeformat(bridge_channel->chan));
+
+	ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan));
+	ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan));
 
 	ast_debug(1, "Joining bridge channel %p(%s) to bridge %p\n",
 		bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->bridge);
@@ -1742,32 +1767,14 @@
 	}
 	ast_channel_internal_bridge_set(bridge_channel->chan, NULL);
 
-	/* Restore original formats of the channel as they came in */
-	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &formats[0]) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning bridge channel %p(%s) to read format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&formats[0]));
-		if (ast_set_read_format(bridge_channel->chan, &formats[0])) {
-			ast_debug(1, "Bridge failed to return bridge channel %p(%s) to read format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&formats[0]));
-		}
-	}
-	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &formats[1]) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning bridge channel %p(%s) to write format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&formats[1]));
-		if (ast_set_write_format(bridge_channel->chan, &formats[1])) {
-			ast_debug(1, "Bridge failed to return bridge channel %p(%s) to write format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&formats[1]));
-		}
-	}
+	ast_bridge_channel_restore_formats(bridge_channel);
 }
 
 static void bridge_channel_destroy(void *obj)
 {
 	struct ast_bridge_channel *bridge_channel = obj;
+
+	ast_bridge_channel_clear_roles(bridge_channel);
 
 	if (bridge_channel->callid) {
 		bridge_channel->callid = ast_callid_unref(bridge_channel->callid);
@@ -1777,6 +1784,7 @@
 		ao2_ref(bridge_channel->bridge, -1);
 		bridge_channel->bridge = NULL;
 	}
+
 	/* Destroy elements of the bridge channel structure and the bridge channel structure itself */
 	ast_cond_destroy(&bridge_channel->cond);
 }
@@ -2106,6 +2114,14 @@
 	bridge_channel->swap = swap;
 	bridge_channel->features = features;
 
+	if (ast_bridge_channel_establish_roles(bridge_channel)) {
+		/* A bridge channel should not be allowed to join if its roles couldn't be copied properly. */
+		state = AST_BRIDGE_CHANNEL_STATE_HANGUP;
+		ast_channel_internal_bridge_channel_set(chan, NULL);
+		ao2_ref(bridge_channel, -1);
+		goto join_exit;
+	}
+
 	bridge_channel_join(bridge_channel);
 	state = bridge_channel->state;
 
@@ -2197,6 +2213,11 @@
 	bridge_channel->depart_wait = independent ? 0 : 1;
 	bridge_channel->callid = ast_read_threadstorage_callid();
 
+	if (ast_bridge_channel_establish_roles(bridge_channel)) {
+		res = -1;
+		goto bridge_impart_cleanup;
+	}
+
 	/* Actually create the thread that will handle the channel */
 	if (independent) {
 		/* Independently imparted channels cannot have a PBX. */
@@ -2211,6 +2232,8 @@
 		res = ast_pthread_create(&bridge_channel->thread, NULL,
 			bridge_channel_depart_thread, bridge_channel);
 	}
+
+bridge_impart_cleanup:
 	if (res) {
 		/* cleanup */
 		ast_channel_internal_bridge_channel_set(chan, NULL);

Added: team/jrose/bridge_projects/main/bridging_roles.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/bridging_roles.c?view=auto&rev=382551
==============================================================================
--- team/jrose/bridge_projects/main/bridging_roles.c (added)
+++ team/jrose/bridge_projects/main/bridging_roles.c Wed Mar  6 17:33:11 2013
@@ -1,0 +1,453 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Channel Bridging Roles API
+ *
+ * \author Jonathan Rose <jrose at digium.com>
+ *
+ * \ingroup bridges
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/datastore.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_roles.h"
+
+struct bridge_role_option {
+	AST_LIST_ENTRY(bridge_role_option) list;
+	char option[AST_ROLE_LEN];
+	char value[1];
+};
+
+struct bridge_role {
+	AST_LIST_ENTRY(bridge_role) list;
+	AST_LIST_HEAD(, bridge_role_option) options;
+	char role[AST_ROLE_LEN];
+};
+
+struct bridge_roles_datastore {
+	AST_LIST_HEAD(, bridge_role) role_list;
+};
+
+/*!
+ * \internal
+ * \brief Destructor function for a bridge role
+ * \since 12.0.0
+ *
+ * \param role bridge_role being destroyed
+ *
+ * \return Nothing
+ */
+static void bridge_role_destroy(struct bridge_role *role)
+{
+	struct bridge_role_option *role_option;
+	while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
+		ast_free(role_option);
+	}
+	ast_free(role);
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for bridge role datastores
+ * \since 12.0.0
+ *
+ * \param data Pointer to the datastore being destroyed
+ *
+ * \return Nothing
+ */
+static void bridge_role_datastore_destroy(void *data)
+{
+	struct bridge_roles_datastore *roles_datastore = data;
+	struct bridge_role *role;
+
+	while ((role = AST_LIST_REMOVE_HEAD(&roles_datastore->role_list, list))) {
+		bridge_role_destroy(role);
+	}
+
+	ast_free(roles_datastore);
+}
+
+static const struct ast_datastore_info bridge_role_info = {
+	.type = "bridge roles",
+	.destroy = bridge_role_datastore_destroy,
+};
+
+/*!
+ * \internal
+ * \brief Setup a bridge role datastore on a channel
+ * \since 12.0.0
+ *
+ * \param chan Chan the datastore is being setup on
+ *
+ * \retval NULL if failed
+ * \retval pointer to the newly created datastore
+ */
+static struct bridge_roles_datastore *setup_bridge_roles_datastore(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore = NULL;
+	struct bridge_roles_datastore *roles_datastore = NULL;
+
+	if (!(datastore = ast_datastore_alloc(&bridge_role_info, NULL))) {
+		return NULL;
+	}
+
+	if (!(roles_datastore = ast_calloc(1, sizeof(*roles_datastore)))) {
+		ast_datastore_free(datastore);
+		return NULL;
+	}
+
+	datastore->data = roles_datastore;
+	ast_channel_datastore_add(chan, datastore);
+	return roles_datastore;
+}
+
+/*!
+ * \internal
+ * \brief Get the bridge_roles_datastore from a channel if it exists. Don't create one if it doesn't.
+ * \since 12.0.0
+ *
+ * \param chan Channel we want the bridge_roles_datastore from
+ *
+ * \retval NULL if we can't find the datastore
+ * \retval pointer to the bridge_roles_datastore
+ */
+static struct bridge_roles_datastore *fetch_bridge_roles_datastore(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore = NULL;
+
+	ast_channel_lock(chan);
+	if (!(datastore = ast_channel_datastore_find(chan, &bridge_role_info, NULL))) {
+		ast_channel_unlock(chan);
+		return NULL;
+	}
+	ast_channel_unlock(chan);
+
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Get the bridge_roles_datastore from a channel if it exists. If not, create one.
+ * \since 12.0.0
+ *
+ * \param chan Channel we want the bridge_roles_datastore from
+ *
+ * \retval NULL If we can't find and can't create the datastore
+ * \retval pointer to the bridge_roles_datastore
+ */
+static struct bridge_roles_datastore *fetch_or_create_bridge_roles_datastore(struct ast_channel *chan)
+{
+	struct bridge_roles_datastore *roles_datastore;
+
+	ast_channel_lock(chan);
+	roles_datastore = fetch_bridge_roles_datastore(chan);
+	if (!roles_datastore) {
+		roles_datastore = setup_bridge_roles_datastore(chan);
+	}
+	ast_channel_unlock(chan);
+
+	return roles_datastore;
+}
+
+/*!
+ * \internal
+ * \brief Obtain a role from a bridge_roles_datastore if the datastore has it
+ * \since 12.0.0
+ *
+ * \param roles_datastore The bridge_roles_datastore we are looking for the role of
+ * \param role_name Name of the role being sought
+ *
+ * \retval NULL if the datastore does not have the requested role
+ * \retval pointer to the requested role
+ */
+static struct bridge_role *get_role_from_datastore(struct bridge_roles_datastore *roles_datastore, const char *role_name)
+{
+	struct bridge_role *role;
+
+	AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
+		if (!strcmp(role->role, role_name)) {
+			return role;
+		}
+	}
+
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Obtain a role from a channel structure if the channel's datastore has it
+ * \since 12.0.0
+ *
+ * \param channel The channel we are checking the role of

[... 255 lines stripped ...]



More information about the asterisk-commits mailing list