aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_minivm.c2
-rw-r--r--configs/features.conf.sample17
-rw-r--r--res/res_features.c183
3 files changed, 195 insertions, 7 deletions
diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index 8dc8d6a5f..7af82d767 100644
--- a/apps/app_minivm.c
+++ b/apps/app_minivm.c
@@ -2301,7 +2301,7 @@ static int timezone_add(char *zonename, char *config)
struct minivm_zone *newzone;
char *msg_format, *timezone;
- newzone = malloc(sizeof(struct minivm_zone));
+ newzone = ast_calloc(1, sizeof(struct minivm_zone));
if (newzone == NULL)
return 0;
diff --git a/configs/features.conf.sample b/configs/features.conf.sample
index 85a0d9d5f..966d008a4 100644
--- a/configs/features.conf.sample
+++ b/configs/features.conf.sample
@@ -100,3 +100,20 @@ context => parkedcalls ; Which context parked calls are in
;unpauseMonitor => #3,self/callee,UnPauseMonitor ;Allow the callee to unpause monitoring
; ;on their channel
;
+; GROUPS
+; Groups are groupings of features defined in [applicationmap]
+; that can have their own key mappings.
+;
+; Groups are defined as a configuration section,
+; and can be set as part of DYNAMIC_FEATURES in
+; the same way that a normal feature can...
+; etc:
+;
+; Set(DYNAMIC_FEATURES=myGroupName);
+;
+; example:
+; [myGroupName] ; defines the group named myGroupName
+; testfeature => #9 ; associates testfeature with the group and the keycode #9
+; pauseMonitor ; associates pauseMonitor with the group and the keycode
+; ; defined in [applicationmap]
+
diff --git a/res/res_features.c b/res/res_features.c
index f67919c0d..0ca1ebeef 100644
--- a/res/res_features.c
+++ b/res/res_features.c
@@ -78,6 +78,20 @@ enum {
AST_FEATURE_FLAG_BYBOTH = (3 << 3),
};
+struct feature_group_exten {
+ AST_LIST_ENTRY(feature_group_exten) entry;
+ char exten[FEATURE_MAX_LEN];
+ struct ast_call_feature *feature;
+};
+
+struct feature_group {
+ AST_LIST_ENTRY(feature_group) entry;
+ char gname[80];
+ AST_LIST_HEAD_NOLOCK(, feature_group_exten) features;
+};
+
+static AST_RWLIST_HEAD_STATIC(feature_groups, feature_group);
+
static char *parkedcall = "ParkedCall";
static int parkaddhints = 0; /*!< Add parking hints automatically */
@@ -1010,7 +1024,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
AST_RWLOCK_DEFINE_STATIC(features_lock);
static struct ast_call_feature builtin_features[] =
- {
+{
{ AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
{ AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
{ AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
@@ -1037,6 +1051,64 @@ void ast_register_feature(struct ast_call_feature *feature)
ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
}
+/*! \brief This function must be called while feature_groups is locked... */
+static struct feature_group* register_group(const char *fgname)
+{
+ struct feature_group *fg;
+
+ if (!fgname) {
+ ast_log(LOG_NOTICE, "You didn't pass a new group name!\n");
+ return NULL;
+ }
+
+ fg = ast_calloc(1, sizeof(*fg));
+
+ if (!fg) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for group '%s'\n", fgname);
+ return NULL;
+ }
+
+ ast_copy_string(fg->gname, fgname, sizeof(fg->gname));
+
+ AST_LIST_INSERT_HEAD(&feature_groups, fg, entry);
+
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered group '%s'\n", fg->gname);
+
+ return fg;
+}
+
+/*! \brief This function must be called while feature_groups is locked... */
+
+static void register_group_feature(struct feature_group *fg, const char *exten, struct ast_call_feature *feature)
+{
+ struct feature_group_exten *fge;
+
+ fge = ast_calloc(1, sizeof(*fge));
+
+ if (!fge) return;
+
+ if (!fg) {
+ ast_log(LOG_NOTICE, "You didn't pass a group!\n");
+ return;
+ }
+
+ if (!feature) {
+ ast_log(LOG_NOTICE, "You didn't pass a feature!\n");
+ return;
+ }
+
+ ast_copy_string(fge->exten, (ast_strlen_zero(exten) ? feature->exten : exten), sizeof(fge->exten));
+
+ fge->feature = feature;
+
+ AST_LIST_INSERT_HEAD(&fg->features, fge, entry);
+
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered feature '%s' for group '%s' at exten '%s'\n",
+ feature->sname, fg->gname, exten);
+}
+
/*! \brief unregister feature from feature_list */
void ast_unregister_feature(struct ast_call_feature *feature)
{
@@ -1073,6 +1145,44 @@ static struct ast_call_feature *find_dynamic_feature(const char *name)
return tmp;
}
+/*! \brief Remove all groups in the list */
+static void ast_unregister_groups(void)
+{
+ struct feature_group *fg;
+ struct feature_group_exten *fge;
+
+ AST_RWLIST_WRLOCK(&feature_groups);
+ while ((fg = AST_LIST_REMOVE_HEAD(&feature_groups, entry))) {
+ while ((fge = AST_LIST_REMOVE_HEAD(&fg->features, entry)))
+ free(fge);
+ free(fg);
+ }
+ AST_RWLIST_UNLOCK(&feature_groups);
+}
+
+/*! \brief Find a group by name */
+static struct feature_group *find_group(const char *name) {
+ struct feature_group *fg = NULL;
+
+ AST_LIST_TRAVERSE(&feature_groups, fg, entry) {
+ if (!strcasecmp(fg->gname, name))
+ break;
+ }
+
+ return fg;
+}
+
+static struct feature_group_exten *find_group_exten(struct feature_group *fg, const char *code) {
+ struct feature_group_exten *fge = NULL;
+
+ AST_LIST_TRAVERSE(&fg->features, fge, entry) {
+ if(!strcasecmp(fge->exten, code))
+ break;
+ }
+
+ return fge;
+}
+
void ast_rdlock_call_features(void)
{
ast_rwlock_rdlock(&features_lock);
@@ -1197,13 +1307,15 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
struct ast_flags features;
int res = FEATURE_RETURN_PASSDIGITS;
struct ast_call_feature *feature;
+ struct feature_group *fg = NULL;
+ struct feature_group_exten *fge;
const char *dynamic_features=pbx_builtin_getvar_helper(chan,"DYNAMIC_FEATURES");
char *tmp, *tok;
if (sense == FEATURE_SENSE_CHAN)
- ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
+ ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
else
- ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
+ ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
if (option_debug > 2)
ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features.flags);
@@ -1229,9 +1341,23 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
tmp = ast_strdupa(dynamic_features);
while ((tok = strsep(&tmp, "#"))) {
- AST_LIST_LOCK(&feature_list);
- if (!(feature = find_dynamic_feature(tok)))
+ AST_RWLIST_RDLOCK(&feature_groups);
+
+ fg = find_group(tok);
+
+ if (fg && (fge = find_group_exten(fg, code))) {
+ res = fge->feature->operation(chan, peer, config, code, sense);
+ AST_RWLIST_UNLOCK(&feature_groups);
+ continue;
+ }
+
+ AST_RWLIST_UNLOCK(&feature_groups);
+ AST_LIST_LOCK(&feature_list);
+
+ if(!(feature = find_dynamic_feature(tok))) {
+ AST_LIST_UNLOCK(&feature_list);
continue;
+ }
/* Feature is up for consideration */
if (!strcmp(feature->exten, code)) {
@@ -2454,11 +2580,22 @@ static int load_config(void)
{
int start = 0, end = 0;
int res;
+ int i;
struct ast_context *con = NULL;
struct ast_config *cfg = NULL;
struct ast_variable *var = NULL;
+ struct feature_group *fg = NULL;
char old_parking_ext[AST_MAX_EXTENSION];
char old_parking_con[AST_MAX_EXTENSION] = "";
+ char *ctg;
+ static const char *categories[] = {
+ /* Categories in features.conf that are not
+ * to be parsed as group categories
+ */
+ "general",
+ "featuremap",
+ "applicationmap"
+ };
if (!ast_strlen_zero(parking_con)) {
strcpy(old_parking_ext, parking_ext);
@@ -2671,7 +2808,41 @@ static int load_config(void)
if (option_verbose >= 1)
ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
- }
+ }
+
+ ast_unregister_groups();
+ AST_RWLIST_WRLOCK(&feature_groups);
+
+ ctg = NULL;
+ struct ast_call_feature *feature;
+ while ((ctg = ast_category_browse(cfg, ctg))) {
+ for (i = 0; i < ARRAY_LEN(categories); i++) {
+ if (!strcasecmp(categories[i], ctg))
+ break;
+ }
+
+ if (i < ARRAY_LEN(categories))
+ continue;
+
+ if (!(fg = register_group(ctg)))
+ continue;
+
+ for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
+ AST_LIST_LOCK(&feature_list);
+ if(!(feature = find_dynamic_feature(var->name)) &&
+ !(feature = ast_find_call_feature(var->name))) {
+ AST_LIST_UNLOCK(&feature_list);
+ ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
+ continue;
+ }
+ AST_LIST_UNLOCK(&feature_list);
+
+ register_group_feature(fg, var->value, feature);
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&feature_groups);
+
ast_config_destroy(cfg);
/* Remove the old parking extension */