[asterisk-commits] russell: trunk r364437 - in /trunk: CHANGES main/features.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 27 19:59:12 CDT 2012


Author: russell
Date: Fri Apr 27 19:58:54 2012
New Revision: 364437

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=364437
Log:
features: Add FEATURE() and FEATUREMAP() functions.

Add two new dialplan functions: FEATURE() and FEATUREMAP().  FEATURE()
lets you set some of the configuration options from the [general] section
of features.conf on a per-channel basis.  FEATUREMAP() lets you customize
the key sequence used to activate built-in features, such as blindxfer,
and automon.  See the built-in documentation for details.

Review: https://reviewboard.asterisk.org/r/1871/

Modified:
    trunk/CHANGES
    trunk/main/features.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=364437&r1=364436&r2=364437
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Apr 27 19:58:54 2012
@@ -209,6 +209,11 @@
    VM_INFO.
  * The REDIRECTING function now supports the redirecting original party id
    and reason.
+ * Two new functions have been added: FEATURE() and FEATUREMAP().  FEATURE()
+   lets you set some of the configuration options from the [general] section
+   of features.conf on a per-channel basis.  FEATUREMAP() lets you customize
+   the key sequence used to activate built-in features, such as blindxfer,
+   and automon.  See the built-in documentation for details.
 
 Followme changes
 -------------

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=364437&r1=364436&r2=364437
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Fri Apr 27 19:58:54 2012
@@ -1,7 +1,8 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2008, Digium, Inc.
+ * Copyright (C) 1999 - 2012, Digium, Inc.
+ * Copyright (C) 2012, Russell Bryant
  *
  * Mark Spencer <markster at digium.com>
  *
@@ -408,6 +409,57 @@
 			<para>Bridge together two channels already in the PBX.</para>
 		</description>
 	</manager>
+	<function name="FEATURE" language="en_US">
+		<synopsis>
+			Get or set a feature option on a channel.
+		</synopsis>
+		<syntax>
+			<parameter name="option_name" required="true">
+				<para>The allowed values are:</para>
+				<enumlist>
+					<enum name="parkingtime"><para>Specified in seconds.</para></enum>
+				</enumlist>
+			</parameter>
+		</syntax>
+		<description>
+			<para>When this function is used as a read, it will get the current
+			value of the specified feature option for this channel.  It will be
+			the value of this option configured in features.conf if a channel specific
+			value has not been set.  This function can also be used to set a channel
+			specific value for the supported feature options.</para>
+		</description>
+		<see-also>
+			<ref type="function">FEATUREMAP</ref>
+		</see-also>
+	</function>
+	<function name="FEATUREMAP" language="en_US">
+		<synopsis>
+			Get or set a feature map to a given value on a specific channel.
+		</synopsis>
+		<syntax>
+			<parameter name="feature_name" required="true">
+				<para>The allowed values are:</para>
+				<enumlist>
+					<enum name="atxfer"><para>Attended Transfer</para></enum>
+					<enum name="blindxfer"><para>Blind Transfer</para></enum>
+					<enum name="automon"><para>Auto Monitor</para></enum>
+					<enum name="disconnect"><para>Call Disconnect</para></enum>
+					<enum name="parkcall"><para>Park Call</para></enum>
+					<enum name="automixmon"><para>Auto MixMonitor</para></enum>
+				</enumlist>
+			</parameter>
+		</syntax>
+		<description>
+			<para>When this function is used as a read, it will get the current
+			digit sequence mapped to the specified feature for this channel.  This
+			value will be the one configured in features.conf if a channel specific
+			value has not been set.  This function can also be used to set a channel
+			specific value for a feature mapping.</para>
+		</description>
+		<see-also>
+			<ref type="function">FEATURE</ref>
+		</see-also>
+	</function>
  ***/
 
 #define DEFAULT_PARK_TIME							45000	/*!< ms */
@@ -508,7 +560,7 @@
 	char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
 	char exten[AST_MAX_EXTENSION];
 	int priority;
-	int parkingtime;                            /*!< Maximum length in parking lot before return */
+	unsigned int parkingtime;                   /*!< Maximum length in parking lot before return */
 	/*! Method to entertain the caller when parked: AST_CONTROL_RINGING, AST_CONTROL_HOLD, or 0(none) */
 	enum ast_control_frame_type hold_method;
 	unsigned int notquiteyet:1;
@@ -535,7 +587,7 @@
 	/*! Last available extension for parking */
 	int parking_stop;
 	/*! Default parking time in ms. */
-	int parkingtime;
+	unsigned int parkingtime;
 	/*!
 	 * \brief Enable DTMF based transfers on bridge when picking up parked calls.
 	 *
@@ -1482,6 +1534,8 @@
 	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)
 {
@@ -1515,7 +1569,7 @@
 	}
 
 	pu->start = ast_tvnow();
-	pu->parkingtime = (args->timeout > 0) ? args->timeout : pu->parkinglot->cfg.parkingtime;
+	pu->parkingtime = (args->timeout > 0) ? args->timeout : get_parkingtime(chan, pu->parkinglot);
 	if (args->extout)
 		*(args->extout) = pu->parkingnum;
 
@@ -1588,7 +1642,7 @@
 
 	/* Wake up the (presumably select()ing) thread */
 	pthread_kill(parking_thread, SIGURG);
-	ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n",
+	ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %u seconds\n",
 		ast_channel_name(chan), pu->parkingnum, pu->parkinglot->name,
 		pu->context, pu->exten, pu->priority, (pu->parkingtime / 1000));
 
@@ -3158,6 +3212,10 @@
 	ast_rwlock_unlock(&features_lock);
 }
 
+/*!
+ * \internal
+ * \pre Expects feature_lock to be readlocked
+ */
 struct ast_call_feature *ast_find_call_feature(const char *name)
 {
 	int x;
@@ -3167,6 +3225,143 @@
 	}
 
 	return find_dynamic_feature(name);
+}
+
+struct feature_exten {
+	char sname[FEATURE_SNAME_LEN];
+	char exten[FEATURE_MAX_LEN];
+};
+
+struct feature_ds {
+	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.
+	 * */
+	unsigned int parkingtime;
+	unsigned int parkingtime_is_set:1;
+};
+
+static void feature_ds_destroy(void *data)
+{
+	struct feature_ds *feature_ds = data;
+
+	if (feature_ds->feature_map) {
+		ao2_ref(feature_ds->feature_map, -1);
+		feature_ds->feature_map = NULL;
+	}
+
+	ast_free(feature_ds);
+}
+
+static const struct ast_datastore_info feature_ds_info = {
+	.type = "FEATURE",
+	.destroy = feature_ds_destroy,
+};
+
+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;
+}
+
+/*!
+ * \internal
+ * \brief Find or create feature datastore on a channel
+ *
+ * \pre chan is locked
+ *
+ * \return the data on the FEATURE datastore, or NULL on error
+ */
+static struct feature_ds *get_feature_ds(struct ast_channel *chan)
+{
+	struct feature_ds *feature_ds;
+	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)))) {
+		return NULL;
+	}
+
+	if (!(feature_ds->feature_map = ao2_container_alloc(7, feature_exten_hash, feature_exten_cmp))) {
+		feature_ds_destroy(feature_ds);
+		return NULL;
+	}
+
+	if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
+		feature_ds_destroy(feature_ds);
+		return NULL;
+	}
+
+	ds->data = feature_ds;
+
+	ast_channel_datastore_add(chan, ds);
+
+	return feature_ds;
+}
+
+/*!
+ * \internal
+ * \brief Get the extension for a given builtin feature
+ *
+ * \pre expects feature_lock to be readlocked
+ *
+ * \retval 0 success
+ * \retval non-zero failiure
+ */
+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_ds *feature_ds;
+	struct feature_exten *fe = NULL;
+
+	*buf = '\0';
+
+	if (!(feature = ast_find_call_feature(feature_name))) {
+		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;
 }
 
 /*!
@@ -3299,25 +3494,33 @@
 
 	ast_rwlock_rdlock(&features_lock);
 	for (x = 0; x < FEATURES_COUNT; x++) {
-		if ((ast_test_flag(features, builtin_features[x].feature_mask)) &&
-		    !ast_strlen_zero(builtin_features[x].exten)) {
-			/* Feature is up for consideration */
-			if (!strcmp(builtin_features[x].exten, code)) {
-				ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
-				if (operation == FEATURE_INTERPRET_CHECK) {
-					res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
-				} else if (operation == FEATURE_INTERPRET_DO) {
-					res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
-				}
-				if (feature) {
-					memcpy(feature, &builtin_features[x], sizeof(*feature));
-				}
-				feature_detected = 1;
-				break;
-			} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
-				if (res == AST_FEATURE_RETURN_PASSDIGITS) {
-					res = AST_FEATURE_RETURN_STOREDIGITS;
-				}
+		char feature_exten[FEATURE_MAX_LEN] = "";
+
+		if (!ast_test_flag(features, builtin_features[x].feature_mask)) {
+			continue;
+		}
+
+		if (builtin_feature_get_exten(chan, builtin_features[x].sname, feature_exten, sizeof(feature_exten))) {
+			continue;
+		}
+
+		/* Feature is up for consideration */
+
+		if (!strcmp(feature_exten, code)) {
+			ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, feature_exten);
+			if (operation == FEATURE_INTERPRET_CHECK) {
+				res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
+			} else if (operation == FEATURE_INTERPRET_DO) {
+				res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
+			}
+			if (feature) {
+				memcpy(feature, &builtin_features[x], sizeof(*feature));
+			}
+			feature_detected = 1;
+			break;
+		} else if (!strncmp(feature_exten, code, strlen(code))) {
+			if (res == AST_FEATURE_RETURN_PASSDIGITS) {
+				res = AST_FEATURE_RETURN_STOREDIGITS;
 			}
 		}
 	}
@@ -5539,9 +5742,9 @@
 		} else if (!strcasecmp(var->name, "parkedmusicclass")) {
 			ast_copy_string(cfg->mohclass, var->value, sizeof(cfg->mohclass));
 		} else if (!strcasecmp(var->name, "parkingtime")) {
-			int parkingtime = 0;
-
-			if ((sscanf(var->value, "%30d", &parkingtime) != 1) || parkingtime < 1) {
+			unsigned int parkingtime = 0;
+
+			if ((sscanf(var->value, "%30u", &parkingtime) != 1) || parkingtime < 1) {
 				ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
 				error = -1;
 			} else {
@@ -6851,7 +7054,7 @@
 		ast_cli(a->fd,"%-22s:      %s\n", "Parking context", curlot->cfg.parking_con);
 		ast_cli(a->fd,"%-22s:      %d-%d\n", "Parked call extensions",
 			curlot->cfg.parking_start, curlot->cfg.parking_stop);
-		ast_cli(a->fd,"%-22s:      %d ms\n", "Parkingtime", curlot->cfg.parkingtime);
+		ast_cli(a->fd,"%-22s:      %u ms\n", "Parkingtime", curlot->cfg.parkingtime);
 		ast_cli(a->fd,"%-22s:      %s\n", "Comeback to origin",
 				(curlot->cfg.comebacktoorigin ? "yes" : "no"));
 		ast_cli(a->fd,"%-22s:      %s%s\n", "Comeback context",
@@ -8282,6 +8485,163 @@
 }
 #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_ds *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 {
+		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
+		res = -1;
+	}
+
+	return res;
+}
+
+static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
+		const char *value)
+{
+	int res = 0;
+	struct feature_ds *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 {
+		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
+		res = -1;
+	}
+
+return_cleanup:
+	ast_channel_unlock(chan);
+
+	return res;
+}
+
+static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
+	       char *buf, size_t len)
+{
+	int res;
+
+	ast_rdlock_call_features();
+
+	if ((res = builtin_feature_get_exten(chan, data, buf, len))) {
+		ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
+	}
+
+	ast_unlock_call_features();
+
+	return res;
+}
+
+static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
+		const char *value)
+{
+	struct feature_ds *feature_ds;
+	struct feature_exten *fe;
+
+	if (!ast_find_call_feature(data)) {
+		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;
+}
+
+static struct ast_custom_function feature_function = {
+	.name = "FEATURE",
+	.read = feature_read,
+	.write = feature_write
+};
+
+static struct ast_custom_function featuremap_function = {
+	.name = "FEATUREMAP",
+	.read = featuremap_read,
+	.write = featuremap_write
+};
+
 int ast_features_init(void)
 {
 	int res;
@@ -8306,6 +8666,8 @@
 		ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
 		ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
 	}
+	res |= __ast_custom_function_register(&feature_function, NULL);
+	res |= __ast_custom_function_register(&featuremap_function, NULL);
 
 	res |= ast_devstate_prov_add("Park", metermaidstate);
 #if defined(TEST_FRAMEWORK)




More information about the asterisk-commits mailing list