Author: knhor Date: Wed Nov 22 20:40:14 2006 New Revision: 3440 Modified: freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c Log: mostly complete support or ivr menus with caller control callbacks Modified: freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c ============================================================================== --- freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c (original) +++ freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c Wed Nov 22 20:40:14 2006 @@ -56,12 +56,23 @@ FILE_STOP_ALL } file_stop_t; +/* Global Values */ +static struct { + switch_memory_pool_t *conference_pool; + switch_mutex_t *conference_mutex; + switch_hash_t *conference_hash; + switch_mutex_t *id_mutex; + switch_mutex_t *hash_mutex; + uint32_t id_pool; + int32_t running; + uint32_t threads; +} globals; + typedef enum { - CALLER_CONTROL_NOEVENT, CALLER_CONTROL_MUTE, CALLER_CONTROL_DEAF_MUTE, CALLER_CONTROL_ENERGY_UP, - CALLER_CONTROL_ENERGY_EQU_CONFERENCE, + CALLER_CONTROL_ENERGY_EQU_CONF, CALLER_CONTROL_ENERGEY_DN, CALLER_CONTROL_VOL_TALK_UP, CALLER_CONTROL_VOL_TALK_ZERO, @@ -70,44 +81,23 @@ CALLER_CONTROL_VOL_LISTEN_ZERO, CALLER_CONTROL_VOL_LISTEN_DN, CALLER_CONTROL_HANGUP, + CALLER_CONTROL_MENU, } caller_control_t; -static struct _caller_control_actions_struct { - char *key; - char *default_digits; - caller_control_t action; -} caller_control_actions[] = { - {"mute", "0", CALLER_CONTROL_MUTE}, - {"deaf mute", "*", CALLER_CONTROL_DEAF_MUTE}, - {"energy up", "9", CALLER_CONTROL_ENERGY_UP}, - {"energy equ", "8", CALLER_CONTROL_ENERGY_EQU_CONFERENCE}, - {"energy dn", "7", CALLER_CONTROL_ENERGEY_DN}, - {"vol talk up", "3", CALLER_CONTROL_VOL_TALK_UP}, - {"vol talk zero", "2", CALLER_CONTROL_VOL_TALK_ZERO}, - {"vol talk dn", "1", CALLER_CONTROL_VOL_TALK_DN}, - {"vol listen up", "6", CALLER_CONTROL_VOL_LISTEN_UP}, - {"vol listen zero", "5", CALLER_CONTROL_VOL_LISTEN_ZERO}, - {"vol listen dn", "4", CALLER_CONTROL_VOL_LISTEN_DN}, - {"hangup", "#", CALLER_CONTROL_HANGUP}, -}; -#define CALLER_CONTROL_ACTIONS_QTY (sizeof(caller_control_actions)/sizeof(caller_control_actions[0])) - -/* Global Values */ -static struct { - switch_memory_pool_t *conference_pool; - switch_mutex_t *conference_mutex; - switch_hash_t *conference_hash; - switch_mutex_t *id_mutex; - switch_mutex_t *hash_mutex; - uint32_t id_pool; - int32_t running; - uint32_t threads; -} globals; - -// forward declaration for conference_obj structure +// forward declaration for conference_obj and caller_control struct conference_member; typedef struct conference_member conference_member_t; +typedef struct caller_control_actions { + void (*handler)(conference_member_t *, void *); + void *data; +} caller_control_action_t; + +typedef struct caller_control_menu_info { + switch_ivr_menu_t *stack; + char *name; +} caller_control_menu_info_t; + typedef enum { MFLAG_RUNNING = (1 << 0), MFLAG_CAN_SPEAK = (1 << 1), @@ -117,7 +107,6 @@ MFLAG_NOCHANNEL = (1 << 5) } member_flag_t; - typedef enum { CFLAG_RUNNING = (1 << 0), CFLAG_DYNAMIC = (1 << 1), @@ -137,7 +126,7 @@ NODE_TYPE_SPEECH } node_type_t; -struct confernce_file_node { +typedef struct confernce_file_node { switch_file_handle_t fh; switch_speech_handle_t sh; node_type_t type; @@ -145,12 +134,10 @@ switch_memory_pool_t *pool; uint32_t leadin; struct confernce_file_node *next; -}; +} confernce_file_node_t; -typedef struct confernce_file_node confernce_file_node_t; - /* Conference Object */ -struct conference_obj { +typedef struct conference_obj { char *name; char *timer_name; char *tts_engine; @@ -186,16 +173,14 @@ uint32_t count; int32_t energy_level; uint8_t min; -}; -typedef struct conference_obj conference_obj_t; +} conference_obj_t; /* Relationship with another member */ -struct conference_relationship { +typedef struct conference_relationship { uint32_t id; uint32_t flags; struct conference_relationship *next; -}; -typedef struct conference_relationship conference_relationship_t; +} conference_relationship_t; /* Conference Member Object */ struct conference_member { @@ -231,12 +216,11 @@ }; /* Record Node */ -struct conference_record { +typedef struct conference_record { conference_obj_t *conference; char *path; switch_memory_pool_t *pool; -}; -typedef struct conference_record conference_record_t; +} conference_record_t; // API command parser typedef struct api_command { @@ -272,7 +256,6 @@ static void conference_function(switch_core_session_t *session, char *data); static void launch_conference_thread(conference_obj_t *conference); static void *SWITCH_THREAD_FUNC input_thread_run(switch_thread_t *thread, void *obj); -static void launch_input_thread(conference_member_t *member, switch_memory_pool_t *pool); static switch_status_t conference_local_play_file(switch_core_session_t *session, char *path, uint32_t leadin, char *buf, switch_size_t len); static switch_status_t conference_member_play_file(conference_member_t *member, char *file, uint32_t leadin); static switch_status_t conference_member_say(conference_obj_t *conference, conference_member_t *member, char *text, uint32_t leadin); @@ -807,7 +790,7 @@ return NULL; } -static void conference_loop_fn_mute_toggle(conference_member_t *member) +static void conference_loop_fn_mute_toggle(conference_member_t *member, void *data) { if (switch_test_flag(member, MFLAG_CAN_SPEAK)) { conf_api_sub_mute(member, NULL, NULL); @@ -817,7 +800,7 @@ } } -static void conference_loop_fn_deafmute_toggle(conference_member_t *member) +static void conference_loop_fn_deafmute_toggle(conference_member_t *member, void *data) { if (switch_test_flag(member, MFLAG_CAN_SPEAK)) { conf_api_sub_mute(member, NULL, NULL); @@ -828,7 +811,7 @@ } } -static void conference_loop_fn_energy_up(conference_member_t *member) +static void conference_loop_fn_energy_up(conference_member_t *member, void *data) { char msg[512]; @@ -842,7 +825,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_energy_equ_conference(conference_member_t *member) +static void conference_loop_fn_energy_equ_conf(conference_member_t *member, void *data) { char msg[512]; @@ -853,7 +836,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_energy_dn(conference_member_t *member) +static void conference_loop_fn_energy_dn(conference_member_t *member, void *data) { char msg[512]; @@ -867,7 +850,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_talk_up(conference_member_t *member) +static void conference_loop_fn_volume_talk_up(conference_member_t *member, void *data) { char msg[512]; @@ -879,7 +862,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_talk_zero(conference_member_t *member) +static void conference_loop_fn_volume_talk_zero(conference_member_t *member, void *data) { char msg[512]; @@ -890,7 +873,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_talk_dn(conference_member_t *member) +static void conference_loop_fn_volume_talk_dn(conference_member_t *member, void *data) { char msg[512]; @@ -902,7 +885,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_listen_up(conference_member_t *member) +static void conference_loop_fn_volume_listen_up(conference_member_t *member, void *data) { char msg[512]; @@ -914,7 +897,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_listen_zero(conference_member_t *member) +static void conference_loop_fn_volume_listen_zero(conference_member_t *member, void *data) { char msg[512]; @@ -925,7 +908,7 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_volume_listen_dn(conference_member_t *member) +static void conference_loop_fn_volume_listen_dn(conference_member_t *member, void *data) { char msg[512]; @@ -937,11 +920,34 @@ conference_member_say(member->conference, member, msg, 0); } -static void conference_loop_fn_hangup(conference_member_t *member) +static void conference_loop_fn_hangup(conference_member_t *member, void *data) { switch_clear_flag_locked(member, MFLAG_RUNNING); } +typedef struct caller_control_menu_ctx { + switch_ivr_menu_xml_ctx_t *xml_ctx; + switch_ivr_menu_t *menu_stack; + char *name; +} caller_control_menu_ctx_t; + +static void conference_loop_fn_menu(conference_member_t *member, void *data) +{ + if (data != NULL) { + caller_control_menu_ctx_t *menu_ctx = (caller_control_menu_ctx_t *)data; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "conference_loop_fn_menu handler '%s'\n",menu_ctx->name); + if (menu_ctx->menu_stack != NULL && menu_ctx->xml_ctx != NULL) { + switch_ivr_menu_execute(member->session,menu_ctx->menu_stack,menu_ctx->name,NULL); + } else { + if(menu_ctx->menu_stack == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "conference_loop_fn_menu handler NULL menu_stack\n"); + if(menu_ctx->xml_ctx == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "conference_loop_fn_menu handler NULL xml_ctx\n"); + } + } +} + /* Create a thread for the conference and launch it */ static void launch_input_thread(conference_member_t *member, switch_memory_pool_t *pool) { @@ -1038,51 +1044,13 @@ switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); if(member->conference->dtmf_parser != NULL) { - for (digit = dtmf; *digit; digit++) { - int *action = (int *)switch_ivr_digit_stream_parser_feed(member->conference->dtmf_parser, *digit); + caller_control_action_t *action; - if (action != NULL) { - switch(*action) { - case CALLER_CONTROL_NOEVENT: - // the digit string collected so far has not been recognized - break; - case CALLER_CONTROL_MUTE: - conference_loop_fn_mute_toggle(member); - break; - case CALLER_CONTROL_DEAF_MUTE: - conference_loop_fn_deafmute_toggle(member); - break; - case CALLER_CONTROL_ENERGY_UP: - conference_loop_fn_energy_up(member); - break; - case CALLER_CONTROL_ENERGY_EQU_CONFERENCE: - conference_loop_fn_energy_equ_conference(member); - break; - case CALLER_CONTROL_ENERGEY_DN: - conference_loop_fn_energy_dn(member); - break; - case CALLER_CONTROL_VOL_TALK_UP: - conference_loop_fn_volume_talk_up(member); - break; - case CALLER_CONTROL_VOL_TALK_ZERO: - conference_loop_fn_volume_talk_zero(member); - break; - case CALLER_CONTROL_VOL_TALK_DN: - conference_loop_fn_volume_talk_dn(member); - break; - case CALLER_CONTROL_VOL_LISTEN_UP: - conference_loop_fn_volume_listen_up(member); - break; - case CALLER_CONTROL_VOL_LISTEN_ZERO: - conference_loop_fn_volume_listen_zero(member); - break; - case CALLER_CONTROL_VOL_LISTEN_DN: - conference_loop_fn_volume_listen_dn(member); - break; - case CALLER_CONTROL_HANGUP: - conference_loop_fn_hangup(member); - break; - } + for (digit = dtmf; *digit; digit++) { + action = (caller_control_action_t *)switch_ivr_digit_stream_parser_feed(member->conference->dtmf_parser, *digit); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "digit_stream_parser_feed result != NULL %u\n",action != NULL); + if (action != NULL && action->handler != NULL) { + action->handler(member,action->data); } } } @@ -3254,11 +3222,71 @@ /*.chat_interface */ &conference_chat_interface }; +static switch_ivr_action_t conference_caller_control_menu_handler(switch_ivr_menu_t *menu, char *param, char *buf, size_t buflen, void *obj) +{ + switch_ivr_action_t action = SWITCH_IVR_ACTION_NOOP; + + if (param != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "caller control menu action '%s'\n",param); + } + + return action; +} + +static switch_status_t conference_caller_control_menu_build(caller_control_menu_ctx_t **ctx, conference_obj_t *conference, switch_xml_t profile, char *menu_name) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (ctx != NULL && conference != NULL && profile != NULL && menu_name != NULL) { + switch_xml_t xml_menus = switch_xml_child(profile, "menus"); + switch_xml_t xml_menu = (xml_menus != NULL ? switch_xml_find_child(xml_menus, "menu", "name", menu_name) : NULL); + + // if we found the requested menu in our profile + if (xml_menu != NULL && xml_menu != NULL) { + *ctx = (caller_control_menu_ctx_t *)switch_core_alloc(conference->pool,sizeof(caller_control_menu_ctx_t)); + + if (*ctx != NULL) { + memset(*ctx,0,sizeof(caller_control_menu_ctx_t)); + + // setup an xml parser context, and a menu stack context for the specified menu in our memory pool + status = switch_ivr_menu_stack_xml_init(&(*ctx)->xml_ctx, conference->pool); + if (status == SWITCH_STATUS_SUCCESS) { + (*ctx)->name = switch_core_strdup(conference->pool,menu_name); + // add our xml menu handler to the xml stack parser + status = switch_ivr_menu_stack_xml_add_custom((*ctx)->xml_ctx, "control", &conference_caller_control_menu_handler); + if (status != SWITCH_STATUS_SUCCESS) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to add custom xml handler\n"); + // parse the xml stack to build the menu stack + status = switch_ivr_menu_stack_xml_build((*ctx)->xml_ctx, &(*ctx)->menu_stack, xml_menus, xml_menu, conference->timer_name); + if (status != SWITCH_STATUS_SUCCESS) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to build xml menu stack\n"); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to init xml menu context\n"); + } + if (status != SWITCH_STATUS_SUCCESS) + *ctx = NULL; + } + } else { + if(xml_menus == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "caller control menu unable to find xml menus\n"); + if(xml_menu == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "caller control menu unable to find xml menu '%s'\n",menu_name); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "null parameters!\n"); + } + + return status; +} + /* create a new conferene with a specific profile */ static conference_obj_t *conference_new(char *name, switch_xml_t profile, switch_memory_pool_t *pool) { conference_obj_t *conference; switch_xml_t xml_kvp; + switch_xml_t xml_controls; + switch_xml_t xml_menus; + char *controls_set = NULL; char *rate_name = NULL; char *interval_name = NULL; char *timer_name = NULL; @@ -3470,46 +3498,125 @@ conference->rate = rate; conference->interval = interval; - if(switch_ivr_digit_stream_parser_new(conference->pool,&conference->dtmf_parser) == SWITCH_STATUS_SUCCESS) { - int i; + xml_controls = switch_xml_child(profile, "controls"); + controls_set = (xml_controls != NULL ? (char *) switch_xml_attr_soft(xml_controls, "set") : NULL); - // count the number of caller control definitons in the profile - for (i=0,xml_kvp = switch_xml_child(profile, "control"); xml_kvp; xml_kvp = xml_kvp->next) { - i++; - } - // map in the default control handler strings only if the profile has no caller control definitions - if (i == 0) { - for(i=0,status=SWITCH_STATUS_SUCCESS; status == SWITCH_STATUS_SUCCESS && i<CALLER_CONTROL_ACTIONS_QTY; i++) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing default caller control action name '%s' bound to '%s' digits.\n", - caller_control_actions[i].key,caller_control_actions[i].default_digits); - status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,caller_control_actions[i].default_digits,(void *)&(caller_control_actions[i].action)); - } - } + // try to build caller control if the set has been specified and != "none" + if (xml_controls != NULL && controls_set != NULL && strcasecmp(controls_set,"none") != 0) { + struct default_caller_control_table { + char *key; + char *digits; + caller_control_t action; + void (*handler)(conference_member_t *, void *); + } dcctbl[] = { + {"mute", "0", CALLER_CONTROL_MUTE, conference_loop_fn_mute_toggle}, + {"deaf mute", "*", CALLER_CONTROL_DEAF_MUTE, conference_loop_fn_deafmute_toggle}, + {"energy up", "9", CALLER_CONTROL_ENERGY_UP, conference_loop_fn_energy_up}, + {"energy equ", "8", CALLER_CONTROL_ENERGY_EQU_CONF, conference_loop_fn_energy_equ_conf}, + {"energy dn", "7", CALLER_CONTROL_ENERGEY_DN, conference_loop_fn_energy_dn}, + {"vol talk up", "3", CALLER_CONTROL_VOL_TALK_UP, conference_loop_fn_volume_talk_up}, + {"vol talk zero", "2", CALLER_CONTROL_VOL_TALK_ZERO, conference_loop_fn_volume_talk_zero}, + {"vol talk dn", "1", CALLER_CONTROL_VOL_TALK_DN, conference_loop_fn_volume_talk_dn}, + {"vol listen up", "6", CALLER_CONTROL_VOL_LISTEN_UP, conference_loop_fn_volume_listen_up}, + {"vol listen zero", "5", CALLER_CONTROL_VOL_LISTEN_ZERO, conference_loop_fn_volume_listen_zero}, + {"vol listen dn", "4", CALLER_CONTROL_VOL_LISTEN_DN, conference_loop_fn_volume_listen_dn}, + {"hangup", "#", CALLER_CONTROL_HANGUP, conference_loop_fn_hangup}, + {"menu", NULL, CALLER_CONTROL_MENU, conference_loop_fn_menu}, + }; + int dcctbl_qty = (sizeof(dcctbl)/sizeof(dcctbl[0])); - // parse the profile tree for caller control digit strings - for (xml_kvp = switch_xml_child(profile, "control"); xml_kvp; xml_kvp = xml_kvp->next) { - char *key = (char *) switch_xml_attr(xml_kvp, "action"); - char *val = (char *) switch_xml_attr(xml_kvp, "digits"); + if(switch_ivr_digit_stream_parser_new(conference->pool,&conference->dtmf_parser) == SWITCH_STATUS_SUCCESS) { + + // map in the default control handler strings if specified + if (strcasecmp(controls_set,"default") == 0) { + int i; + caller_control_action_t *action; - if(key != NULL && val != NULL && *key && *val) { - // scan through all of the valid actions, and if found, - // set the new caller control action digit string, then - // stop scanning the table, and go to the next xml kvp. - for(i=0,status=SWITCH_STATUS_NOOP; i<CALLER_CONTROL_ACTIONS_QTY && status == SWITCH_STATUS_NOOP; i++) { - if(strcasecmp(caller_control_actions[i].key,key) == 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing caller control action name '%s' bound to '%s' digits.\n",key,val); - status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,val,(void *)&caller_control_actions[i].action); + for(i=0,status=SWITCH_STATUS_SUCCESS; status == SWITCH_STATUS_SUCCESS && i<dcctbl_qty; i++) { + switch(dcctbl[i].action) + { + case CALLER_CONTROL_MENU: + // default caller control, has no menu specification + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing default caller control action '%s' bound to '%s'.\n", + dcctbl[i].key, + dcctbl[i].digits); + action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t)); + if (action != NULL) { + action->handler = dcctbl[i].handler; + action->data = NULL; + status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,dcctbl[i].digits,action); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",dcctbl[i].key,dcctbl[i].digits); + status = SWITCH_STATUS_MEMERR; + } + break; } } - if(status == SWITCH_STATUS_NOOP) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control action name '%s'.\n",key); + } else if (strcasecmp(controls_set,"custom") == 0) { + // parse the controls tree for caller control digit strings + for (xml_kvp = switch_xml_child(xml_controls, "control"); xml_kvp; xml_kvp = xml_kvp->next) { + char *key = (char *) switch_xml_attr(xml_kvp, "action"); + char *val = (char *) switch_xml_attr(xml_kvp, "digits"); + + if(!switch_strlen_zero(key) && !switch_strlen_zero(val)) { + int i; + caller_control_action_t *action; + + // scan through all of the valid actions, and if found, + // set the new caller control action digit string, then + // stop scanning the table, and go to the next xml kvp. + for(i=0,status=SWITCH_STATUS_NOOP; i<dcctbl_qty && status == SWITCH_STATUS_NOOP; i++) { + caller_control_menu_ctx_t *menu_ctx = NULL; + + if(strcasecmp(dcctbl[i].key,key) == 0) { + switch(dcctbl[i].action) + { + case CALLER_CONTROL_MENU: + { + char *menu_name = (char *)switch_xml_attr_soft(xml_kvp, "menu-name"); + + status = conference_caller_control_menu_build(&menu_ctx,conference,profile,menu_name); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to build menu '%s' bound to '%s'\n", menu_name, val); + break; + } + } + // deliberate fall through to default case + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing caller control action '%s' bound to '%s'.\n",key,val); + action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t)); + if (action != NULL) { + action->handler = dcctbl[i].handler; + action->data = (void *)menu_ctx; + status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,val,action); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",dcctbl[i].key,dcctbl[i].digits); + status = SWITCH_STATUS_MEMERR; + } + break; + } + } + } + if(status == SWITCH_STATUS_NOOP) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control action name '%s'.\n",key); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control config entry pair action='%s' digits='%s'\n",key,val); + } } - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control config entry pair action='%s' digits='%s'\n",key,val); } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate caller control digit parser.\n"); } } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate caller control digit parser.\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no caller controls intalled.\n"); + } + + xml_menus = switch_xml_child(profile, "menus"); + if (xml_menus != NULL) { +// switch_xml_t xml_menu; } /* Activate the conference mutex for exclusivity */