2011-04-21 18:11:40 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011, Digium, Inc.
|
|
|
|
*
|
|
|
|
* David Vossel <dvossel@digium.com>
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file
|
|
|
|
*
|
|
|
|
* \brief ConfBridge config parser
|
|
|
|
*
|
|
|
|
* \author David Vossel <dvossel@digium.com>
|
|
|
|
*/
|
|
|
|
|
2012-06-15 16:20:16 +00:00
|
|
|
/*** MODULEINFO
|
|
|
|
<support_level>core</support_level>
|
|
|
|
***/
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
#include "asterisk/logger.h"
|
|
|
|
#include "asterisk/config.h"
|
2012-07-19 23:21:40 +00:00
|
|
|
#include "asterisk/config_options.h"
|
2011-04-21 18:11:40 +00:00
|
|
|
#include "include/confbridge.h"
|
|
|
|
#include "asterisk/astobj2.h"
|
|
|
|
#include "asterisk/cli.h"
|
|
|
|
#include "asterisk/bridging_features.h"
|
|
|
|
#include "asterisk/stringfields.h"
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
struct confbridge_cfg {
|
|
|
|
struct ao2_container *bridge_profiles;
|
|
|
|
struct ao2_container *user_profiles;
|
|
|
|
struct ao2_container *menus;
|
|
|
|
};
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static void *bridge_profile_alloc(const char *category);
|
|
|
|
static void *bridge_profile_find(struct ao2_container *container, const char *category);
|
|
|
|
static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static void bridge_profile_destructor(void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct bridge_profile *b_profile = obj;
|
|
|
|
ao2_cleanup(b_profile->sounds);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
static void *bridge_profile_alloc(const char *category)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct bridge_profile *b_profile;
|
|
|
|
|
|
|
|
if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
|
|
|
|
ao2_ref(b_profile, -1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
|
|
|
|
|
|
|
|
return b_profile;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
static void *bridge_profile_find(struct ao2_container *container, const char *category)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
return ao2_find(container, category, OBJ_KEY);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
static struct aco_type bridge_type = {
|
|
|
|
.type = ACO_ITEM,
|
|
|
|
.category_match = ACO_BLACKLIST,
|
|
|
|
.category = "^general$",
|
|
|
|
.matchfield = "type",
|
|
|
|
.matchvalue = "bridge",
|
|
|
|
.item_alloc = bridge_profile_alloc,
|
|
|
|
.item_find = bridge_profile_find,
|
|
|
|
.item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *user_profile_alloc(const char *category);
|
|
|
|
static void *user_profile_find(struct ao2_container *container, const char *category);
|
|
|
|
static void user_profile_destructor(void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
return;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static void *user_profile_alloc(const char *category)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct user_profile *u_profile;
|
|
|
|
|
|
|
|
if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
|
|
|
|
|
|
|
|
return u_profile;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
static void *user_profile_find(struct ao2_container *container, const char *category)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
return ao2_find(container, category, OBJ_KEY);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
static struct aco_type user_type = {
|
|
|
|
.type = ACO_ITEM,
|
|
|
|
.category_match = ACO_BLACKLIST,
|
|
|
|
.category = "^general$",
|
|
|
|
.matchfield = "type",
|
|
|
|
.matchvalue = "user",
|
|
|
|
.item_alloc = user_profile_alloc,
|
|
|
|
.item_find = user_profile_find,
|
|
|
|
.item_offset = offsetof(struct confbridge_cfg, user_profiles),
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *menu_alloc(const char *category);
|
|
|
|
static void *menu_find(struct ao2_container *container, const char *category);
|
|
|
|
static void menu_destructor(void *obj);
|
|
|
|
|
|
|
|
static void *menu_alloc(const char *category)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct conf_menu *menu;
|
|
|
|
if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ast_copy_string(menu->name, category, sizeof(menu->name));
|
|
|
|
return menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *menu_find(struct ao2_container *container, const char *category)
|
|
|
|
{
|
|
|
|
return ao2_find(container, category, OBJ_KEY);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct aco_type menu_type = {
|
|
|
|
.type = ACO_ITEM,
|
|
|
|
.category_match = ACO_BLACKLIST,
|
|
|
|
.category = "^general$",
|
|
|
|
.matchfield = "type",
|
|
|
|
.matchvalue = "menu",
|
|
|
|
.item_alloc = menu_alloc,
|
|
|
|
.item_find = menu_find,
|
|
|
|
.item_offset = offsetof(struct confbridge_cfg, menus),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Used to pass to aco_option_register */
|
|
|
|
static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
|
|
|
|
static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
|
|
|
|
static struct aco_type *user_types[] = ACO_TYPES(&user_type);
|
|
|
|
|
|
|
|
/* The general category is reserved, but unused */
|
|
|
|
static struct aco_type general_type = {
|
|
|
|
.type = ACO_GLOBAL,
|
|
|
|
.category_match = ACO_WHITELIST,
|
|
|
|
.category = "^general$",
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct aco_file confbridge_conf = {
|
|
|
|
.filename = "confbridge.conf",
|
|
|
|
.types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
|
|
|
|
};
|
|
|
|
|
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
|
|
|
|
|
|
|
|
static void *confbridge_cfg_alloc(void);
|
|
|
|
|
|
|
|
CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
|
|
|
|
.files = ACO_FILES(&confbridge_conf),
|
|
|
|
);
|
|
|
|
|
|
|
|
/*! bridge profile container functions */
|
|
|
|
static int bridge_cmp_cb(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
const struct bridge_profile *entry1 = obj, *entry2 = arg;
|
|
|
|
const char *name = arg;
|
|
|
|
return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
|
|
|
|
CMP_MATCH | CMP_STOP : 0;
|
|
|
|
}
|
|
|
|
static int bridge_hash_cb(const void *obj, const int flags)
|
|
|
|
{
|
|
|
|
const struct bridge_profile *b_profile = obj;
|
|
|
|
const char *name = obj;
|
|
|
|
return ast_str_case_hash(flags & OBJ_KEY ? name : b_profile->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! menu container functions */
|
|
|
|
static int menu_cmp_cb(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
const struct conf_menu *entry1 = obj, *entry2 = arg;
|
|
|
|
const char *name = arg;
|
|
|
|
return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
|
|
|
|
CMP_MATCH | CMP_STOP : 0;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
static int menu_hash_cb(const void *obj, const int flags)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
const struct conf_menu *menu = obj;
|
|
|
|
const char *name = obj;
|
|
|
|
return ast_str_case_hash(flags & OBJ_KEY ? name : menu->name);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
static void menu_destructor(void *obj)
|
|
|
|
{
|
|
|
|
struct conf_menu *menu = obj;
|
|
|
|
struct conf_menu_entry *entry = NULL;
|
|
|
|
|
|
|
|
while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
|
|
|
|
conf_menu_entry_destroy(entry);
|
|
|
|
ast_free(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! User profile container functions */
|
|
|
|
static int user_cmp_cb(void *obj, void *arg, int flags)
|
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
const struct user_profile *entry1 = obj, *entry2 = arg;
|
|
|
|
const char *name = arg;
|
|
|
|
return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
|
|
|
|
CMP_MATCH | CMP_STOP : 0;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
static int user_hash_cb(const void *obj, const int flags)
|
|
|
|
{
|
|
|
|
const struct user_profile *u_profile = obj;
|
2012-07-19 23:21:40 +00:00
|
|
|
const char *name = obj;
|
|
|
|
return ast_str_case_hash(flags & OBJ_KEY ? name : u_profile->name);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! Bridge Profile Sounds functions */
|
|
|
|
static void bridge_profile_sounds_destroy_cb(void *obj)
|
|
|
|
{
|
|
|
|
struct bridge_profile_sounds *sounds = obj;
|
|
|
|
ast_string_field_free_memory(sounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
|
|
|
|
{
|
|
|
|
struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
|
|
|
|
|
|
|
|
if (!sounds) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ast_string_field_init(sounds, 512)) {
|
|
|
|
ao2_ref(sounds, -1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sounds;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct bridge_profile_sounds *sounds = b_profile->sounds;
|
2011-04-21 18:11:40 +00:00
|
|
|
if (ast_strlen_zero(sound_file)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcasecmp(sound_name, "sound_only_person")) {
|
|
|
|
ast_string_field_set(sounds, onlyperson, sound_file);
|
2011-11-22 16:41:58 +00:00
|
|
|
} else if (!strcasecmp(sound_name, "sound_only_one")) {
|
|
|
|
ast_string_field_set(sounds, onlyone, sound_file);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else if (!strcasecmp(sound_name, "sound_has_joined")) {
|
|
|
|
ast_string_field_set(sounds, hasjoin, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_has_left")) {
|
|
|
|
ast_string_field_set(sounds, hasleft, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_kicked")) {
|
|
|
|
ast_string_field_set(sounds, kicked, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_muted")) {
|
|
|
|
ast_string_field_set(sounds, muted, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_unmuted")) {
|
|
|
|
ast_string_field_set(sounds, unmuted, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_there_are")) {
|
|
|
|
ast_string_field_set(sounds, thereare, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_other_in_party")) {
|
|
|
|
ast_string_field_set(sounds, otherinparty, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
|
|
|
|
ast_string_field_set(sounds, placeintoconf, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
|
|
|
|
ast_string_field_set(sounds, waitforleader, sound_file);
|
2012-04-29 02:23:22 +00:00
|
|
|
} else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
|
|
|
|
ast_string_field_set(sounds, leaderhasleft, sound_file);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else if (!strcasecmp(sound_name, "sound_get_pin")) {
|
|
|
|
ast_string_field_set(sounds, getpin, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
|
|
|
|
ast_string_field_set(sounds, invalidpin, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_locked")) {
|
|
|
|
ast_string_field_set(sounds, locked, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
|
|
|
|
ast_string_field_set(sounds, unlockednow, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_locked_now")) {
|
|
|
|
ast_string_field_set(sounds, lockednow, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_error_menu")) {
|
|
|
|
ast_string_field_set(sounds, errormenu, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_join")) {
|
|
|
|
ast_string_field_set(sounds, join, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_leave")) {
|
|
|
|
ast_string_field_set(sounds, leave, sound_file);
|
2011-11-17 18:09:13 +00:00
|
|
|
} else if (!strcasecmp(sound_name, "sound_participants_muted")) {
|
|
|
|
ast_string_field_set(sounds, participantsmuted, sound_file);
|
|
|
|
} else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
|
|
|
|
ast_string_field_set(sounds, participantsunmuted, sound_file);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! CONFBRIDGE dialplan function functions and channel datastore. */
|
|
|
|
struct func_confbridge_data {
|
|
|
|
struct bridge_profile b_profile;
|
|
|
|
struct user_profile u_profile;
|
|
|
|
unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
|
|
|
|
unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
|
|
|
|
};
|
|
|
|
static void func_confbridge_destroy_cb(void *data)
|
|
|
|
{
|
|
|
|
struct func_confbridge_data *b_data = data;
|
|
|
|
conf_bridge_profile_destroy(&b_data->b_profile);
|
|
|
|
ast_free(b_data);
|
|
|
|
};
|
|
|
|
static const struct ast_datastore_info confbridge_datastore = {
|
|
|
|
.type = "confbridge",
|
|
|
|
.destroy = func_confbridge_destroy_cb
|
|
|
|
};
|
|
|
|
int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
|
|
|
|
{
|
2012-12-05 01:11:26 +00:00
|
|
|
struct ast_datastore *datastore;
|
|
|
|
struct func_confbridge_data *b_data;
|
|
|
|
char *parse;
|
2012-07-19 23:21:40 +00:00
|
|
|
struct ast_variable tmpvar = { 0, };
|
2011-04-21 18:11:40 +00:00
|
|
|
AST_DECLARE_APP_ARGS(args,
|
|
|
|
AST_APP_ARG(type);
|
|
|
|
AST_APP_ARG(option);
|
|
|
|
);
|
|
|
|
|
|
|
|
/* parse all the required arguments and make sure they exist. */
|
2012-12-05 01:11:26 +00:00
|
|
|
if (ast_strlen_zero(data)) {
|
2011-04-21 18:11:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
parse = ast_strdupa(data);
|
|
|
|
AST_STANDARD_APP_ARGS(args, parse);
|
|
|
|
if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
2012-12-05 01:11:26 +00:00
|
|
|
datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
|
|
|
|
if (!datastore) {
|
|
|
|
datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
|
|
|
|
if (!datastore) {
|
|
|
|
ast_channel_unlock(chan);
|
2011-04-21 18:11:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-12-05 01:11:26 +00:00
|
|
|
b_data = ast_calloc(1, sizeof(*b_data));
|
|
|
|
if (!b_data) {
|
|
|
|
ast_channel_unlock(chan);
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_datastore_free(datastore);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-12-05 01:11:26 +00:00
|
|
|
b_data->b_profile.sounds = bridge_profile_sounds_alloc();
|
|
|
|
if (!b_data->b_profile.sounds) {
|
|
|
|
ast_channel_unlock(chan);
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_datastore_free(datastore);
|
|
|
|
ast_free(b_data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
datastore->data = b_data;
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_channel_datastore_add(chan, datastore);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else {
|
|
|
|
b_data = datastore->data;
|
|
|
|
}
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_channel_unlock(chan);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-12-05 01:11:26 +00:00
|
|
|
/* SET(CONFBRIDGE(type,option)=value) */
|
|
|
|
if (!value) {
|
|
|
|
value = "";
|
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
tmpvar.name = args.option;
|
|
|
|
tmpvar.value = value;
|
|
|
|
tmpvar.file = "CONFBRIDGE";
|
2012-12-05 01:11:26 +00:00
|
|
|
if (!strcasecmp(args.type, "bridge")) {
|
|
|
|
if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
|
|
|
|
b_data->b_usable = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp(args.type, "user")) {
|
|
|
|
if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
|
|
|
|
b_data->u_usable = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
|
|
|
|
cmd, args.type, args.option, value);
|
2011-04-21 18:11:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
|
|
|
|
{
|
|
|
|
struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
|
|
|
|
|
|
|
|
if (!menu_action) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
menu_action->id = id;
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
case MENU_ACTION_NOOP:
|
|
|
|
case MENU_ACTION_TOGGLE_MUTE:
|
|
|
|
case MENU_ACTION_INCREASE_LISTENING:
|
|
|
|
case MENU_ACTION_DECREASE_LISTENING:
|
|
|
|
case MENU_ACTION_INCREASE_TALKING:
|
|
|
|
case MENU_ACTION_DECREASE_TALKING:
|
|
|
|
case MENU_ACTION_RESET_LISTENING:
|
|
|
|
case MENU_ACTION_RESET_TALKING:
|
|
|
|
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
|
2011-11-17 18:09:13 +00:00
|
|
|
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
|
|
|
|
case MENU_ACTION_PARTICIPANT_COUNT:
|
2011-04-21 18:11:40 +00:00
|
|
|
case MENU_ACTION_ADMIN_KICK_LAST:
|
|
|
|
case MENU_ACTION_LEAVE:
|
2011-06-30 20:33:15 +00:00
|
|
|
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
|
2011-07-07 17:24:57 +00:00
|
|
|
case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
|
2011-04-21 18:11:40 +00:00
|
|
|
break;
|
|
|
|
case MENU_ACTION_PLAYBACK:
|
|
|
|
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
|
|
|
|
if (!(ast_strlen_zero(databuf))) {
|
|
|
|
ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
|
|
|
|
} else {
|
|
|
|
ast_free(menu_action);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_DIALPLAN_EXEC:
|
|
|
|
if (!(ast_strlen_zero(databuf))) {
|
|
|
|
AST_DECLARE_APP_ARGS(args,
|
|
|
|
AST_APP_ARG(context);
|
|
|
|
AST_APP_ARG(exten);
|
|
|
|
AST_APP_ARG(priority);
|
|
|
|
);
|
|
|
|
AST_STANDARD_APP_ARGS(args, databuf);
|
|
|
|
if (!ast_strlen_zero(args.context)) {
|
|
|
|
ast_copy_string(menu_action->data.dialplan_args.context,
|
|
|
|
args.context,
|
|
|
|
sizeof(menu_action->data.dialplan_args.context));
|
|
|
|
}
|
|
|
|
if (!ast_strlen_zero(args.exten)) {
|
|
|
|
ast_copy_string(menu_action->data.dialplan_args.exten,
|
|
|
|
args.exten,
|
|
|
|
sizeof(menu_action->data.dialplan_args.exten));
|
|
|
|
}
|
|
|
|
menu_action->data.dialplan_args.priority = 1; /* 1 by default */
|
|
|
|
if (!ast_strlen_zero(args.priority) &&
|
|
|
|
(sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
|
|
|
|
/* invalid priority */
|
|
|
|
ast_free(menu_action);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ast_free(menu_action);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
|
|
|
|
{
|
2011-06-13 20:44:59 +00:00
|
|
|
struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
|
2011-04-21 18:11:40 +00:00
|
|
|
int res = 0;
|
|
|
|
char *tmp_action_names = ast_strdupa(action_names);
|
|
|
|
char *action = NULL;
|
|
|
|
char *action_args;
|
|
|
|
char *tmp;
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
char *delimiter = ",";
|
|
|
|
|
|
|
|
if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
char *comma;
|
|
|
|
char *startbrace;
|
|
|
|
char *endbrace;
|
|
|
|
unsigned int action_len;
|
|
|
|
|
|
|
|
if (ast_strlen_zero(tmp_action_names)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
startbrace = strchr(tmp_action_names, '(');
|
|
|
|
endbrace = strchr(tmp_action_names, ')');
|
|
|
|
comma = strchr(tmp_action_names, ',');
|
|
|
|
|
|
|
|
/* If the next action has brackets with comma delimited arguments in it,
|
|
|
|
* make the delimeter ')' instead of a comma to preserve the argments */
|
|
|
|
if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
|
|
|
|
delimiter = ")";
|
|
|
|
} else {
|
|
|
|
delimiter = ",";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(action = strsep(&tmp_action_names, delimiter))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
action = ast_strip(action);
|
|
|
|
if (ast_strlen_zero(action)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
action_len = strlen(action);
|
|
|
|
ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
|
|
|
|
if (!strcasecmp(action, "toggle_mute")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
|
|
|
|
} else if (!strcasecmp(action, "no_op")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
|
|
|
|
} else if (!strcasecmp(action, "increase_listening_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "decrease_listening_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "increase_talking_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "reset_listening_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "reset_talking_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "decrease_talking_volume")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
|
|
|
|
} else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
|
2011-11-17 18:09:13 +00:00
|
|
|
} else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
|
|
|
|
} else if (!strcasecmp(action, "participant_count")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else if (!strcasecmp(action, "admin_kick_last")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
|
|
|
|
} else if (!strcasecmp(action, "leave_conference")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
|
2011-06-30 20:33:15 +00:00
|
|
|
} else if (!strcasecmp(action, "set_as_single_video_src")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
|
2011-07-07 17:24:57 +00:00
|
|
|
} else if (!strcasecmp(action, "release_as_single_video_src")) {
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
|
2011-04-21 18:11:40 +00:00
|
|
|
} else if (!strncasecmp(action, "dialplan_exec(", 14)) {
|
|
|
|
ast_copy_string(buf, action, sizeof(buf));
|
|
|
|
action_args = buf;
|
|
|
|
if ((action_args = strchr(action, '('))) {
|
|
|
|
action_args++;
|
|
|
|
}
|
|
|
|
/* it is possible that this argument may or may not
|
|
|
|
* have a closing brace at this point, it all depends on if
|
|
|
|
* comma delimited arguments were provided */
|
|
|
|
if ((tmp = strchr(action, ')'))) {
|
|
|
|
*tmp = '\0';
|
|
|
|
}
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
|
|
|
|
} else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
|
|
|
|
ast_copy_string(buf, action, sizeof(buf));
|
|
|
|
action_args = buf;
|
|
|
|
if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
|
|
|
|
*tmp = '\0';
|
|
|
|
action_args++;
|
|
|
|
}
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
|
|
|
|
} else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
|
|
|
|
ast_copy_string(buf, action, sizeof(buf));
|
|
|
|
action_args = buf;
|
|
|
|
if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
|
|
|
|
*tmp = '\0';
|
|
|
|
action_args++;
|
|
|
|
}
|
|
|
|
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if adding any of the actions failed, bail */
|
|
|
|
if (res) {
|
2012-03-08 17:02:52 +00:00
|
|
|
struct conf_menu_action *menu_action;
|
|
|
|
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
|
|
|
ast_free(menu_action);
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
ast_free(menu_entry);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-06-13 20:44:59 +00:00
|
|
|
/* remove any list entry with an identical DTMF sequence for overrides */
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
|
|
|
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
|
|
|
AST_LIST_REMOVE_CURRENT(entry);
|
|
|
|
ast_free(cur);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
|
|
|
|
{
|
|
|
|
int which = 0;
|
|
|
|
char *res = NULL;
|
|
|
|
int wordlen = strlen(word);
|
|
|
|
struct ao2_iterator i;
|
|
|
|
struct user_profile *u_profile = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
i = ao2_iterator_init(cfg->user_profiles, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((u_profile = ao2_iterator_next(&i))) {
|
|
|
|
if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
|
|
|
|
res = ast_strdup(u_profile->name);
|
|
|
|
ao2_ref(u_profile, -1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ao2_ref(u_profile, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&i);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
struct ao2_iterator it;
|
|
|
|
struct user_profile *u_profile;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show profile users";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show profile users\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"--------- User Profiles -----------\n");
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_lock(cfg->user_profiles);
|
|
|
|
it = ao2_iterator_init(cfg->user_profiles, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((u_profile = ao2_iterator_next(&it))) {
|
|
|
|
ast_cli(a->fd,"%s\n", u_profile->name);
|
|
|
|
ao2_ref(u_profile, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&it);
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_unlock(cfg->user_profiles);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
struct user_profile u_profile;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show profile user";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show profile user [<profile name>]\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
if (a->pos == 4) {
|
|
|
|
return complete_user_profile_name(a->line, a->word, a->pos, a->n);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 5) {
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
|
|
|
|
ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_cli(a->fd,"--------------------------------------------\n");
|
|
|
|
ast_cli(a->fd,"Name: %s\n",
|
|
|
|
u_profile.name);
|
|
|
|
ast_cli(a->fd,"Admin: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_ADMIN ?
|
|
|
|
"true" : "false");
|
|
|
|
ast_cli(a->fd,"Marked User: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_MARKEDUSER ?
|
|
|
|
"true" : "false");
|
|
|
|
ast_cli(a->fd,"Start Muted: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_STARTMUTED?
|
|
|
|
"true" : "false");
|
|
|
|
ast_cli(a->fd,"MOH When Empty: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_MUSICONHOLD ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"MOH Class: %s\n",
|
|
|
|
ast_strlen_zero(u_profile.moh_class) ?
|
|
|
|
"default" : u_profile.moh_class);
|
2012-03-10 20:06:46 +00:00
|
|
|
ast_cli(a->fd,"Announcement: %s\n",
|
|
|
|
u_profile.announcement);
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"Quiet: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_QUIET ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Wait Marked: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_WAITMARKED ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"END Marked: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_ENDMARKED ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Drop_silence: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Silence Threshold: %dms\n",
|
|
|
|
u_profile.silence_threshold);
|
|
|
|
ast_cli(a->fd,"Talking Threshold: %dms\n",
|
|
|
|
u_profile.talking_threshold);
|
|
|
|
ast_cli(a->fd,"Denoise: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_DENOISE ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Jitterbuffer: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_JITTERBUFFER ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Talk Detect Events: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_TALKER_DETECT ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"DTMF Pass Through: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_DTMF_PASS ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"PIN: %s\n",
|
|
|
|
ast_strlen_zero(u_profile.pin) ?
|
|
|
|
"None" : u_profile.pin);
|
|
|
|
ast_cli(a->fd,"Announce User Count: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Announce join/leave: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"Announce User Count all: %s\n",
|
|
|
|
u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
ast_cli(a->fd,"\n");
|
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
|
|
|
|
{
|
|
|
|
int which = 0;
|
|
|
|
char *res = NULL;
|
|
|
|
int wordlen = strlen(word);
|
|
|
|
struct ao2_iterator i;
|
|
|
|
struct bridge_profile *b_profile = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
i = ao2_iterator_init(cfg->bridge_profiles, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((b_profile = ao2_iterator_next(&i))) {
|
|
|
|
if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
|
|
|
|
res = ast_strdup(b_profile->name);
|
|
|
|
ao2_ref(b_profile, -1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ao2_ref(b_profile, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&i);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
struct ao2_iterator it;
|
|
|
|
struct bridge_profile *b_profile;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show profile bridges";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show profile bridges\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_lock(cfg->bridge_profiles);
|
|
|
|
it = ao2_iterator_init(cfg->bridge_profiles, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((b_profile = ao2_iterator_next(&it))) {
|
|
|
|
ast_cli(a->fd,"%s\n", b_profile->name);
|
|
|
|
ao2_ref(b_profile, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&it);
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_unlock(cfg->bridge_profiles);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
struct bridge_profile b_profile;
|
|
|
|
char tmp[64];
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show profile bridge";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show profile bridge <profile name>\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
if (a->pos == 4) {
|
|
|
|
return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 5) {
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
|
|
|
|
ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_cli(a->fd,"--------------------------------------------\n");
|
|
|
|
ast_cli(a->fd,"Name: %s\n", b_profile.name);
|
|
|
|
|
|
|
|
if (b_profile.internal_sample_rate) {
|
|
|
|
snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
|
|
|
|
} else {
|
|
|
|
ast_copy_string(tmp, "auto", sizeof(tmp));
|
|
|
|
}
|
|
|
|
ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
|
|
|
|
|
|
|
|
if (b_profile.mix_interval) {
|
|
|
|
ast_cli(a->fd,"Mixing Interval: %d\n", b_profile.mix_interval);
|
|
|
|
} else {
|
|
|
|
ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_cli(a->fd,"Record Conference: %s\n",
|
|
|
|
b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
|
|
|
|
"yes" : "no");
|
|
|
|
|
|
|
|
ast_cli(a->fd,"Record File: %s\n",
|
|
|
|
ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
|
|
|
|
b_profile.rec_file);
|
|
|
|
|
|
|
|
if (b_profile.max_members) {
|
|
|
|
ast_cli(a->fd,"Max Members: %d\n", b_profile.max_members);
|
|
|
|
} else {
|
|
|
|
ast_cli(a->fd,"Max Members: No Limit\n");
|
|
|
|
}
|
|
|
|
|
2012-12-05 01:11:26 +00:00
|
|
|
switch (b_profile.flags
|
|
|
|
& (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
|
|
|
|
case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
|
2011-06-30 20:33:15 +00:00
|
|
|
ast_cli(a->fd, "Video Mode: last_marked\n");
|
2012-12-05 01:11:26 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
|
2011-06-30 20:33:15 +00:00
|
|
|
ast_cli(a->fd, "Video Mode: first_marked\n");
|
2012-12-05 01:11:26 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
|
2011-06-30 20:33:15 +00:00
|
|
|
ast_cli(a->fd, "Video Mode: follow_talker\n");
|
2012-12-05 01:11:26 +00:00
|
|
|
break;
|
|
|
|
case 0:
|
2011-06-30 20:33:15 +00:00
|
|
|
ast_cli(a->fd, "Video Mode: no video\n");
|
2012-12-05 01:11:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Opps. We have more than one video mode flag set. */
|
|
|
|
ast_assert(0);
|
|
|
|
break;
|
2011-06-30 20:33:15 +00:00
|
|
|
}
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
|
2012-04-29 02:23:22 +00:00
|
|
|
ast_cli(a->fd,"sound_leader_has_left: %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
|
2011-11-17 18:09:13 +00:00
|
|
|
ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
|
|
|
|
ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"\n");
|
|
|
|
|
|
|
|
conf_bridge_profile_destroy(&b_profile);
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *complete_menu_name(const char *line, const char *word, int pos, int state)
|
|
|
|
{
|
|
|
|
int which = 0;
|
|
|
|
char *res = NULL;
|
|
|
|
int wordlen = strlen(word);
|
|
|
|
struct ao2_iterator i;
|
|
|
|
struct conf_menu *menu = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
i = ao2_iterator_init(cfg->menus, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((menu = ao2_iterator_next(&i))) {
|
|
|
|
if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
|
|
|
|
res = ast_strdup(menu->name);
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&i);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
struct ao2_iterator it;
|
|
|
|
struct conf_menu *menu;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show menus";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show profile menus\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd,"--------- Menus -----------\n");
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_lock(cfg->menus);
|
|
|
|
it = ao2_iterator_init(cfg->menus, 0);
|
2011-04-21 18:11:40 +00:00
|
|
|
while ((menu = ao2_iterator_next(&it))) {
|
|
|
|
ast_cli(a->fd,"%s\n", menu->name);
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&it);
|
2012-07-19 23:21:40 +00:00
|
|
|
ao2_unlock(cfg->menus);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
|
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
|
2011-04-21 18:11:40 +00:00
|
|
|
struct conf_menu_entry *menu_entry = NULL;
|
|
|
|
struct conf_menu_action *menu_action = NULL;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "confbridge show menu";
|
|
|
|
e->usage =
|
|
|
|
"Usage confbridge show menu [<menu name>]\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
if (a->pos == 3) {
|
|
|
|
return complete_menu_name(a->line, a->word, a->pos, a->n);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 4) {
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
|
2011-04-21 18:11:40 +00:00
|
|
|
ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
ao2_lock(menu);
|
|
|
|
|
|
|
|
ast_cli(a->fd,"Name: %s\n", menu->name);
|
|
|
|
AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
|
|
|
|
int action_num = 0;
|
|
|
|
ast_cli(a->fd, "%s=", menu_entry->dtmf);
|
|
|
|
AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
|
|
|
|
if (action_num) {
|
|
|
|
ast_cli(a->fd, ", ");
|
|
|
|
}
|
|
|
|
switch (menu_action->id) {
|
|
|
|
case MENU_ACTION_TOGGLE_MUTE:
|
|
|
|
ast_cli(a->fd, "toggle_mute");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_NOOP:
|
|
|
|
ast_cli(a->fd, "no_op");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_INCREASE_LISTENING:
|
|
|
|
ast_cli(a->fd, "increase_listening_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_DECREASE_LISTENING:
|
|
|
|
ast_cli(a->fd, "decrease_listening_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_RESET_LISTENING:
|
|
|
|
ast_cli(a->fd, "reset_listening_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_RESET_TALKING:
|
|
|
|
ast_cli(a->fd, "reset_talking_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_INCREASE_TALKING:
|
|
|
|
ast_cli(a->fd, "increase_talking_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_DECREASE_TALKING:
|
|
|
|
ast_cli(a->fd, "decrease_talking_volume");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_PLAYBACK:
|
|
|
|
ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
|
|
|
|
ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_DIALPLAN_EXEC:
|
|
|
|
ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
|
|
|
|
menu_action->data.dialplan_args.context,
|
|
|
|
menu_action->data.dialplan_args.exten,
|
|
|
|
menu_action->data.dialplan_args.priority);
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
|
|
|
|
ast_cli(a->fd, "admin_toggle_conference_lock");
|
|
|
|
break;
|
2011-11-17 18:09:13 +00:00
|
|
|
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
|
|
|
|
ast_cli(a->fd, "admin_toggle_mute_participants");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_PARTICIPANT_COUNT:
|
|
|
|
ast_cli(a->fd, "participant_count");
|
|
|
|
break;
|
2011-04-21 18:11:40 +00:00
|
|
|
case MENU_ACTION_ADMIN_KICK_LAST:
|
|
|
|
ast_cli(a->fd, "admin_kick_last");
|
|
|
|
break;
|
|
|
|
case MENU_ACTION_LEAVE:
|
|
|
|
ast_cli(a->fd, "leave_conference");
|
|
|
|
break;
|
2011-06-30 20:33:15 +00:00
|
|
|
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
|
|
|
|
ast_cli(a->fd, "set_as_single_video_src");
|
|
|
|
break;
|
2011-07-07 17:24:57 +00:00
|
|
|
case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
|
|
|
|
ast_cli(a->fd, "release_as_single_video_src");
|
|
|
|
break;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
action_num++;
|
|
|
|
}
|
|
|
|
ast_cli(a->fd,"\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ao2_unlock(menu);
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ast_cli_entry cli_confbridge_parser[] = {
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
|
|
|
|
AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static void confbridge_cfg_destructor(void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct confbridge_cfg *cfg = obj;
|
|
|
|
ao2_cleanup(cfg->user_profiles);
|
|
|
|
ao2_cleanup(cfg->bridge_profiles);
|
|
|
|
ao2_cleanup(cfg->menus);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *confbridge_cfg_alloc(void)
|
|
|
|
{
|
|
|
|
struct confbridge_cfg *cfg;
|
|
|
|
|
|
|
|
if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
|
|
|
|
return NULL;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
|
|
|
|
goto error;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
|
|
|
|
goto error;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
|
|
|
|
goto error;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
return cfg;
|
|
|
|
error:
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return NULL;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct user_profile *u_profile = obj;
|
|
|
|
|
|
|
|
if (strcasecmp(var->name, "announce_user_count_all")) {
|
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
if (ast_true(var->value)) {
|
|
|
|
u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
|
|
|
|
} else if (ast_false(var->value)) {
|
|
|
|
u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
|
|
|
|
} else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
|
|
|
|
u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
|
|
|
|
} else {
|
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
struct bridge_profile *b_profile = obj;
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (strcasecmp(var->name, "mixing_interval")) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
switch (b_profile->mix_interval) {
|
|
|
|
case 10:
|
|
|
|
case 20:
|
|
|
|
case 40:
|
|
|
|
case 80:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct bridge_profile *b_profile = obj;
|
|
|
|
|
|
|
|
if (strcasecmp(var->name, "video_mode")) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(var->value, "first_marked")) {
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_set_flags_to(b_profile,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
|
2012-07-19 23:21:40 +00:00
|
|
|
} else if (!strcasecmp(var->value, "last_marked")) {
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_set_flags_to(b_profile,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
|
2012-07-19 23:21:40 +00:00
|
|
|
} else if (!strcasecmp(var->value, "follow_talker")) {
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_set_flags_to(b_profile,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
|
Fix segfault introduced by conversion to ACO API
The value "none" is specified in the config file as a valid value for
the "video_mode" option. The code prior to the ACO conversion did not
check for "none", but just ignored it and relied on the default zero
value. The parsing with ACO is more strict, so without handling
"none" specifically, parsing would fail.
When parsing failed, but the module loaded anyway, the config info
would never be stored, and one place in the code did not check for
this case and would segfault. It was also possible that the
aco_info struct's internals would be destroyed and used as well.
This patch keeps the module from loading after parse failures, adds
the "none" option to "video_mode", registers CLI functions only
after parsing has completed, checks the config data for NULL before
accessing it, and returns -1 on some allocation failures when
initializing.
(closes issue ASTERISK-20159)
Reported by: Birger "WIMPy" Harzenetter
Tested by: Birger "WIMPy" Harzenetter
Patches:
confbridge_fix3.txt uploaded by Terry Wilson
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370341 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2012-07-21 13:25:26 +00:00
|
|
|
} else if (!strcasecmp(var->value, "none")) {
|
2012-12-05 01:11:26 +00:00
|
|
|
ast_clear_flag(b_profile,
|
|
|
|
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
|
|
|
|
| BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
|
2012-07-19 23:21:40 +00:00
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct user_profile *u_profile = obj;
|
|
|
|
|
|
|
|
return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2011-04-21 18:11:40 +00:00
|
|
|
{
|
2012-07-19 23:21:40 +00:00
|
|
|
struct bridge_profile *b_profile = obj;
|
|
|
|
struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
|
|
|
|
struct bridge_profile_sounds *oldsounds = b_profile->sounds;
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!sounds) {
|
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
|
|
|
|
ao2_ref(sounds, -1);
|
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
/* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
|
|
|
|
* structure of a dynamic profile will need to be altered, a completely new sounds structure must be
|
|
|
|
* created instead of simply holding a reference to the one built by the config file. */
|
|
|
|
ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
|
|
|
|
ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
|
|
|
|
ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
|
|
|
|
ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
|
|
|
|
ast_string_field_set(sounds, muted, b_profile->sounds->muted);
|
|
|
|
ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
|
|
|
|
ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
|
|
|
|
ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
|
|
|
|
ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
|
|
|
|
ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
|
|
|
|
ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
|
|
|
|
ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
|
|
|
|
ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
|
|
|
|
ast_string_field_set(sounds, locked, b_profile->sounds->locked);
|
|
|
|
ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
|
|
|
|
ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
|
|
|
|
ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
|
|
|
|
ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
|
|
|
|
ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
|
|
|
|
|
|
|
|
ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
|
2012-12-05 01:11:26 +00:00
|
|
|
ao2_ref(oldsounds, -1); /* original sounds struct we don't need anymore */
|
2012-07-19 23:21:40 +00:00
|
|
|
b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
set_sound(var->name, var->value, obj);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
add_menu_entry(obj, var->name, var->value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int conf_load_config(int reload)
|
|
|
|
{
|
|
|
|
if (!reload) {
|
|
|
|
if (aco_info_init(&cfg_info)) {
|
Fix segfault introduced by conversion to ACO API
The value "none" is specified in the config file as a valid value for
the "video_mode" option. The code prior to the ACO conversion did not
check for "none", but just ignored it and relied on the default zero
value. The parsing with ACO is more strict, so without handling
"none" specifically, parsing would fail.
When parsing failed, but the module loaded anyway, the config info
would never be stored, and one place in the code did not check for
this case and would segfault. It was also possible that the
aco_info struct's internals would be destroyed and used as well.
This patch keeps the module from loading after parse failures, adds
the "none" option to "video_mode", registers CLI functions only
after parsing has completed, checks the config data for NULL before
accessing it, and returns -1 on some allocation failures when
initializing.
(closes issue ASTERISK-20159)
Reported by: Birger "WIMPy" Harzenetter
Tested by: Birger "WIMPy" Harzenetter
Patches:
confbridge_fix3.txt uploaded by Terry Wilson
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370341 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2012-07-21 13:25:26 +00:00
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
/* User options */
|
|
|
|
aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
|
|
|
|
aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
|
|
|
|
aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
|
|
|
|
aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
|
|
|
|
aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
|
|
|
|
aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
|
|
|
|
aco_option_register(&cfg_info, "music_on_hold_when_empty", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MUSICONHOLD);
|
|
|
|
aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
|
|
|
|
aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
|
|
|
|
aco_option_register(&cfg_info, "announce_user_count", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCEUSERCOUNT);
|
|
|
|
/* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
|
|
|
|
aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON);
|
|
|
|
aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
|
|
|
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
|
|
|
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
|
|
|
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
|
|
|
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
|
|
|
aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
|
|
|
|
aco_option_register(&cfg_info, "music_on_hold_class", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, moh_class));
|
|
|
|
aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
|
|
|
|
aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
|
|
|
|
aco_option_register(&cfg_info, "dsp_drop_silence", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DROP_SILENCE);
|
|
|
|
aco_option_register(&cfg_info, "dsp_silence_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_SILENCE_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold));
|
|
|
|
aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold));
|
|
|
|
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
|
|
|
|
/* This option should only be used with the CONFBRIDGE dialplan function */
|
|
|
|
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
|
|
|
|
|
|
|
|
/* Bridge options */
|
|
|
|
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
|
|
|
|
/* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
|
|
|
|
aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
|
|
|
|
aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
|
|
|
|
aco_option_register(&cfg_info, "record_conference", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_CONFERENCE);
|
|
|
|
aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
|
|
|
|
aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
|
|
|
|
aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
|
|
|
|
aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
|
|
|
|
/* This option should only be used with the CONFBRIDGE dialplan function */
|
|
|
|
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
|
|
|
|
|
|
|
|
/* Menu options */
|
|
|
|
aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
|
|
|
|
|
Fix segfault introduced by conversion to ACO API
The value "none" is specified in the config file as a valid value for
the "video_mode" option. The code prior to the ACO conversion did not
check for "none", but just ignored it and relied on the default zero
value. The parsing with ACO is more strict, so without handling
"none" specifically, parsing would fail.
When parsing failed, but the module loaded anyway, the config info
would never be stored, and one place in the code did not check for
this case and would segfault. It was also possible that the
aco_info struct's internals would be destroyed and used as well.
This patch keeps the module from loading after parse failures, adds
the "none" option to "video_mode", registers CLI functions only
after parsing has completed, checks the config data for NULL before
accessing it, and returns -1 on some allocation failures when
initializing.
(closes issue ASTERISK-20159)
Reported by: Birger "WIMPy" Harzenetter
Tested by: Birger "WIMPy" Harzenetter
Patches:
confbridge_fix3.txt uploaded by Terry Wilson
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370341 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2012-07-21 13:25:26 +00:00
|
|
|
if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reload && ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-07-19 23:21:40 +00:00
|
|
|
error:
|
2012-10-01 16:26:23 +00:00
|
|
|
/* On a reload, just keep the config we already have in place. */
|
|
|
|
if (!reload) {
|
|
|
|
conf_destroy_config();
|
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
return -1;
|
2011-04-21 18:11:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
|
|
|
|
{
|
|
|
|
memcpy(dst, src, sizeof(*dst));
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
|
|
|
|
{
|
|
|
|
struct user_profile *tmp2;
|
|
|
|
struct ast_datastore *datastore = NULL;
|
|
|
|
struct func_confbridge_data *b_data = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
|
|
|
if (chan) {
|
|
|
|
ast_channel_lock(chan);
|
2012-12-05 01:11:26 +00:00
|
|
|
datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
if (datastore) {
|
2011-04-21 18:11:40 +00:00
|
|
|
b_data = datastore->data;
|
|
|
|
if (b_data->u_usable) {
|
|
|
|
conf_user_profile_copy(result, &b_data->u_profile);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_strlen_zero(user_profile_name)) {
|
|
|
|
user_profile_name = DEFAULT_USER_PROFILE;
|
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
|
2011-04-21 18:11:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ao2_lock(tmp2);
|
|
|
|
conf_user_profile_copy(result, tmp2);
|
|
|
|
ao2_unlock(tmp2);
|
|
|
|
ao2_ref(tmp2, -1);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
|
|
|
|
{
|
2012-12-07 00:00:39 +00:00
|
|
|
*dst = *src;
|
2011-04-21 18:11:40 +00:00
|
|
|
if (src->sounds) {
|
|
|
|
ao2_ref(src->sounds, +1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
|
|
|
|
{
|
|
|
|
if (b_profile->sounds) {
|
|
|
|
ao2_ref(b_profile->sounds, -1);
|
|
|
|
b_profile->sounds = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
|
|
|
|
{
|
|
|
|
struct bridge_profile *tmp2;
|
|
|
|
struct ast_datastore *datastore = NULL;
|
|
|
|
struct func_confbridge_data *b_data = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
2011-04-21 18:11:40 +00:00
|
|
|
|
Fix segfault introduced by conversion to ACO API
The value "none" is specified in the config file as a valid value for
the "video_mode" option. The code prior to the ACO conversion did not
check for "none", but just ignored it and relied on the default zero
value. The parsing with ACO is more strict, so without handling
"none" specifically, parsing would fail.
When parsing failed, but the module loaded anyway, the config info
would never be stored, and one place in the code did not check for
this case and would segfault. It was also possible that the
aco_info struct's internals would be destroyed and used as well.
This patch keeps the module from loading after parse failures, adds
the "none" option to "video_mode", registers CLI functions only
after parsing has completed, checks the config data for NULL before
accessing it, and returns -1 on some allocation failures when
initializing.
(closes issue ASTERISK-20159)
Reported by: Birger "WIMPy" Harzenetter
Tested by: Birger "WIMPy" Harzenetter
Patches:
confbridge_fix3.txt uploaded by Terry Wilson
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370341 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2012-07-21 13:25:26 +00:00
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:11:40 +00:00
|
|
|
if (chan) {
|
|
|
|
ast_channel_lock(chan);
|
2012-12-05 01:11:26 +00:00
|
|
|
datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
if (datastore) {
|
2011-04-21 18:11:40 +00:00
|
|
|
b_data = datastore->data;
|
|
|
|
if (b_data->b_usable) {
|
|
|
|
conf_bridge_profile_copy(result, &b_data->b_profile);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ast_strlen_zero(bridge_profile_name)) {
|
|
|
|
bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
|
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
|
2011-04-21 18:11:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ao2_lock(tmp2);
|
|
|
|
conf_bridge_profile_copy(result, tmp2);
|
|
|
|
ao2_unlock(tmp2);
|
|
|
|
ao2_ref(tmp2, -1);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dtmf_menu_hook_pvt {
|
|
|
|
struct conference_bridge_user *conference_bridge_user;
|
|
|
|
struct conf_menu_entry menu_entry;
|
|
|
|
struct conf_menu *menu;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void menu_hook_destroy(void *hook_pvt)
|
|
|
|
{
|
|
|
|
struct dtmf_menu_hook_pvt *pvt = hook_pvt;
|
|
|
|
struct conf_menu_action *action = NULL;
|
|
|
|
|
|
|
|
ao2_ref(pvt->menu, -1);
|
|
|
|
|
|
|
|
while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
|
|
|
|
ast_free(action);
|
|
|
|
}
|
|
|
|
ast_free(pvt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int menu_hook_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
|
|
|
{
|
|
|
|
struct dtmf_menu_hook_pvt *pvt = hook_pvt;
|
|
|
|
return conf_handle_dtmf(bridge_channel, pvt->conference_bridge_user, &pvt->menu_entry, pvt->menu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
|
|
|
|
{
|
|
|
|
struct conf_menu_action *menu_action = NULL;
|
|
|
|
struct conf_menu_action *new_menu_action = NULL;
|
|
|
|
|
|
|
|
memcpy(dst, src, sizeof(*dst));
|
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
|
|
|
|
|
|
|
|
AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
|
|
|
|
if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
|
|
|
|
AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
|
|
|
|
{
|
|
|
|
struct conf_menu_action *menu_action = NULL;
|
|
|
|
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
|
|
|
ast_free(menu_action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
|
|
|
|
{
|
|
|
|
struct conf_menu_entry *menu_entry = NULL;
|
|
|
|
|
|
|
|
ao2_lock(menu);
|
|
|
|
AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
|
|
|
|
if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
|
|
|
|
copy_menu_entry(result, menu_entry);
|
|
|
|
ao2_unlock(menu);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_unlock(menu);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user)
|
|
|
|
{
|
|
|
|
struct conf_menu *menu;
|
|
|
|
struct conf_menu_entry *menu_entry = NULL;
|
2012-07-19 23:21:40 +00:00
|
|
|
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return -1;
|
|
|
|
}
|
2011-04-21 18:11:40 +00:00
|
|
|
|
2012-07-19 23:21:40 +00:00
|
|
|
if (!(menu = menu_find(cfg->menus, menu_name))) {
|
2011-04-21 18:11:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ao2_lock(menu);
|
|
|
|
AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
|
|
|
|
struct dtmf_menu_hook_pvt *pvt;
|
|
|
|
if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
|
|
|
|
ao2_unlock(menu);
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
|
|
|
|
ast_free(pvt);
|
|
|
|
ao2_unlock(menu);
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pvt->conference_bridge_user = conference_bridge_user;
|
|
|
|
ao2_ref(menu, +1);
|
|
|
|
pvt->menu = menu;
|
|
|
|
|
|
|
|
ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_unlock(menu);
|
|
|
|
ao2_ref(menu, -1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-19 23:21:40 +00:00
|
|
|
|
|
|
|
void conf_destroy_config(void)
|
|
|
|
{
|
|
|
|
ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
|
|
|
|
aco_info_destroy(&cfg_info);
|
|
|
|
ao2_global_obj_release(cfg_handle);
|
|
|
|
}
|