func_channel: dtmf_features setting
authorJonathan Rose <jrose@digium.com>
Tue, 23 Jul 2013 21:32:33 +0000 (21:32 +0000)
committerJonathan Rose <jrose@digium.com>
Tue, 23 Jul 2013 21:32:33 +0000 (21:32 +0000)
Allows reading andsetting dtmf features via a channel function
CHANNEL(dtmf_features)

(closes issue ASTERISK-21876)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2648/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395215 65c4cc65-6c06-0410-ace0-fbb531ad65f3

funcs/func_channel.c
include/asterisk/bridging_basic.h
main/bridging_basic.c

index 006c0cb..4d098c3 100644 (file)
@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/indications.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/global_datastores.h"
+#include "asterisk/bridging_basic.h"
 
 /*** DOCUMENTATION
        <function name="CHANNELS" language="en_US">
@@ -101,6 +102,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <enum name="audiowriteformat">
                                                <para>R/O format currently being written.</para>
                                        </enum>
+                                       <enum name="dtmf_features">
+                                               <para>R/W The channel's DTMF bridge features.
+                                               May include one or more of 'T' 'K' 'H' 'W' and 'X' in a similar manner to options
+                                               in the <literal>Dial</literal> application. When setting it, the features string
+                                               must be all upper case.</para>
+                                       </enum>
                                        <enum name="callgroup">
                                                <para>R/W numeric call pickup groups that this channel is a member.</para>
                                        </enum>
@@ -436,9 +443,13 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
                ast_copy_string(buf, ast_channel_trace_is_enabled(chan) ? "1" : "0", len);
                ast_channel_unlock(chan);
 #endif
-       } else if (!strcasecmp(data, "tonezone") && ast_channel_zone(chan))
+       } else if (!strcasecmp(data, "tonezone") && ast_channel_zone(chan)) {
                locked_copy_string(chan, buf, ast_channel_zone(chan)->country, len);
-       else if (!strcasecmp(data, "language"))
+       } else if (!strcasecmp(data, "dtmf_features")) {
+               if (ast_bridge_features_ds_get_string(chan, buf, len)) {
+                       buf[0] = '\0';
+               }
+       } else if (!strcasecmp(data, "language"))
                locked_copy_string(chan, buf, ast_channel_language(chan), len);
        else if (!strcasecmp(data, "musicclass"))
                locked_copy_string(chan, buf, ast_channel_musicclass(chan), len);
@@ -615,6 +626,8 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio
                        ast_channel_unlock(chan);
                        new_zone = ast_tone_zone_unref(new_zone);
                }
+       } else if (!strcasecmp(data, "dtmf_features")) {
+               ret = ast_bridge_features_ds_set_string(chan, value);
        } else if (!strcasecmp(data, "callgroup")) {
                ast_channel_lock(chan);
                ast_channel_callgroup_set(chan, ast_get_group(value));
index cc42354..4db1429 100644 (file)
@@ -36,15 +36,41 @@ extern "C" {
 /* ------------------------------------------------------------------- */
 
 /*!
+ * \brief Sets the features a channel will use upon being bridged.
+ * \since 12.0.0
+ *
+ * \param chan Which channel to set features for
+ * \param features Which feature codes to set for the channel
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_bridge_features_ds_set_string(struct ast_channel *chan, const char *features);
+
+/*!
+ * \brief writes a channel's DTMF features to a buffer string
+ * \since 12.0.0
+ *
+ * \param chan channel whose feature flags should be checked
+ * \param buffer pointer string buffer where the output should be stored
+ * \param buf_size size of the provided buffer (ideally enough for all features, 6+)
+ *
+ * \retval 0 on successful write
+ * \retval -1 on failure
+ */
+int ast_bridge_features_ds_get_string(struct ast_channel *chan, char *buffer, size_t buf_size);
+
+/*!
  * \brief Get DTMF feature flags from the channel.
  * \since 12.0.0
  *
  * \param chan Channel to get DTMF features datastore.
  *
  * \note The channel should be locked before calling this function.
+ * \note The channel must remain locked until the flags returned have been consumed.
  *
  * \retval flags on success.
- * \retval NULL on error.
+ * \retval NULL if the datastore does not exist.
  */
 struct ast_flags *ast_bridge_features_ds_get(struct ast_channel *chan);
 
index da4b403..3353066 100644 (file)
@@ -82,6 +82,143 @@ static const struct ast_datastore_info dtmf_features_info = {
        .destroy = ast_free_ptr,
 };
 
+/*!
+ * \internal
+ * \since 12.0.0
+ * \brief read a feature code character and set it on for the give feature_flags struct
+ *
+ * \param feature_flags flags being modifed
+ * \param feature feature code provided - should be an uppercase letter
+ *
+ * \retval 0 if the feature was set successfully
+ * \retval -1 failure because the requested feature code isn't handled by this function
+ */
+static int set_feature_flag_from_char(struct ast_flags *feature_flags, char feature)
+{
+       switch (feature) {
+       case 'T':
+               ast_set_flag(feature_flags, AST_FEATURE_REDIRECT);
+               return 0;
+       case 'K':
+               ast_set_flag(feature_flags, AST_FEATURE_PARKCALL);
+               return 0;
+       case 'H':
+               ast_set_flag(feature_flags, AST_FEATURE_DISCONNECT);
+               return 0;
+       case 'W':
+               ast_set_flag(feature_flags, AST_FEATURE_AUTOMON);
+               return 0;
+       case 'X':
+               ast_set_flag(feature_flags, AST_FEATURE_AUTOMIXMON);
+               return 0;
+       default:
+               return -1;
+       }
+}
+
+/*!
+ * \internal
+ * \since 12.0.0
+ * \brief Write a features string to a string buffer based on the feature flags provided
+ *
+ * \param feature_flags pointer to the feature flags to write from.
+ * \param buffer pointer to a string buffer to write the features
+ * \param buffer_size size of the buffer provided (should be able to fit all feature codes)
+ *
+ * \retval 0 on successful write
+ * \retval -1 failure due to running out of buffer space
+ */
+static int dtmf_features_flags_to_string(struct ast_flags *feature_flags, char *buffer, size_t buffer_size)
+{
+       size_t buffer_expended = 0;
+       unsigned int cur_feature;
+       static const struct {
+               char letter;
+               unsigned int flag;
+       } associations[] = {
+               { 'T', AST_FEATURE_REDIRECT },
+               { 'K', AST_FEATURE_PARKCALL },
+               { 'H', AST_FEATURE_DISCONNECT },
+               { 'W', AST_FEATURE_AUTOMON },
+               { 'X', AST_FEATURE_AUTOMIXMON },
+       };
+
+       for (cur_feature = 0; cur_feature < ARRAY_LEN(associations); cur_feature++) {
+               if (ast_test_flag(feature_flags, associations[cur_feature].flag)) {
+                       if (buffer_expended == buffer_size - 1) {
+                               buffer[buffer_expended] = '\0';
+                               return -1;
+                       }
+                       buffer[buffer_expended++] = associations[cur_feature].letter;
+               }
+       }
+
+       buffer[buffer_expended] = '\0';
+       return 0;
+}
+
+static int build_dtmf_features(struct ast_flags *flags, const char *features)
+{
+       const char *feature;
+
+       char missing_features[strlen(features) + 1];
+       size_t number_of_missing_features = 0;
+
+       for (feature = features; *feature; feature++) {
+               if (!isupper(*feature)) {
+                       ast_log(LOG_ERROR, "Features string '%s' rejected because it contains non-uppercase feature.\n", features);
+                       return -1;
+               }
+
+               if (set_feature_flag_from_char(flags, *feature)) {
+                       missing_features[number_of_missing_features++] = *feature;
+               }
+       }
+
+       missing_features[number_of_missing_features] = '\0';
+
+       if (number_of_missing_features) {
+               ast_log(LOG_WARNING, "Features '%s' from features string '%s' can not be applied.\n", missing_features, features);
+       }
+
+       return 0;
+}
+
+int ast_bridge_features_ds_set_string(struct ast_channel *chan, const char *features)
+{
+       struct ast_flags flags = {0};
+
+       if (build_dtmf_features(&flags, features)) {
+               return -1;
+       }
+
+       ast_channel_lock(chan);
+       if (ast_bridge_features_ds_set(chan, &flags)) {
+               ast_channel_unlock(chan);
+               ast_log(LOG_ERROR, "Failed to apply features datastore for '%s' to channel '%s'\n", features, ast_channel_name(chan));
+               return -1;
+       }
+       ast_channel_unlock(chan);
+
+       return 0;
+}
+
+int ast_bridge_features_ds_get_string(struct ast_channel *chan, char *buffer, size_t buf_size)
+{
+       struct ast_flags *channel_flags;
+       struct ast_flags held_copy;
+
+       ast_channel_lock(chan);
+       if (!(channel_flags = ast_bridge_features_ds_get(chan))) {
+               ast_channel_unlock(chan);
+               return -1;
+       }
+       held_copy = *channel_flags;
+       ast_channel_unlock(chan);
+
+       return dtmf_features_flags_to_string(&held_copy, buffer, buf_size);
+}
+
 int ast_bridge_features_ds_set(struct ast_channel *chan, struct ast_flags *flags)
 {
        struct ast_datastore *datastore;