diff --git a/src/mod/applications/mod_protovm/Makefile b/src/mod/applications/mod_protovm/Makefile new file mode 100644 index 0000000000..650837295f --- /dev/null +++ b/src/mod/applications/mod_protovm/Makefile @@ -0,0 +1,3 @@ +BASE=/home/fs/src/fs-trunk-git/ +LOCAL_OBJS=ivr.o util.o config.o menu.o +include $(BASE)/build/modmake.rules diff --git a/src/mod/applications/mod_protovm/config.c b/src/mod/applications/mod_protovm/config.c new file mode 100644 index 0000000000..feec69b79e --- /dev/null +++ b/src/mod/applications/mod_protovm/config.c @@ -0,0 +1,130 @@ +/* Copy paste from FS mod_voicemail */ +#include + +#include "config.h" + +const char *global_cf = "protovm.conf"; + +void populate_profile_menu_event(vmivr_profile_t *profile, vmivr_menu_profile_t *menu) { + switch_xml_t cfg, xml, x_profiles, x_profile, x_keys, x_phrases, x_menus, x_menu; + + free_profile_menu_event(menu); + + if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf); + goto end; + } + if (!(x_profiles = switch_xml_child(cfg, "profiles"))) { + goto end; + } + + if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", profile->name))) { + if ((x_menus = switch_xml_child(x_profile, "menus"))) { + if ((x_menu = switch_xml_find_child(x_menus, "menu", "name", menu->name))) { + if ((x_keys = switch_xml_child(x_menu, "keys"))) { + switch_event_import_xml(switch_xml_child(x_keys, "key"), "dtmf", "action", &menu->event_keys_dtmf); + switch_event_import_xml(switch_xml_child(x_keys, "key"), "action", "dtmf", &menu->event_keys_action); + switch_event_import_xml(switch_xml_child(x_keys, "key"), "action", "variable", &menu->event_keys_varname); + } + if ((x_phrases = switch_xml_child(x_menu, "phrases"))) { + switch_event_import_xml(switch_xml_child(x_phrases, "phrase"), "name", "value", &menu->event_phrases); + } + } + } + } +end: + if (xml) + switch_xml_free(xml); + return; + +} + +void free_profile_menu_event(vmivr_menu_profile_t *menu) { + if (menu->event_keys_dtmf) { + switch_event_destroy(&menu->event_keys_dtmf); + } + if (menu->event_keys_action) { + switch_event_destroy(&menu->event_keys_action); + } + if (menu->event_keys_varname) { + switch_event_destroy(&menu->event_keys_varname); + } + + if (menu->event_phrases) { + switch_event_destroy(&menu->event_phrases); + } +} + +vmivr_profile_t *get_profile(switch_core_session_t *session, const char *profile_name) +{ + vmivr_profile_t *profile = NULL; + switch_xml_t cfg, xml, x_profiles, x_profile, x_apis, param; + + if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf); + return profile; + } + if (!(x_profiles = switch_xml_child(cfg, "profiles"))) { + goto end; + } + + if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", profile_name))) { + if (!(profile = switch_core_session_alloc(session, sizeof(vmivr_profile_t)))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n"); + goto end; + } + + profile->name = profile_name; + + /* TODO Make the following configurable */ + profile->api_profile = profile->name; + profile->menu_check_auth = "std_authenticate"; + profile->menu_check_main = "std_navigator"; + profile->menu_check_terminate = "std_purge"; + + if ((x_apis = switch_xml_child(x_profile, "apis"))) { + int total_options = 0; + int total_invalid_options = 0; + for (param = switch_xml_child(x_apis, "api"); param; param = param->next) { + char *var, *val; + if ((var = (char *) switch_xml_attr_soft(param, "name")) && (val = (char *) switch_xml_attr_soft(param, "value"))) { + if (!strcasecmp(var, "msg_undelete") && !profile->api_msg_undelete) + profile->api_msg_undelete = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_delete") && !profile->api_msg_delete) + profile->api_msg_delete = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_list") && !profile->api_msg_list) + profile->api_msg_list = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_count") && !profile->api_msg_count) + profile->api_msg_count = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_save") && !profile->api_msg_save) + profile->api_msg_save = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_purge") && !profile->api_msg_purge) + profile->api_msg_purge = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "msg_get") && !profile->api_msg_get) + profile->api_msg_get = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "pref_greeting_set") && !profile->api_pref_greeting_set) + profile->api_pref_greeting_set = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "pref_recname_set") && !profile->api_pref_recname_set) + profile->api_pref_recname_set = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "pref_password_set") && !profile->api_pref_password_set) + profile->api_pref_password_set = switch_core_session_strdup(session, val); + else if (!strcasecmp(var, "auth_login") && !profile->api_auth_login) + profile->api_auth_login = switch_core_session_strdup(session, val); + else + total_invalid_options++; + total_options++; + } + } + if (total_options - total_invalid_options != 11) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing api definition for profile '%s'\n", profile_name); + profile = NULL; + } + } + + } + +end: + switch_xml_free(xml); + return profile; +} + diff --git a/src/mod/applications/mod_protovm/config.h b/src/mod/applications/mod_protovm/config.h new file mode 100644 index 0000000000..04713fb7ae --- /dev/null +++ b/src/mod/applications/mod_protovm/config.h @@ -0,0 +1,49 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +extern const char *global_cf; + +struct vmivr_profile { + const char *name; + + const char *domain; + const char *id; + + const char *menu_check_auth; + const char *menu_check_main; + const char *menu_check_terminate; + + switch_bool_t authorized; + + const char *api_profile; + const char *api_auth_login; + const char *api_msg_delete; + const char *api_msg_undelete; + const char *api_msg_list; + const char *api_msg_count; + const char *api_msg_save; + const char *api_msg_purge; + const char *api_msg_get; + const char *api_pref_greeting_set; + const char *api_pref_recname_set; + const char *api_pref_password_set; + +}; +typedef struct vmivr_profile vmivr_profile_t; + +struct vmivr_menu_profile { + const char *name; + + switch_event_t *event_keys_action; + switch_event_t *event_keys_dtmf; + switch_event_t *event_keys_varname; + switch_event_t *event_phrases; +}; +typedef struct vmivr_menu_profile vmivr_menu_profile_t; + +vmivr_profile_t *get_profile(switch_core_session_t *session, const char *profile_name); + +void free_profile_menu_event(vmivr_menu_profile_t *menu); +void populate_profile_menu_event(vmivr_profile_t *profile, vmivr_menu_profile_t *menu); + +#endif /* _CONFIG_H_ */ diff --git a/src/mod/applications/mod_protovm/ivr.c b/src/mod/applications/mod_protovm/ivr.c new file mode 100644 index 0000000000..4b1f6ef0cc --- /dev/null +++ b/src/mod/applications/mod_protovm/ivr.c @@ -0,0 +1,249 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Marc Olivier Chouinard + * + * + * ivr.c -- MT IVR System Interface + * + */ + +#include + +#include "ivr.h" + +int match_dtmf(switch_core_session_t *session, dtmf_ss_t *loc) { + switch_bool_t is_invalid[128] = { SWITCH_FALSE }; + int i; + loc->potentialMatch = NULL; + loc->completeMatch = NULL; + loc->potentialMatchCount = 0; + + for (i = 0; i < loc->dtmf_received; i++) { + int j; + loc->potentialMatchCount = 0; + for (j = 0; !zstr(loc->dtmf_accepted[j]) && j < 128; j++) { + switch_bool_t cMatch = SWITCH_FALSE; + char test[2] = { 0 }; + + if (is_invalid[j]) + continue; + + test[0] = loc->dtmf_stored[i]; + if (loc->dtmf_accepted[j][i] == 'N' && atoi(test) >= 2 && atoi(test) <= 9) + cMatch = SWITCH_TRUE; + if (loc->dtmf_accepted[j][i] == 'X' && atoi(test) >= 0 && atoi(test) <= 9) { + cMatch = SWITCH_TRUE; + } + if (i >= strlen(loc->dtmf_accepted[j]) - 1 && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] == '.') + cMatch = SWITCH_TRUE; + if (loc->dtmf_accepted[j][i] == loc->dtmf_stored[i]) + cMatch = SWITCH_TRUE; + + if (cMatch == SWITCH_FALSE) { + is_invalid[j] = SWITCH_TRUE; + continue; + } + + if (i == strlen(loc->dtmf_accepted[j]) - 1 && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] == '.') { + loc->completeMatch = loc->dtmf_accepted[j]; + } + if (i == loc->dtmf_received - 1 && loc->dtmf_received == strlen(loc->dtmf_accepted[j]) && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] != '.') { + loc->completeMatch = loc->dtmf_accepted[j]; + continue; + } + loc->potentialMatchCount++; + } + } + + return 1; +} + +static switch_status_t cb_on_dtmf_ignore(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +{ + switch (itype) { + case SWITCH_INPUT_TYPE_DTMF: + { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_dtmf_t *dtmf = (switch_dtmf_t *) input; + switch_channel_queue_dtmf(channel, dtmf); + return SWITCH_STATUS_BREAK; + } + default: + break; + } + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t cb_on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +{ + dtmf_ss_t *loc = (dtmf_ss_t*) buf; + + switch (itype) { + case SWITCH_INPUT_TYPE_DTMF: + { + switch_dtmf_t *dtmf = (switch_dtmf_t *) input; + switch_bool_t audio_was_stopped = loc->audio_stopped; + loc->audio_stopped = SWITCH_TRUE; + + if (loc->dtmf_received >= sizeof(loc->dtmf_stored)) { + loc->result = RES_BUFFER_OVERFLOW; + break; + } + if (!loc->terminate_key || dtmf->digit != loc->terminate_key) + loc->dtmf_stored[loc->dtmf_received++] = dtmf->digit; + + match_dtmf(session, loc); + + if (loc->terminate_key && dtmf->digit == loc->terminate_key && loc->result == RES_WAITFORMORE) { + if (loc->potentialMatchCount == 1 && loc->completeMatch != NULL) { + loc->result = RES_FOUND; + } else { + loc->result = RES_INVALID; + } + return SWITCH_STATUS_BREAK; + } else { + if (loc->potentialMatchCount == 0 && loc->completeMatch != NULL) { + loc->result = RES_FOUND; + return SWITCH_STATUS_BREAK; + } else if (loc->potentialMatchCount > 0) { + loc->result = RES_WAITFORMORE; + if (!audio_was_stopped) + return SWITCH_STATUS_BREAK; + } else { + loc->result = RES_INVALID; + return SWITCH_STATUS_BREAK; + } + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t captureMenuInitialize(dtmf_ss_t *loc, char **dtmf_accepted) { + int i; + + memset(loc, 0, sizeof(*loc)); + + for (i = 0; dtmf_accepted[i] && i < 16; i++) { + strncpy(loc->dtmf_accepted[i], dtmf_accepted[i], 128); + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t playbackBufferDTMF(switch_core_session_t *session, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout) { + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (switch_channel_ready(channel)) { + switch_input_args_t args = { 0 }; + + args.input_callback = cb_on_dtmf_ignore; + + if (macro_name) { + status = switch_ivr_phrase_macro_event(session, macro_name, data, event, lang, &args); + } + } else { + status = SWITCH_STATUS_BREAK; + } + + return status; +} + + +switch_status_t captureMenu(switch_core_session_t *session, dtmf_ss_t *loc, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout) { + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (switch_channel_ready(channel)) { + switch_input_args_t args = { 0 }; + + args.input_callback = cb_on_dtmf; + args.buf = loc; + + if (macro_name && loc->audio_stopped == SWITCH_FALSE && loc->result == RES_WAITFORMORE) { + status = switch_ivr_phrase_macro_event(session, macro_name, data, event, lang, &args); + } + + if (switch_channel_ready(channel) && (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) && timeout && loc->result == RES_WAITFORMORE) { + loc->audio_stopped = SWITCH_TRUE; + switch_ivr_collect_digits_callback(session, &args, timeout, 0); + if (loc->result == RES_WAITFORMORE) { + if (loc->potentialMatchCount == 1 && loc->completeMatch != NULL) { + loc->result = RES_FOUND; + } else { + loc->result = RES_TIMEOUT; + } + } + } + } else { + status = SWITCH_STATUS_BREAK; + } + + return status; +} + +switch_status_t captureMenuRecord(switch_core_session_t *session, dtmf_ss_t *loc, switch_event_t *event, const char *file_path, switch_file_handle_t *fh, int max_record_len) { + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (switch_channel_ready(channel)) { + switch_input_args_t args = { 0 }; + + args.input_callback = cb_on_dtmf; + args.buf = loc; + + if (loc->audio_stopped == SWITCH_FALSE && loc->result == RES_WAITFORMORE) { + loc->recorded_audio = SWITCH_TRUE; + status = switch_ivr_record_file(session, fh, file_path, &args, max_record_len); + + } + if (loc->result == RES_WAITFORMORE) { + loc->result = RES_TIMEOUT; + } + + } else { + status = SWITCH_STATUS_BREAK; + } + + return status; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/src/mod/applications/mod_protovm/ivr.h b/src/mod/applications/mod_protovm/ivr.h new file mode 100644 index 0000000000..99136475fd --- /dev/null +++ b/src/mod/applications/mod_protovm/ivr.h @@ -0,0 +1,30 @@ +struct dtmf_ss { + char dtmf_stored[128]; + int dtmf_received; + char dtmf_accepted[16][128]; + int result; + switch_bool_t audio_stopped; + switch_bool_t recorded_audio; + const char *potentialMatch; + int potentialMatchCount; + const char *completeMatch; + char terminate_key; +}; +typedef struct dtmf_ss dtmf_ss_t; + +#define RES_WAITFORMORE 0 +#define RES_FOUND 1 +#define RES_INVALID 3 +#define RES_TIMEOUT 4 +#define RES_BREAK 5 +#define RES_RECORD 6 +#define RES_BUFFER_OVERFLOW 99 + +#define MAX_DTMF_SIZE_OPTION 32 + +switch_status_t captureMenu(switch_core_session_t *session, dtmf_ss_t *loc, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout); +switch_status_t captureMenuRecord(switch_core_session_t *session, dtmf_ss_t *loc, switch_event_t *event, const char *file_path, switch_file_handle_t *fh, int max_record_len); +switch_status_t captureMenuInitialize(dtmf_ss_t *loc, char **dtmf_accepted); + +switch_status_t playbackBufferDTMF(switch_core_session_t *session, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout); + diff --git a/src/mod/applications/mod_protovm/menu.c b/src/mod/applications/mod_protovm/menu.c new file mode 100644 index 0000000000..4dc3e19e78 --- /dev/null +++ b/src/mod/applications/mod_protovm/menu.c @@ -0,0 +1,599 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Marc Olivier Chouinard + * + * + * menu.c -- VoiceMail Menu + * + */ +#include + +#include "ivr.h" +#include "menu.h" +#include "util.h" +#include "config.h" + +/* List of available menu */ +vmivr_menu_function_t menu_list[] = { + {"std_authenticate", mtvm_menu_authenticate}, + {"std_navigator", mtvm_menu_main}, + {"std_record_name", mtvm_menu_record_name}, + {"std_set_password", mtvm_menu_set_password}, + {"std_select_greeting_slot", mtvm_menu_select_greeting_slot}, + {"std_record_greeting_with_slot", mtvm_menu_record_greeting_with_slot}, + {"std_preference", mtvm_menu_preference}, + {"std_purge", mtvm_menu_purge}, + { NULL, NULL } +}; + +#define MAX_ATTEMPT 3 /* TODO Make these fields configurable */ +#define DEFAULT_IVR_TIMEOUT 3000 + +void mtvm_menu_purge(switch_core_session_t *session, vmivr_profile_t *profile) { + if (profile->id && profile->authorized) { + if (1==1 /* TODO make Purge email on exit optional ??? */) { + const char *cmd = switch_core_session_sprintf(session, "%s %s %s", profile->api_profile, profile->domain, profile->id); + mt_api_execute(session, profile->api_msg_purge, cmd); + } + } +} +void mtvm_menu_main(switch_core_session_t *session, vmivr_profile_t *profile) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *msg_list_params = NULL; + size_t msg_count = 0; + size_t current_msg = 1; + size_t next_msg = current_msg; + size_t previous_msg = current_msg; + char *cmd = NULL; + int retry; + + /* Different switch to control playback of phrases */ + switch_bool_t initial_count_played = SWITCH_FALSE; + switch_bool_t skip_header = SWITCH_FALSE; + switch_bool_t msg_deleted = SWITCH_FALSE; + switch_bool_t msg_undeleted = SWITCH_FALSE; + switch_bool_t msg_saved = SWITCH_FALSE; + + vmivr_menu_profile_t menu = { "std_navigator" }; + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + if (!menu.event_keys_dtmf || !menu.event_phrases) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n"); + return; + } + + /* Get VoiceMail List And update msg count */ + cmd = switch_core_session_sprintf(session, "json %s %s %s", profile->api_profile, profile->domain, profile->id); + msg_list_params = jsonapi2event(session, NULL, profile->api_msg_list, cmd); + msg_count = atol(switch_event_get_header(msg_list_params,"VM-List-Count")); + + /* TODO Add Detection of new message and notify the user */ + + for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) { + dtmf_ss_t loc; + char *dtmfa[16] = { 0 }; + switch_event_t *phrase_params = NULL; + + switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS); + + append_event_profile(phrase_params, profile, menu); + + populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa); + + previous_msg = current_msg; + + /* Simple Protection to not go out of msg list scope */ + /* TODO: Add Prompt to notify they reached the begining or the end */ + if (next_msg == 0) { + next_msg = 1; + } else if (next_msg > msg_count) { + next_msg = msg_count; + } + + current_msg = next_msg; + + captureMenuInitialize(&loc, dtmfa); + + /* Prompt related to previous Message here */ + append_event_message(session, profile, phrase_params, msg_list_params, previous_msg); + if (msg_deleted) { + msg_deleted = SWITCH_FALSE; + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "deleted", phrase_params, NULL, 0); + } + if (msg_undeleted) { + msg_undeleted = SWITCH_FALSE; + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "undeleted", phrase_params, NULL, 0); + } + if (msg_saved) { + msg_saved = SWITCH_FALSE; + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "saved", phrase_params, NULL, 0); + } + + /* Prompt related the current message */ + append_event_message(session, profile, phrase_params, msg_list_params, current_msg); + + /* TODO check if msg is gone (purged by another session, notify user and auto jump to next message or something) */ + if (!skip_header) { + if (!initial_count_played) { + cmd = switch_core_session_sprintf(session, "json %s %s %s", profile->api_profile, profile->domain, profile->id); + jsonapi2event(session, phrase_params, profile->api_msg_count, cmd); + initial_count_played = SWITCH_TRUE; + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "msg_count"), NULL, phrase_params, NULL, 0); + } + if (msg_count > 0) { + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "say_msg_number"), NULL, phrase_params, NULL, 0); + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "say_date"), NULL, phrase_params, NULL, 0); + } + } + if (msg_count > 0) { + /* TODO Update the Read date of a message (When msg start, or when it listen compleatly ??? To be determined */ + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "play_message"), NULL, phrase_params, NULL, 0); + } + skip_header = SWITCH_FALSE; + + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT); + + if (loc.result == RES_TIMEOUT) { + /* TODO Ask for the prompt Again IF retry != 0 */ + } else if (loc.result == RES_INVALID) { + /* TODO Say invalid option, and ask for the prompt again IF retry != 0 */ + } else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */ + const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored); + + /* Reset the try count */ + retry = MAX_ATTEMPT; + + if (action) { + if (!strcasecmp(action, "skip_intro")) { /* Skip Header / Play the recording again */ + skip_header = SWITCH_TRUE; + } else if (!strcasecmp(action, "next_msg")) { /* Next Message */ + next_msg++; + } else if (!strcasecmp(action, "prev_msg")) { /* Previous Message */ + next_msg--; + } else if (!strcasecmp(action, "delete_msg")) { /* Delete / Undelete Message */ + if (strncasecmp(switch_event_get_header(phrase_params, "VM-Message-Flags"), "delete", 6)) { + cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID")); + mt_api_execute(session, profile->api_msg_delete, cmd); + + msg_deleted = SWITCH_TRUE; + /* TODO Option for auto going to next message or just return to the menu (So user used to do 76 to delete and next message wont be confused) */ + next_msg++; + } else { + cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID")); + mt_api_execute(session, profile->api_msg_undelete, cmd); + + msg_undeleted = SWITCH_TRUE; + } + } else if (!strcasecmp(action, "save_msg")) { /* Save Message */ + cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID")); + mt_api_execute(session, profile->api_msg_save, cmd); + + msg_saved = SWITCH_TRUE; + } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */ + void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5); + if (fPtr) { + fPtr(session, profile); + } + } else if (!strcasecmp(action, "return")) { /* Return */ + retry = -1; + } + } + } + + /* IF the API to get the message returned us a COPY of the file locally (temp file create from a DB or from a web server), delete it */ + if (switch_true(switch_event_get_header(phrase_params, "VM-Message-Private-Local-Copy"))) { + const char *file_path = switch_event_get_header(phrase_params, "VM-Message-File-Path"); + if (file_path && unlink(file_path) != 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete temp file [%s]\n", file_path); + } + } + switch_event_destroy(&phrase_params); + } + + switch_event_destroy(&msg_list_params); + + free_profile_menu_event(&menu); + + return; +} + +void mtvm_menu_record_name(switch_core_session_t *session, vmivr_profile_t *profile) { + switch_status_t status; + vmivr_menu_profile_t menu = { "std_record_name" }; + + char *tmp_filepath = generate_random_file_name(session, "protovm", "wav" /* TODO make it configurable */); + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + status = mtvm_menu_record(session, profile, menu, tmp_filepath); + + if (status == SWITCH_STATUS_SUCCESS) { + char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, tmp_filepath); + mt_api_execute(session, profile->api_pref_recname_set, cmd); + } +} + +void mtvm_menu_set_password(switch_core_session_t *session, vmivr_profile_t *profile) { + char *password; + vmivr_menu_profile_t menu = { "std_set_password" }; + + password = mtvm_menu_get_input_set(session, profile, menu, "XXX." /* TODO Conf Min 3 Digit */, "#" /* TODO Conf terminate input key */); + + /* TODO Add Prompts to tell if password was set and if it was not */ + if (password) { + char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, password); + mt_api_execute(session, profile->api_pref_password_set, cmd); + } + + free_profile_menu_event(&menu); +} + +void mtvm_menu_authenticate(switch_core_session_t *session, vmivr_profile_t *profile) { + switch_channel_t *channel = switch_core_session_get_channel(session); + vmivr_menu_profile_t menu = { "std_authenticate" }; + int retry; + const char *auth_var = NULL; + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + if (profile->id && (auth_var = switch_channel_get_variable(channel, "voicemail_authorized")) && switch_true(auth_var)) { + profile->authorized = SWITCH_TRUE; + } + + for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0 && profile->authorized == SWITCH_FALSE; retry--) { + const char *id = profile->id, *password = NULL; + char *cmd = NULL; + + if (!id) { + vmivr_menu_profile_t sub_menu = { "std_authenticate_ask_user" }; + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &sub_menu); + + id = mtvm_menu_get_input_set(session, profile, sub_menu, "X." /* TODO Conf Min 3 Digit */, "#" /* TODO Conf terminate input key */); + free_profile_menu_event(&sub_menu); + } + if (!password) { + vmivr_menu_profile_t sub_menu = { "std_authenticate_ask_password" }; + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &sub_menu); + + password = mtvm_menu_get_input_set(session, profile, sub_menu, "X." /* TODO Conf Min 3 Digit */, "#" /* TODO Conf terminate input key */); + free_profile_menu_event(&sub_menu); + } + cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, id, password); + + if (mt_api_execute(session, profile->api_auth_login, cmd) == SWITCH_STATUS_SUCCESS) { + profile->id = id; + profile->authorized = SWITCH_TRUE; + } else { + playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "fail_auth"), NULL, NULL, NULL, 0); + } + } + free_profile_menu_event(&menu); +} + + +void mtvm_menu_select_greeting_slot(switch_core_session_t *session, vmivr_profile_t *profile) { + vmivr_menu_profile_t menu = { "std_select_greeting_slot" }; + + const char *result; + int gnum = -1; + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + result = mtvm_menu_get_input_set(session, profile, menu, "X", NULL); + + if (result) + gnum = atoi(result); + if (gnum != -1) { + char * cmd = switch_core_session_sprintf(session, "%s %s %s %d", profile->api_profile, profile->domain, profile->id, gnum); + if (mt_api_execute(session, profile->api_pref_greeting_set, cmd) == SWITCH_STATUS_SUCCESS) { + char *str_num = switch_core_session_sprintf(session, "%d", gnum); + playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0); + } else { + playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "invalid_slot"), NULL, NULL, NULL, 0); + } + } + free_profile_menu_event(&menu); +} + +void mtvm_menu_record_greeting_with_slot(switch_core_session_t *session, vmivr_profile_t *profile) { + + vmivr_menu_profile_t menu = { "std_record_greeting_with_slot" }; + + const char *result; + int gnum = -1; + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + result = mtvm_menu_get_input_set(session, profile, menu, "X", NULL); + + if (result) + gnum = atoi(result); + + /* If user entered 0, we don't accept it */ + if (gnum > 0) { + vmivr_menu_profile_t sub_menu = { "std_record_greeting" }; + char *tmp_filepath = generate_random_file_name(session, "protovm", "wav" /* TODO make it configurable */); + switch_status_t status; + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &sub_menu); + + status = mtvm_menu_record(session, profile, sub_menu, tmp_filepath); + + if (status == SWITCH_STATUS_SUCCESS) { + char *cmd = switch_core_session_sprintf(session, "%s %s %s %d %s", profile->api_profile, profile->domain, profile->id, gnum, tmp_filepath); + char *str_num = switch_core_session_sprintf(session, "%d", gnum); + mt_api_execute(session, profile->api_pref_greeting_set, cmd); + playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0); + } + free_profile_menu_event(&sub_menu); + + } + + free_profile_menu_event(&menu); + +} + +void mtvm_menu_preference(switch_core_session_t *session, vmivr_profile_t *profile) { + switch_channel_t *channel = switch_core_session_get_channel(session); + + int retry; + + vmivr_menu_profile_t menu = { "std_preference" }; + + /* Initialize Menu Configs */ + populate_profile_menu_event(profile, &menu); + + if (!menu.event_keys_dtmf || !menu.event_phrases) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n"); + return; + } + + for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) { + dtmf_ss_t loc; + char *dtmfa[16] = { 0 }; + switch_event_t *phrase_params = NULL; + + switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS); + append_event_profile(phrase_params, profile, menu); + + populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa); + + captureMenuInitialize(&loc, dtmfa); + + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT); + + if (loc.result == RES_TIMEOUT) { + /* TODO Ask for the prompt Again IF retry != 0 */ + } else if (loc.result == RES_INVALID) { + /* TODO Say invalid option, and ask for the prompt again IF retry != 0 */ + } else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */ + const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored); + + /* Reset the try count */ + retry = MAX_ATTEMPT; + + if (action) { + if (!strcasecmp(action, "return")) { /* Return to the previous menu */ + retry = -1; + } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */ + void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5); + if (fPtr) { + fPtr(session, profile); + } + } + } + } + switch_event_destroy(&phrase_params); + } + + free_profile_menu_event(&menu); +} + +char *mtvm_menu_get_input_set(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *input_mask, const char *terminate_key) { + char *result = NULL; + int retry; + + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (!menu.event_keys_dtmf || !menu.event_phrases) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys : %s\n", menu.name); + return result; + } + + for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) { + dtmf_ss_t loc; + char *dtmfa[16] = { 0 }; + int i; + switch_event_t *phrase_params = NULL; + + switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS); + append_event_profile(phrase_params, profile, menu); + + populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa); + + /* Find the last entry and append this one to it */ + for (i=0; dtmfa[i] && i < 16; i++){ + } + dtmfa[i] = (char *) input_mask; + + captureMenuInitialize(&loc, dtmfa); + if (terminate_key) + loc.terminate_key = terminate_key[0]; /* TODO Make this load from the configuration */ + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "instructions"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT); + + if (loc.result == RES_TIMEOUT) { + /* TODO Ask for the prompt Again IF retry != 0 */ + } else if (loc.result == RES_INVALID) { + /* TODO Say invalid option, and ask for the prompt again IF retry != 0 */ + } else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */ + + /* Reset the try count */ + retry = MAX_ATTEMPT; + + if (!strncasecmp(loc.completeMatch, input_mask, 1)) { + result = switch_core_session_strdup(session, loc.dtmf_stored); + retry = -1; + + } + } + switch_event_destroy(&phrase_params); + } + + return result; +} + +switch_status_t mtvm_menu_record(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *file_name) { + switch_status_t status = SWITCH_STATUS_FALSE; + switch_channel_t *channel = switch_core_session_get_channel(session); + int retry; + + switch_bool_t record_prompt = SWITCH_TRUE; + switch_bool_t listen_recording = SWITCH_FALSE; + switch_bool_t play_instruction = SWITCH_TRUE; + + if (!menu.event_keys_dtmf || !menu.event_phrases) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n"); + return status; + } + + for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) { + dtmf_ss_t loc; + + char *dtmfa[16] = { 0 }; + switch_event_t *phrase_params = NULL; + switch_file_handle_t fh = { 0 }; + + /* TODO Make the following configurable */ + fh.thresh = 200; + fh.silence_hits = 4; + //fh.samplerate = 8000; + + + switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS); + append_event_profile(phrase_params, profile, menu); + + populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa); + + captureMenuInitialize(&loc, dtmfa); + if (record_prompt) { + if (play_instruction) { + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "instructions"), NULL, phrase_params, NULL, 0); + } + play_instruction = SWITCH_TRUE; + + captureMenuRecord(session, &loc, phrase_params, file_name, &fh, 30 /* TODO Make max recording configurable */); + } else { + if (listen_recording) { + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Record-File-Path", "%s", file_name); + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "play_recording"), NULL, phrase_params, NULL, 0); + listen_recording = SWITCH_FALSE; + + } + captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT); + } + + if (loc.recorded_audio) { + /* Reset the try count */ + retry = MAX_ATTEMPT; + + /* TODO Check if message is too short */ + + record_prompt = SWITCH_FALSE; + + } else if (loc.result == RES_TIMEOUT) { + /* TODO Ask for the prompt Again IF retry != 0 */ + } else if (loc.result == RES_INVALID) { + /* TODO Say invalid option, and ask for the prompt again IF retry != 0 */ + } else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */ + const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored); + + /* Reset the try count */ + retry = MAX_ATTEMPT; + + if (action) { + if (!strcasecmp(action, "listen")) { /* Listen */ + listen_recording = SWITCH_TRUE; + + } else if (!strcasecmp(action, "save")) { + retry = -1; + /* TODO ALLOW SAVE ONLY IF FILE IS RECORDED AND HIGHER THAN MIN SIZE */ + status = SWITCH_STATUS_SUCCESS; + + } else if (!strcasecmp(action, "rerecord")) { + record_prompt = SWITCH_TRUE; + + } else if (!strcasecmp(action, "skip_instruction")) { /* Skip Recording Greeting */ + play_instruction = SWITCH_FALSE; + + } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */ + void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5); + if (fPtr) { + fPtr(session, profile); + } + } else if (!strcasecmp(action, "return")) { /* Return */ + retry = -1; + } + } + } + switch_event_destroy(&phrase_params); + } + return status; +} + + +void (*mtvm_get_menu_function(const char *menu_name))(switch_core_session_t *session, vmivr_profile_t *profile) { + int i = 0; + + if (menu_name) { + for (i=0; menu_list[i].name ; i++) { + if (!strcasecmp(menu_list[i].name, menu_name)) { + return menu_list[i].pt2Func; + } + } + } + return NULL; +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/src/mod/applications/mod_protovm/menu.h b/src/mod/applications/mod_protovm/menu.h new file mode 100644 index 0000000000..a2d8cb09f5 --- /dev/null +++ b/src/mod/applications/mod_protovm/menu.h @@ -0,0 +1,31 @@ +#ifndef _MENU_H_ +#define _MENU_H_ + +#include "config.h" + +void mtvm_menu_purge(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_authenticate(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_main(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_record_name(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_set_password(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_select_greeting_slot(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_record_greeting_with_slot(switch_core_session_t *session, vmivr_profile_t *profile); +void mtvm_menu_preference(switch_core_session_t *session, vmivr_profile_t *profile); + +switch_status_t mtvm_menu_record(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *file_name); +char *mtvm_menu_get_input_set(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *input_mask, const char *terminate_key); + + +struct vmivr_menu_function { + const char *name; + void (*pt2Func)(switch_core_session_t *session, vmivr_profile_t *profile); + +}; +typedef struct vmivr_menu_function vmivr_menu_function_t; + +extern vmivr_menu_function_t menu_list[]; + +void (*mtvm_get_menu_function(const char *menu_name))(switch_core_session_t *session, vmivr_profile_t *profile); + +#endif /* _MENU_H_ */ + diff --git a/src/mod/applications/mod_protovm/mod_protovm.c b/src/mod/applications/mod_protovm/mod_protovm.c new file mode 100644 index 0000000000..7d826e03c2 --- /dev/null +++ b/src/mod/applications/mod_protovm/mod_protovm.c @@ -0,0 +1,139 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Marc Olivier Chouinard + * + * + * mod_protovm.c -- MT VoiceMail System + * + */ +#include + +#include "config.h" +#include "menu.h" + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_protovm_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_protovm_runtime); +SWITCH_MODULE_LOAD_FUNCTION(mod_protovm_load); + +/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) + * Defines a switch_loadable_module_function_table_t and a static const char[] modname + */ +SWITCH_MODULE_DEFINITION(mod_protovm, mod_protovm_load, mod_protovm_shutdown, NULL); + + +#define MTVM_DESC "protovm" +#define MTVM_USAGE " profile domain [id]" + +SWITCH_STANDARD_APP(protovm_function) +{ + const char *id = NULL; + const char *domain = NULL; + const char *profile_name = NULL; + vmivr_profile_t *profile = NULL; + int argc = 0; + char *argv[6] = { 0 }; + char *mydata = NULL; + + if (!zstr(data)) { + mydata = switch_core_session_strdup(session, data); + argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argv[1]) + profile_name = argv[1]; + + if (argv[2]) + domain = argv[2]; + + if (!strcasecmp(argv[0], "check")) { + if (argv[3]) + id = argv[3]; + + if (domain && profile_name) { + profile = get_profile(session, profile_name); + + if (profile) { + void (*fPtrAuth)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_auth); + void (*fPtrMain)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_main); + void (*fPtrTerminate)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_terminate); + + profile->domain = domain; + profile->id = id; + + if (fPtrAuth && !profile->authorized) { + fPtrAuth(session, profile); + } + + if (fPtrMain && profile->authorized) { + fPtrMain(session, profile); + } + if (fPtrTerminate) { + fPtrTerminate(session, profile); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile '%s' not found\n", profile_name); + } + } + } + return; +} + +/* Macro expands to: switch_status_t mod_protovm_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_protovm_load) +{ + switch_application_interface_t *app_interface; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + SWITCH_ADD_APP(app_interface, "protovm", "protovm", MTVM_DESC, protovm_function, MTVM_USAGE, SAF_NONE); + + /* indicate that the module should continue to be loaded */ + return status; +} + +/* + Called when the system shuts down + Macro expands to: switch_status_t mod_protovm_shutdown() */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_protovm_shutdown) +{ + + return SWITCH_STATUS_SUCCESS; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/src/mod/applications/mod_protovm/protovm.conf.xml b/src/mod/applications/mod_protovm/protovm.conf.xml new file mode 100644 index 0000000000..3c42ce1faa --- /dev/null +++ b/src/mod/applications/mod_protovm/protovm.conf.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_protovm/sounds.xml b/src/mod/applications/mod_protovm/sounds.xml new file mode 100644 index 0000000000..2fe42a40d2 --- /dev/null +++ b/src/mod/applications/mod_protovm/sounds.xml @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_protovm/util.c b/src/mod/applications/mod_protovm/util.c new file mode 100644 index 0000000000..476e99bd26 --- /dev/null +++ b/src/mod/applications/mod_protovm/util.c @@ -0,0 +1,175 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Marc Olivier Chouinard + * + * + * utils.c -- MT VoiceMail / Different utility that might need to go into the core (after cleanup) + * + */ +#include + +#include "util.h" + +switch_status_t mt_merge_media_files(const char** inputs, const char *output) { + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_file_handle_t fh_output = { 0 }; + int channels = 1; + int rate = 8000; /* TODO Make this configurable */ + int j = 0; + + if (switch_core_file_open(&fh_output, output, channels, rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open %s\n", output); + goto end; + } + + for (j = 0; inputs[j] != NULL && j < 128 && status == SWITCH_STATUS_SUCCESS; j++) { + switch_file_handle_t fh_input = { 0 }; + char buf[2048]; + switch_size_t len = sizeof(buf) / 2; + + if (switch_core_file_open(&fh_input, inputs[j], channels, rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open %s\n", inputs[j]); + status = SWITCH_STATUS_GENERR; + break; + } + + while (switch_core_file_read(&fh_input, buf, &len) == SWITCH_STATUS_SUCCESS) { + if (switch_core_file_write(&fh_output, buf, &len) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write error\n"); + status = SWITCH_STATUS_GENERR; + break; + } + } + + if (fh_input.file_interface) { + switch_core_file_close(&fh_input); + } + } + + if (fh_output.file_interface) { + switch_core_file_close(&fh_output); + } +end: + return status; +} + +switch_event_t *jsonapi2event(switch_core_session_t *session, switch_event_t *apply_event, const char *api, const char *data) { + switch_event_t *phrases_event = NULL; + switch_stream_handle_t stream = { 0 }; + SWITCH_STANDARD_STREAM(stream); + switch_api_execute(api, data, session, &stream); + switch_event_create_json(&phrases_event, (char *) stream.data); + switch_safe_free(stream.data); + + if (apply_event) { + switch_event_header_t *hp; + for (hp = phrases_event->headers; hp; hp = hp->next) { + if (!strncasecmp(hp->name, "VM-", 3)) { + switch_event_add_header(apply_event, SWITCH_STACK_BOTTOM, hp->name, "%s", hp->value); + } + } + switch_event_destroy(&phrases_event); + phrases_event = apply_event; + + } + + return phrases_event; +} + +char *generate_random_file_name(switch_core_session_t *session, const char *mod_name, char *file_extension) { + char rand_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = ""; + switch_uuid_t srand_uuid; + + switch_uuid_get(&srand_uuid); + switch_uuid_format(rand_uuid, &srand_uuid); + + return switch_core_session_sprintf(session, "%s%s%s_%s.%s", SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, mod_name, rand_uuid, file_extension); + +} + +switch_status_t mt_api_execute(switch_core_session_t *session, const char *apiname, const char *arguments) { + switch_status_t status = SWITCH_STATUS_SUCCESS; + + switch_stream_handle_t stream = { 0 }; + + SWITCH_STANDARD_STREAM(stream); + switch_api_execute(apiname, arguments, session, &stream); + if (!strncasecmp(stream.data, "-ERR", 4)) { + status = SWITCH_STATUS_GENERR; + } + switch_safe_free(stream.data); + return status; +} + +void append_event_profile(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu) { + /* Used for some appending function */ + if (profile->name && profile->id && profile->domain) { + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Profile", "%s", profile->name); + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Account-ID", "%s", profile->id); + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Account-Domain", "%s", profile->domain); + } +} + +void populate_dtmfa_from_event(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu, char **dtmfa) { + int i = 0; + if (menu.event_keys_dtmf) { + switch_event_header_t *hp; + + for (hp = menu.event_keys_dtmf->headers; hp; hp = hp->next) { + if (strlen(hp->name) < 3 && hp->value) { /* TODO This is a hack to discard default FS Events ! */ + const char *varphrasename = switch_event_get_header(menu.event_keys_varname, hp->value); + dtmfa[i++] = hp->name; + + if (varphrasename && !zstr(varphrasename)) { + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, varphrasename, "%s", hp->name); + } + } + } + } + +} + +void append_event_message(switch_core_session_t *session, vmivr_profile_t *profile, switch_event_t *phrase_params, switch_event_t *msg_list_event, size_t current_msg) { + + char *varname; + char *apicmd; + + varname = switch_mprintf("VM-List-Message-%" SWITCH_SIZE_T_FMT "-UUID", current_msg); + apicmd = switch_mprintf("json %s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(msg_list_event, varname)); + + switch_safe_free(varname); + + jsonapi2event(session, phrase_params, profile->api_msg_get, apicmd); + + /* TODO Set these 2 header correctly */ + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Type", "%s", "new"); + switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Number", "%"SWITCH_SIZE_T_FMT, current_msg); + + switch_event_add_header_string(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Private-Local-Copy", "False"); + + switch_safe_free(apicmd); +} + diff --git a/src/mod/applications/mod_protovm/util.h b/src/mod/applications/mod_protovm/util.h new file mode 100644 index 0000000000..1c46329e18 --- /dev/null +++ b/src/mod/applications/mod_protovm/util.h @@ -0,0 +1,16 @@ +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include "config.h" + +switch_status_t mt_merge_files(const char** inputs, const char *output); + +void append_event_message(switch_core_session_t *session, vmivr_profile_t *profile, switch_event_t *phrase_params, switch_event_t *msg_list_event, size_t current_msg); +void append_event_profile(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu); +char *generate_random_file_name(switch_core_session_t *session, const char *mod_name, char *file_extension); +switch_event_t *jsonapi2event(switch_core_session_t *session, switch_event_t *apply_event, const char *api, const char *data); +switch_status_t mt_merge_media_files(const char** inputs, const char *output); +switch_status_t mt_api_execute(switch_core_session_t *session, const char *apiname, const char *arguments); +void populate_dtmfa_from_event(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu, char **dtmfa); +#endif /* _UTIL_H_ */ +