[asterisk-commits] mmichelson: branch mmichelson/features_config r389518 - /team/mmichelson/feat...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed May 22 13:40:00 CDT 2013


Author: mmichelson
Date: Wed May 22 13:39:56 2013
New Revision: 389518

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389518
Log:
Use channel datastore to store feature config values per-channel.

Tested from dialplan and can use the FEATURE and FEATUREMAP functions
to read/write feature values.

Next step is to write public functions to get at subsets of the data.


Modified:
    team/mmichelson/features_config/main/features.c

Modified: team/mmichelson/features_config/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/features_config/main/features.c?view=diff&rev=389518&r1=389517&r2=389518
==============================================================================
--- team/mmichelson/features_config/main/features.c (original)
+++ team/mmichelson/features_config/main/features.c Wed May 22 13:39:56 2013
@@ -662,6 +662,399 @@
 	struct ast_flags peer_features;
 };
 
+struct features_global_config {
+	struct ast_features_general_config *general;
+	struct ast_features_xfer_config *xfer;
+	struct ast_features_pickup_config *pickup;
+};
+
+struct features_featuremap {
+	AST_DECLARE_STRING_FIELDS (
+		AST_STRING_FIELD(blindxfer);
+		AST_STRING_FIELD(disconnect);
+		AST_STRING_FIELD(automon);
+		AST_STRING_FIELD(atxfer);
+		AST_STRING_FIELD(parkcall);
+		AST_STRING_FIELD(automixmon);
+	);
+};
+
+struct features_applicationmap {
+	/* XXX STUB */
+};
+
+struct features_config {
+	struct features_global_config *global;
+	struct features_featuremap *featuremap;
+	struct features_applicationmap *applicationmap;
+};
+
+static struct aco_type global_option = {
+	.type = ACO_GLOBAL,
+	.name = "globals",
+	.category_match = ACO_WHITELIST,
+	.category = "^general$",
+	.item_offset = offsetof(struct features_config, global),
+};
+
+static struct aco_type featuremap_option = {
+	.type = ACO_GLOBAL,
+	.name = "featuremap",
+	.category_match = ACO_WHITELIST,
+	.category = "^featuremap$",
+	.item_offset = offsetof(struct features_config, featuremap),
+};
+
+static struct aco_type applicationmap_option = {
+	.type = ACO_GLOBAL,
+	.name = "applicationmap",
+	.category_match = ACO_WHITELIST,
+	.category = "^applicationmap$",
+	.item_offset = offsetof(struct features_config, applicationmap),
+};
+
+struct aco_type *global_options[] = ACO_TYPES(&global_option);
+struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
+struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
+
+struct aco_file features_conf = {
+	.filename = "features.conf",
+	.types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option),
+};
+
+AO2_GLOBAL_OBJ_STATIC(globals);
+
+static void features_config_destructor(void *obj)
+{
+	struct features_config *cfg = obj;
+
+	ao2_cleanup(cfg->global);
+	ao2_cleanup(cfg->featuremap);
+	ao2_cleanup(cfg->applicationmap);
+}
+
+static void featuremap_config_destructor(void *obj)
+{
+	struct features_featuremap *cfg = obj;
+
+	ast_string_field_free_memory(cfg);
+}
+
+static void applicationmap_config_destructor (void *obj)
+{
+	/* XXX STUB */
+}
+
+static void global_config_destructor(void *obj)
+{
+	struct features_global_config *cfg = obj;
+
+	ao2_cleanup(cfg->general);
+	ao2_cleanup(cfg->xfer);
+	ao2_cleanup(cfg->pickup);
+}
+
+static void xfer_destructor(void *obj)
+{
+	struct ast_features_xfer_config *cfg = obj;
+
+	ast_string_field_free_memory(cfg);
+}
+
+static void pickup_destructor(void *obj)
+{
+	struct ast_features_pickup_config *cfg = obj;
+
+	ast_string_field_free_memory(cfg);
+}
+
+static struct features_global_config *global_config_alloc(void)
+{
+	RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
+
+	cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
+	if (!cfg) {
+		return NULL;
+	}
+
+	cfg->general = ao2_alloc(sizeof(*cfg->general), NULL);
+	if (!cfg->general) {
+		return NULL;
+	}
+
+	cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
+	if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
+		return NULL;
+	}
+
+	cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
+	if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
+		return NULL;
+	}
+
+	ao2_ref(cfg, +1);
+	return cfg;
+}
+
+static void *features_config_alloc(void)
+{
+	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
+
+	cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
+	if (!cfg) {
+		return NULL;
+	}
+
+	cfg->global = global_config_alloc();;
+	if (!cfg->global) {
+		return NULL;
+	}
+
+	cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
+	if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
+		return NULL;
+	}
+
+	cfg->applicationmap = ao2_alloc(sizeof(*cfg->applicationmap),
+			applicationmap_config_destructor);
+	if (!cfg->applicationmap) {
+		return NULL;
+	}
+
+	ao2_ref(cfg, +1);
+	return cfg;
+}
+
+static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
+{
+	*dest = *src;
+}
+
+static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
+{
+	ast_string_fields_copy(dest, src);
+	dest->transferdigittimeout = src->transferdigittimeout;
+	dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
+	dest->atxferloopdelay = src->atxferloopdelay;
+	dest->atxfercallbackretries = src->atxfercallbackretries;
+	dest->atxferdropcall = src->atxferdropcall;
+}
+
+static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
+{
+	ast_string_fields_copy(dest, src);
+}
+
+static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
+{
+	general_copy(dest->general, src->general);
+	xfer_copy(dest->xfer, src->xfer);
+	pickup_copy(dest->pickup, src->pickup);
+}
+
+static void featuremap_copy(struct features_featuremap *dest, const struct features_featuremap *src)
+{
+	ast_string_fields_copy(dest, src);
+}
+
+static void applicationmap_copy(struct features_applicationmap *dest, const struct features_applicationmap *src)
+{
+	/* XXX STUB */
+}
+
+static void features_copy(struct features_config *dest, const struct features_config *src)
+{
+	global_copy(dest->global, src->global);
+	featuremap_copy(dest->featuremap, src->featuremap);
+	applicationmap_copy(dest->applicationmap, src->applicationmap);
+}
+
+static struct features_config *features_config_dup(const struct features_config *orig)
+{
+	struct features_config *dup;
+
+	dup = features_config_alloc();
+	if (!dup) {
+		return NULL;
+	}
+
+	features_copy(dup, orig);
+
+	return dup;
+}
+
+static int general_set(struct ast_features_general_config *general, const char *name,
+		const char *value)
+{
+	int res = 0;
+
+	if (!strcasecmp(name, "featuredigittimeout")) {
+		res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+
+	return res;
+}
+
+static int general_get(struct ast_features_general_config *general, const char *field,
+		char *buf, size_t len)
+{
+	int res = 0;
+
+	if (!strcasecmp(field, "featuredigittimeout")) {
+		snprintf(buf, len, "%u", general->featuredigittimeout);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+
+	return res;
+}
+
+static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
+		const char *value)
+{
+	int res = 0;
+
+	if (!strcasecmp(name, "transferdigittimeout")) {
+		res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
+	} else if (!strcasecmp(name, "atxfernoanswertimeout")) {
+		res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
+	} else if (!strcasecmp(name, "atxferloopdelay")) {
+		res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
+	} else if (!strcasecmp(name, "atxfercallbackretries")) {
+		res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
+	} else if (!strcasecmp(name, "atxferdropcall")) {
+		xfer->atxferdropcall = ast_true(value);
+	} else if (!strcasecmp(name, "xfersound")) {
+		ast_string_field_set(xfer, xfersound, value);
+	} else if (!strcasecmp(name, "xferfailsound")) {
+		ast_string_field_set(xfer, xferfailsound, value);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+	
+	return res;
+}
+
+static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
+		char *buf, size_t len)
+{
+	int res = 0;
+
+	if (!strcasecmp(field, "transferdigittimeout")) {
+		snprintf(buf, len, "%u", xfer->transferdigittimeout);
+	} else if (!strcasecmp(field, "atxfernoanswertimeout")) {
+		snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
+	} else if (!strcasecmp(field, "atxferloopdelay")) {
+		snprintf(buf, len, "%u", xfer->atxferloopdelay);
+	} else if (!strcasecmp(field, "atxfercallbackretries")) {
+		snprintf(buf, len, "%u", xfer->atxfercallbackretries);
+	} else if (!strcasecmp(field, "atxferdropcall")) {
+		snprintf(buf, len, "%u", xfer->atxferdropcall);
+	} else if (!strcasecmp(field, "xfersound")) {
+		ast_copy_string(buf, xfer->xfersound, len);
+	} else if (!strcasecmp(field, "xferfailsound")) {
+		ast_copy_string(buf, xfer->xferfailsound, len);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+
+	return res;
+}
+
+static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
+		const char *value)
+{
+	int res = 0;
+
+	if (!strcasecmp(name, "pickupsound")) {
+		ast_string_field_set(pickup, pickupsound, value);
+	} else if (!strcasecmp(name, "pickupfailsound")) {
+		ast_string_field_set(pickup, pickupfailsound, value);
+	} else if (!strcasecmp(name, "pickupexten")) {
+		ast_string_field_set(pickup, pickupexten, value);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+	
+	return res;
+}
+
+static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
+		char *buf, size_t len)
+{
+	int res = 0;
+
+	if (!strcasecmp(field, "pickupsound")) {
+		ast_copy_string(buf, pickup->pickupsound, len);
+	} else if (!strcasecmp(field, "pickupfailsound")) {
+		ast_copy_string(buf, pickup->pickupfailsound, len);
+	} else if (!strcasecmp(field, "pickupexten")) {
+		ast_copy_string(buf, pickup->pickupexten, len);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+
+	return res;
+}
+
+static int featuremap_set(struct features_featuremap *featuremap, const char *name,
+		const char *value)
+{
+	int res = 0;
+
+	if (!strcasecmp(name, "blindxfer")) {
+		ast_string_field_set(featuremap, blindxfer, value);
+	} else if (!strcasecmp(name, "disconnect")) {
+		ast_string_field_set(featuremap, disconnect, value);
+	} else if (!strcasecmp(name, "automon")) {
+		ast_string_field_set(featuremap, automon, value);
+	} else if (!strcasecmp(name, "atxfer")) {
+		ast_string_field_set(featuremap, atxfer, value);
+	} else if (!strcasecmp(name, "automixmon")) {
+		ast_string_field_set(featuremap, atxfer, value);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+	
+	return res;
+}
+
+static int featuremap_get(struct features_featuremap *featuremap, const char *field,
+		char *buf, size_t len)
+{
+	int res = 0;
+
+	if (!strcasecmp(field, "blindxfer")) {
+		ast_copy_string(buf, featuremap->blindxfer, len);
+	} else if (!strcasecmp(field, "disconnect")) {
+		ast_copy_string(buf, featuremap->disconnect, len);
+	} else if (!strcasecmp(field, "automon")) {
+		ast_copy_string(buf, featuremap->automon, len);
+	} else if (!strcasecmp(field, "atxfer")) {
+		ast_copy_string(buf, featuremap->atxfer, len);
+	} else if (!strcasecmp(field, "automixmon")) {
+		ast_copy_string(buf, featuremap->automixmon, len);
+	} else {
+		/* Unrecognized option */
+		res = -1;
+	}
+	
+	return res;
+}
+
+CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
+	.files = ACO_FILES(&features_conf),
+);
+
 #if defined(ATXFER_NULL_TECH)
 /*!
  * \internal
@@ -1455,8 +1848,6 @@
 	return pu;
 }
 
-static unsigned int get_parkingtime(struct ast_channel *chan, struct ast_parkinglot *parkinglot);
-
 /* Park a call */
 static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, struct ast_park_call_args *args)
 {
@@ -1490,7 +1881,10 @@
 	}
 
 	pu->start = ast_tvnow();
-	pu->parkingtime = (args->timeout > 0) ? args->timeout : get_parkingtime(chan, pu->parkinglot);
+	/* XXX This line was changed to not use get_parkingtime. This is just a placeholder message, because
+	 * likely this entire function is going away.
+	 */
+	pu->parkingtime = args->timeout;
 	if (args->extout)
 		*(args->extout) = pu->parkingnum;
 
@@ -3141,75 +3535,17 @@
 	return find_dynamic_feature(name);
 }
 
-struct feature_exten {
-	char sname[FEATURE_SNAME_LEN];
-	char exten[FEATURE_MAX_LEN];
-};
-
-struct feature_datastore {
-	struct ao2_container *feature_map;
-
-	/*!
-	 * \brief specified in seconds, stored in milliseconds
-	 *
-	 * \todo XXX This isn't pretty.  At some point it would be nice to have all
-	 * of the global / [general] options in a config object that we store here
-	 * instead of handling each one manually.
-	 *
-	 * \note If anything gets added here, don't forget to update
-	 * feature_ds_duplicate, as well.
-	 * */
-	unsigned int parkingtime;
-	unsigned int parkingtime_is_set:1;
-};
-
-static int feature_exten_hash(const void *obj, int flags)
-{
-	const struct feature_exten *fe = obj;
-	const char *sname = obj;
-
-	return ast_str_hash(flags & OBJ_KEY ? sname : fe->sname);
-}
-
-static int feature_exten_cmp(void *obj, void *arg, int flags)
-{
-	const struct feature_exten *fe = obj, *fe2 = arg;
-	const char *sname = arg;
-
-	return !strcmp(fe->sname, flags & OBJ_KEY ? sname : fe2->sname) ?
-			CMP_MATCH | CMP_STOP : 0;
-}
-
 static void feature_ds_destroy(void *data)
 {
-	struct feature_datastore *feature_ds = data;
-
-	if (feature_ds->feature_map) {
-		ao2_ref(feature_ds->feature_map, -1);
-		feature_ds->feature_map = NULL;
-	}
-
-	ast_free(feature_ds);
+	struct features_config *cfg = data;
+	ao2_cleanup(cfg);
 }
 
 static void *feature_ds_duplicate(void *data)
 {
-	struct feature_datastore *old_ds = data;
-	struct feature_datastore *new_ds;
-
-	if (!(new_ds = ast_calloc(1, sizeof(*new_ds)))) {
-		return NULL;
-	}
-
-	if (old_ds->feature_map) {
-		ao2_ref(old_ds->feature_map, +1);
-		new_ds->feature_map = old_ds->feature_map;
-	}
-
-	new_ds->parkingtime = old_ds->parkingtime;
-	new_ds->parkingtime_is_set = old_ds->parkingtime_is_set;
-
-	return new_ds;
+	struct features_config *old_cfg = data;
+
+	return features_config_dup(old_cfg);
 }
 
 static const struct ast_datastore_info feature_ds_info = {
@@ -3226,35 +3562,40 @@
  *
  * \return the data on the FEATURE datastore, or NULL on error
  */
-static struct feature_datastore *get_feature_ds(struct ast_channel *chan)
-{
-	struct feature_datastore *feature_ds;
+static struct features_config *get_feature_ds(struct ast_channel *chan)
+{
+	RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
+	struct features_config *cfg;
 	struct ast_datastore *ds;
 
 	if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
-		feature_ds = ds->data;
-		return feature_ds;
-	}
-
-	if (!(feature_ds = ast_calloc(1, sizeof(*feature_ds)))) {
+		cfg = ds->data;
+		ao2_ref(cfg, +1);
+		return cfg;
+	}
+
+	orig = ao2_global_obj_ref(globals);
+	if (!orig) {
 		return NULL;
 	}
 
-	if (!(feature_ds->feature_map = ao2_container_alloc(7, feature_exten_hash, feature_exten_cmp))) {
-		feature_ds_destroy(feature_ds);
+	cfg = features_config_dup(orig);
+	if (!cfg) {
 		return NULL;
 	}
 
 	if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
-		feature_ds_destroy(feature_ds);
+		ao2_cleanup(cfg);
 		return NULL;
 	}
 
-	ds->data = feature_ds;
+	/* Give the datastore a reference to the config */
+	ao2_ref(cfg, +1);
+	ds->data = cfg;
 
 	ast_channel_datastore_add(chan, ds);
 
-	return feature_ds;
+	return cfg;
 }
 
 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
@@ -3263,7 +3604,7 @@
 
 	if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
 		/* Hasn't been created yet.  Trigger creation. */
-		get_feature_ds(chan);
+		RAII_VAR(struct features_config *, cfg, get_feature_ds(chan), ao2_cleanup);
 		ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
 	}
 
@@ -3282,37 +3623,15 @@
 static int builtin_feature_get_exten(struct ast_channel *chan, const char *feature_name,
 		char *buf, size_t len)
 {
-	struct ast_call_feature *feature;
-	struct feature_datastore *feature_ds;
-	struct feature_exten *fe = NULL;
-
-	*buf = '\0';
-
-	if (!(feature = ast_find_call_feature(feature_name))) {
+	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
+	SCOPED_CHANNELLOCK(lock, chan);
+	
+	cfg = get_feature_ds(chan);
+	if (!cfg) {
 		return -1;
 	}
 
-	ast_copy_string(buf, feature->exten, len);
-
-	ast_unlock_call_features();
-
-	ast_channel_lock(chan);
-	if ((feature_ds = get_feature_ds(chan))) {
-		fe = ao2_find(feature_ds->feature_map, feature_name, OBJ_KEY);
-	}
-	ast_channel_unlock(chan);
-
-	ast_rdlock_call_features();
-
-	if (fe) {
-		ao2_lock(fe);
-		ast_copy_string(buf, fe->exten, len);
-		ao2_unlock(fe);
-		ao2_ref(fe, -1);
-		fe = NULL;
-	}
-
-	return 0;
+	return featuremap_get(cfg->featuremap, feature_name, buf, len);
 }
 
 /*!
@@ -4166,45 +4485,39 @@
 	}
 
 	res = 0;
-	ast_rdlock_call_features();
 	if (ast_test_flag(flags, AST_FEATURE_REDIRECT)) {
+		if (!builtin_feature_get_exten(chan, "blindxfer", dtmf, sizeof(dtmf))
+				&& !ast_strlen_zero(dtmf)) {
 		/* Add atxfer and blind transfer. */
-		builtin_feature_get_exten(chan, "blindxfer", dtmf, sizeof(dtmf));
-		if (!ast_strlen_zero(dtmf)) {
 /* BUGBUG need to supply a blind transfer structure and destructor to use other than defaults */
 			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf, NULL, NULL, 1);
 		}
-		builtin_feature_get_exten(chan, "atxfer", dtmf, sizeof(dtmf));
-		if (!ast_strlen_zero(dtmf)) {
+		if (!builtin_feature_get_exten(chan, "atxfer", dtmf, sizeof(dtmf)) &&
+				!ast_strlen_zero(dtmf)) {
 /* BUGBUG need to supply an attended transfer structure and destructor to use other than defaults */
 			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, dtmf, NULL, NULL, 1);
 		}
 	}
-	if (ast_test_flag(flags, AST_FEATURE_DISCONNECT)) {
-		builtin_feature_get_exten(chan, "disconnect", dtmf, sizeof(dtmf));
-		if (ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_HANGUP, dtmf, NULL, NULL, 1);
-		}
-	}
-	if (ast_test_flag(flags, AST_FEATURE_PARKCALL)) {
-		builtin_feature_get_exten(chan, "parkcall", dtmf, sizeof(dtmf));
-		if (!ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_PARKCALL, dtmf, NULL, NULL, 1);
-		}
-	}
-	if (ast_test_flag(flags, AST_FEATURE_AUTOMON)) {
-		builtin_feature_get_exten(chan, "automon", dtmf, sizeof(dtmf));
-		if (!ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMON, dtmf, NULL, NULL, 1);
-		}
-	}
-	if (ast_test_flag(flags, AST_FEATURE_AUTOMIXMON)) {
-		builtin_feature_get_exten(chan, "automixmon", dtmf, sizeof(dtmf));
-		if (!ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMIXMON, dtmf, NULL, NULL, 1);
-		}
-	}
-	ast_unlock_call_features();
+	if (ast_test_flag(flags, AST_FEATURE_DISCONNECT) &&
+			!builtin_feature_get_exten(chan, "disconnect", dtmf, sizeof(dtmf)) &&
+			!ast_strlen_zero(dtmf)) {
+		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_HANGUP, dtmf, NULL, NULL, 1);
+	}
+	if (ast_test_flag(flags, AST_FEATURE_PARKCALL) &&
+			!builtin_feature_get_exten(chan, "parkcall", dtmf, sizeof(dtmf)) &&
+			!ast_strlen_zero(dtmf)) {
+		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_PARKCALL, dtmf, NULL, NULL, 1);
+	}
+	if (ast_test_flag(flags, AST_FEATURE_AUTOMON) &&
+			!builtin_feature_get_exten(chan, "automon", dtmf, sizeof(dtmf)) &&
+			!ast_strlen_zero(dtmf)) {
+		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMON, dtmf, NULL, NULL, 1);
+	}
+	if (ast_test_flag(flags, AST_FEATURE_AUTOMIXMON) &&
+			!builtin_feature_get_exten(chan, "automixmon", dtmf, sizeof(dtmf)) &&
+			!ast_strlen_zero(dtmf)) {
+		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMIXMON, dtmf, NULL, NULL, 1);
+	}
 
 #if 0	/* BUGBUG don't report errors untill all of the builtin features are supported. */
 	return res ? -1 : 0;
@@ -6405,174 +6718,35 @@
 	return 0;
 }
 
-struct features_global_config {
-	struct ast_features_general_config *general;
-	struct ast_features_xfer_config *xfer;
-	struct ast_features_pickup_config *pickup;
-};
-
-struct features_featuremap {
-	AST_DECLARE_STRING_FIELDS (
-		AST_STRING_FIELD(blindxfer);
-		AST_STRING_FIELD(disconnect);
-		AST_STRING_FIELD(automon);
-		AST_STRING_FIELD(atxfer);
-		AST_STRING_FIELD(parkcall);
-		AST_STRING_FIELD(automixmon);
-	);
-};
-
-struct features_applicationmap {
-	/* XXX STUB */
-};
-
-struct features_config {
-	struct features_global_config *global;
-	struct features_featuremap *featuremap;
-	struct features_applicationmap *applicationmap;
-};
-
-static struct aco_type global_option = {
-	.type = ACO_GLOBAL,
-	.name = "globals",
-	.category_match = ACO_WHITELIST,
-	.category = "^general$",
-	.item_offset = offsetof(struct features_config, global),
-};
-
-static struct aco_type featuremap_option = {
-	.type = ACO_GLOBAL,
-	.name = "featuremap",
-	.category_match = ACO_WHITELIST,
-	.category = "^featuremap$",
-	.item_offset = offsetof(struct features_config, featuremap),
-};
-
-static struct aco_type applicationmap_option = {
-	.type = ACO_GLOBAL,
-	.name = "applicationmap",
-	.category_match = ACO_WHITELIST,
-	.category = "^applicationmap$",
-	.item_offset = offsetof(struct features_config, applicationmap),
-};
-
-struct aco_type *global_options[] = ACO_TYPES(&global_option);
-struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
-struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
-
-struct aco_file features_conf = {
-	.filename = "features.conf",
-	.types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option),
-};
-
-AO2_GLOBAL_OBJ_STATIC(globals);
-
-static void features_config_destructor(void *obj)
-{
-	struct features_config *cfg = obj;
-
-	ao2_cleanup(cfg->global);
-	ao2_cleanup(cfg->featuremap);
-	ao2_cleanup(cfg->applicationmap);
-}
-
-static void featuremap_config_destructor(void *obj)
-{
-	struct features_featuremap *cfg = obj;
-
-	ast_string_field_free_memory(cfg);
-}
-
-static void applicationmap_config_destructor (void *obj)
-{
-	/* XXX STUB */
-}
-
-static void global_config_destructor(void *obj)
-{
-	struct features_global_config *cfg = obj;
-
-	ao2_cleanup(cfg->general);
-	ao2_cleanup(cfg->xfer);
-	ao2_cleanup(cfg->pickup);
-}
-
-static void xfer_destructor(void *obj)
-{
-	struct ast_features_xfer_config *cfg = obj;
-
-	ast_string_field_free_memory(cfg);
-}
-
-static void pickup_destructor(void *obj)
-{
-	struct ast_features_pickup_config *cfg = obj;
-
-	ast_string_field_free_memory(cfg);
-}
-
-static struct features_global_config *global_config_alloc(void)
-{
-	RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
-
-	cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
+struct ast_features_general_config *ast_get_features_general_config(void)
+{
+	RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+
 	if (!cfg) {
 		return NULL;
 	}
 
-	cfg->general = ao2_alloc(sizeof(*cfg->general), NULL);
-	if (!cfg->general) {
-		return NULL;
-	}
-
-	cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
-	if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
-		return NULL;
-	}
-
-	cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
-	if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
-		return NULL;
-	}
-
-	ao2_ref(cfg, +1);
-	return cfg;
-}
-
-static void *features_config_alloc(void)
-{
-	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
-
-	cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
+	ast_assert(cfg->global && cfg->global->general);
+
+	ao2_ref(cfg->global->general, +1);
+	return cfg->global->general;
+}
+
+struct ast_features_xfer_config *ast_get_features_xfer_config(void)
+{
+	RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+
 	if (!cfg) {
 		return NULL;
 	}
 
-	cfg->global = global_config_alloc();;
-	if (!cfg->global) {
-		return NULL;
-	}
-
-	cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
-	if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
-		return NULL;
-	}
-
-	cfg->applicationmap = ao2_alloc(sizeof(*cfg->applicationmap),
-			applicationmap_config_destructor);
-	if (!cfg->applicationmap) {
-		return NULL;
-	}
-
-	ao2_ref(cfg, +1);
-	return cfg;
-}
-
-CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
-	.files = ACO_FILES(&features_conf),
-);
-
-struct ast_features_general_config *ast_get_features_general_config(void)
+	ast_assert(cfg->global && cfg->global->xfer);
+
+	ao2_ref(cfg->global->xfer, +1);
+	return cfg->global->xfer;
+}
+
+struct ast_features_pickup_config *ast_get_features_pickup_config(void)
 {
 	RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
 
@@ -6580,34 +6754,6 @@
 		return NULL;
 	}
 
-	ast_assert(cfg->global && cfg->global->general);
-
-	ao2_ref(cfg->global->general, +1);
-	return cfg->global->general;
-}
-
-struct ast_features_xfer_config *ast_get_features_xfer_config(void)
-{
-	RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
-
-	if (!cfg) {
-		return NULL;
-	}
-
-	ast_assert(cfg->global && cfg->global->xfer);
-
-	ao2_ref(cfg->global->xfer, +1);
-	return cfg->global->xfer;
-}
-
-struct ast_features_pickup_config *ast_get_features_pickup_config(void)
-{
-	RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
-
-	if (!cfg) {
-		return NULL;
-	}
-
 	ast_assert(cfg->global && cfg->global->pickup);
 
 	ao2_ref(cfg->global->pickup, +1);
@@ -6626,13 +6772,8 @@
 {
 	struct features_global_config *global = obj;
 	struct ast_features_general_config *general = global->general;
-
-	if (!strcasecmp(var->name, "featuredigittimeout")) {
-		return ast_parse_arg(var->value, PARSE_INT32, &general->featuredigittimeout);
-	} else {
-		/* Unrecognized option */
-		return -1;
-	}
+	
+	return general_set(general, var->name, var->value);
 }
 
 static int xfer_handler(const struct aco_option *opt,
@@ -6641,27 +6782,7 @@
 	struct features_global_config *global = obj;
 	struct ast_features_xfer_config *xfer = global->xfer;
 
-	if (!strcasecmp(var->name, "transferdigittimeout")) {
-		return ast_parse_arg(var->value, PARSE_INT32, &xfer->transferdigittimeout);
-	} else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
-		return ast_parse_arg(var->value, PARSE_INT32, &xfer->atxfernoanswertimeout);
-	} else if (!strcasecmp(var->name, "atxferloopdelay")) {
-		return ast_parse_arg(var->value, PARSE_INT32, &xfer->atxferloopdelay);
-	} else if (!strcasecmp(var->name, "atxfercallbackretries")) {
-		return ast_parse_arg(var->value, PARSE_INT32, &xfer->atxfercallbackretries);
-	} else if (!strcasecmp(var->name, "atxferdropcall")) {
-		xfer->atxferdropcall = ast_true(var->value);
-		return 0;
-	} else if (!strcasecmp(var->name, "xfersound")) {
-		ast_string_field_set(xfer, xfersound, var->value);
-		return 0;
-	} else if (!strcasecmp(var->name, "xferfailsound")) {
-		ast_string_field_set(xfer, xferfailsound, var->value);
-		return 0;
-	} else {
-		/* Unrecognized option */
-		return -1;
-	}
+	return xfer_set(xfer, var->name, var->value);
 }
 
 static int pickup_handler(const struct aco_option *opt,
@@ -6670,19 +6791,15 @@
 	struct features_global_config *global = obj;
 	struct ast_features_pickup_config *pickup = global->pickup;
 
-	if (!strcasecmp(var->name, "pickupsound")) {
-		ast_string_field_set(pickup, pickupsound, var->value);
-		return 0;
-	} else if (!strcasecmp(var->name, "pickupfailsound")) {
-		ast_string_field_set(pickup, pickupfailsound, var->value);
-		return 0;
-	} else if (!strcasecmp(var->name, "pickupexten")) {
-		ast_string_field_set(pickup, pickupexten, var->value);
-		return 0;
-	} else {
-		/* Unrecognized option */
-		return -1;
-	}
+	return pickup_set(pickup, var->name, var->value);
+}
+
+static int featuremap_handler(const struct aco_option *opt,
+		struct ast_variable *var, void *obj)
+{
+	struct features_featuremap *featuremap = obj;
+
+	return featuremap_set(featuremap, var->name, var->value);
 }
 
 static int really_load_config(int reload)
@@ -6717,18 +6834,18 @@
 	aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
 			"", pickup_handler, 0);
 
-	aco_option_register(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
-			"#", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, blindxfer));
-	aco_option_register(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
-			"*", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, disconnect));
-	aco_option_register(&cfg_info, "automon", ACO_EXACT, featuremap_options,
-			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, automon));
-	aco_option_register(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
-			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, atxfer));
-	aco_option_register(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
-			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, parkcall));
-	aco_option_register(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
-			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct features_featuremap, automixmon));
+	aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
+			"#", featuremap_handler, 0);
+	aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
+			"*", featuremap_handler, 0);
+	aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
+			"", featuremap_handler, 0);
+	aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
+			"", featuremap_handler, 0);
+	aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
+			"", featuremap_handler, 0);
+	aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
+			"", featuremap_handler, 0);
 	
 	/* XXX This regex may need to be more specific */
 	aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
@@ -8333,69 +8450,32 @@
 }
 #endif	/* defined(TEST_FRAMEWORK) */
 
-/*!
- * \internal
- * \brief Get parkingtime for a channel
- */
-static unsigned int get_parkingtime(struct ast_channel *chan, struct ast_parkinglot *parkinglot)
-{
-	const char *parkinglot_name;
-	struct feature_datastore *feature_ds;
-	unsigned int parkingtime;
-
-	ast_channel_lock(chan);
-
-	feature_ds = get_feature_ds(chan);
-	if (feature_ds && feature_ds->parkingtime_is_set) {
-		parkingtime = feature_ds->parkingtime;
-		ast_channel_unlock(chan);
-		return parkingtime;
-	}
-
-	parkinglot_name = ast_strdupa(S_OR(ast_channel_parkinglot(chan), ""));
-
-	ast_channel_unlock(chan);
-
-	if (!parkinglot) {
-		if (!ast_strlen_zero(parkinglot_name)) {
-			parkinglot = find_parkinglot(parkinglot_name);
-		}
-
-		if (!parkinglot) {
-			parkinglot = parkinglot_addref(default_parkinglot);
-		}
-	} else {
-		/* Just to balance the unref below */
-		parkinglot_addref(parkinglot);
-	}
-
-	parkingtime = parkinglot->cfg.parkingtime;
-
-	parkinglot_unref(parkinglot);
-
-	return parkingtime;
-}
-
 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
 	       char *buf, size_t len)
 {
-	int res = 0;
-
-	if (!strcasecmp(data, "parkingtime")) {
-		snprintf(buf, len, "%u", get_parkingtime(chan, NULL) / 1000);
-	} else if (!strcasecmp(data, "inherit")) {
-		struct ast_datastore *ds;
-		unsigned int inherit;
-
-		ast_channel_lock(chan);
-		ds = get_feature_chan_ds(chan);
-		inherit = ds ? ds->inheritance : 0;
-		ast_channel_unlock(chan);
+	int res;
+	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	if (!strcasecmp(data, "inherit")) {
+		struct ast_datastore *ds = get_feature_chan_ds(chan);
+		unsigned int inherit = ds ? ds->inheritance : 0;
 
 		snprintf(buf, len, "%s", inherit ? "yes" : "no");
-	} else {
+		return 0;
+	}
+
+	cfg = get_feature_ds(chan);
+	if (!cfg) {
+		return -1;
+	}
+
+	res = general_get(cfg->global->general, data, buf, len) &&
+		xfer_get(cfg->global->xfer, data, buf, len) &&
+		pickup_get(cfg->global->pickup, data, buf, len);
+
+	if (res) {
 		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
-		res = -1;
 	}
 
 	return res;
@@ -8404,37 +8484,29 @@
 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
 		const char *value)
 {
-	int res = 0;
-	struct feature_datastore *feature_ds;
-
-	ast_channel_lock(chan);
-
-	if (!(feature_ds = get_feature_ds(chan))) {
-		res = -1;
-		goto return_cleanup;
-	}
-
-	if (!strcasecmp(data, "parkingtime")) {
-		feature_ds->parkingtime_is_set = 1;
-		if (sscanf(value, "%30u", &feature_ds->parkingtime) == 1) {
-			feature_ds->parkingtime *= 1000; /* stored in ms */
-		} else {
-			ast_log(LOG_WARNING, "'%s' is not a valid parkingtime\n", value);
-			feature_ds->parkingtime_is_set = 0;
-			res = -1;
-		}
-	} else if (!strcasecmp(data, "inherit")) {
-		struct ast_datastore *ds;
-		if ((ds = get_feature_chan_ds(chan))) {
+	int res;
+	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	if (!strcasecmp(data, "inherit")) {
+		struct ast_datastore *ds = get_feature_chan_ds(chan);
+		if (ds) {
 			ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
 		}
-	} else {
+		return 0;
+	}
+
+	if (!(cfg = get_feature_ds(chan))) {
+		return -1;
+	}
+
+	res = general_set(cfg->global->general, data, value) &&
+		xfer_set(cfg->global->xfer, data, value) &&
+		pickup_set(cfg->global->pickup, data, value);
+
+	if (res) {
 		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
-		res = -1;
-	}
-
-return_cleanup:
-	ast_channel_unlock(chan);
+	}
 
 	return res;
 }
@@ -8444,13 +8516,11 @@
 {
 	int res;
 
-	ast_rdlock_call_features();
-
-	if ((res = builtin_feature_get_exten(chan, data, buf, len))) {
+	res = builtin_feature_get_exten(chan, data, buf, len);
+
+	if (res) {
 		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
 	}
-
-	ast_unlock_call_features();
 
 	return res;
 }
@@ -8458,41 +8528,19 @@
 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
 		const char *value)
 {
-	struct feature_datastore *feature_ds;
-	struct feature_exten *fe;
-	struct ast_call_feature *feat;
-
-	ast_rdlock_call_features();
-	feat = ast_find_call_feature(data);
-	ast_unlock_call_features();
-	if (!feat) {
+	int res;
+	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	if (!(cfg = get_feature_ds(chan))) {
+		return -1;
+	}
+
+	res = featuremap_set(cfg->featuremap, data, value);
+	if (res) {
 		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
 		return -1;
 	}
-
-	ast_channel_lock(chan);
-
-	if (!(feature_ds = get_feature_ds(chan))) {
-		ast_channel_unlock(chan);
-		return -1;
-	}
-
-	if (!(fe = ao2_find(feature_ds->feature_map, data, OBJ_KEY))) {
-		if (!(fe = ao2_alloc(sizeof(*fe), NULL))) {
-			ast_channel_unlock(chan);
-			return -1;
-		}
-		ast_copy_string(fe->sname, data, sizeof(fe->sname));
-		ao2_link(feature_ds->feature_map, fe);
-	}
-
-	ast_channel_unlock(chan);
-
-	ao2_lock(fe);
-	ast_copy_string(fe->exten, value, sizeof(fe->exten));
-	ao2_unlock(fe);
-	ao2_ref(fe, -1);
-	fe = NULL;
 
 	return 0;
 }




More information about the asterisk-commits mailing list