diff options
-rw-r--r-- | funcs/func_channel.c | 17 | ||||
-rw-r--r-- | include/asterisk/bridging_basic.h | 28 | ||||
-rw-r--r-- | main/bridging_basic.c | 137 |
3 files changed, 179 insertions, 3 deletions
diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 006c0cb99..4d098c347 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -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)); diff --git a/include/asterisk/bridging_basic.h b/include/asterisk/bridging_basic.h index cc42354dc..4db142946 100644 --- a/include/asterisk/bridging_basic.h +++ b/include/asterisk/bridging_basic.h @@ -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); diff --git a/main/bridging_basic.c b/main/bridging_basic.c index da4b4034a..335306685 100644 --- a/main/bridging_basic.c +++ b/main/bridging_basic.c @@ -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; |