/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, 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 -- VoiceMail IVR Engone * */ #include #include "ivr.h" static int match_dtmf(switch_core_session_t *session, ivre_data_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 < 16 && i < loc->dtmf_received; i++) { int j; loc->potentialMatchCount = 0; for (j = 0; j < 128 && !zstr(loc->dtmf_accepted[j]); 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) { ivre_data_t *loc = (ivre_data_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 ivre_init(ivre_data_t *loc, char **dtmf_accepted) { int i; memset(loc, 0, sizeof(*loc)); for (i = 0; dtmf_accepted[i] && i < 128; i++) { strncpy(loc->dtmf_accepted[i], dtmf_accepted[i], 16); } loc->record_tone = "%(1000, 0, 640)"; return SWITCH_STATUS_SUCCESS; } switch_status_t ivre_playback_dtmf_buffered(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 ivre_playback(switch_core_session_t *session, ivre_data_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 ivre_record(switch_core_session_t *session, ivre_data_t *loc, switch_event_t *event, const char *file_path, switch_file_handle_t *fh, int max_record_len, switch_size_t *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; switch_ivr_gentones(session, loc->record_tone, 0, NULL); status = switch_ivr_record_file(session, fh, file_path, &args, max_record_len); if (record_len) { *record_len = fh->samples_out / (fh->samplerate ? fh->samplerate : 8000); } } 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 noet */