diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 1fed73ca67..fb023848ec 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -846,24 +846,31 @@ SWITCH_DECLARE(switch_bool_t) switch_ivr_uuid_exists(const char *uuid); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_create(switch_ivr_dmachine_t **dmachine_p, - switch_memory_pool_t *pool, - uint32_t digit_timeout, uint32_t input_timeout); + const char *name, + switch_memory_pool_t *pool, + uint32_t digit_timeout, uint32_t input_timeout, + switch_ivr_dmachine_callback_t match_callback, + switch_ivr_dmachine_callback_t nonmatch_callback, + void *user_data); SWITCH_DECLARE(void) switch_ivr_dmachine_destroy(switch_ivr_dmachine_t **dmachine); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_bind(switch_ivr_dmachine_t *dmachine, - const char *digits, - int32_t key, - switch_ivr_dmachine_callback_t callback, - void *user_data); + const char *realm, + const char *digits, + int32_t key, + switch_ivr_dmachine_callback_t callback, + void *user_data); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_feed(switch_ivr_dmachine_t *dmachine, const char *digits, switch_ivr_dmachine_match_t **match); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_clear(switch_ivr_dmachine_t *dmachine); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_ping(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_match_t **match_p); SWITCH_DECLARE(switch_ivr_dmachine_match_t *) switch_ivr_dmachine_get_match(switch_ivr_dmachine_t *dmachine); +SWITCH_DECLARE(const char *) switch_ivr_dmachine_get_failed_digits(switch_ivr_dmachine_t *dmachine); SWITCH_DECLARE(void) switch_ivr_dmachine_set_digit_timeout_ms(switch_ivr_dmachine_t *dmachine, uint32_t digit_timeout_ms); SWITCH_DECLARE(void) switch_ivr_dmachine_set_input_timeout_ms(switch_ivr_dmachine_t *dmachine, uint32_t input_timeout_ms); - +SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_clear_realm(switch_ivr_dmachine_t *dmachine, const char *realm); +SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_set_realm(switch_ivr_dmachine_t *dmachine, const char *realm); /** @} */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index d67d972e92..b1fc627354 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -190,9 +190,14 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_DTMF_LOG_LEN 1000 typedef uint8_t switch_byte_t; +typedef enum { + DTMF_FLAG_SKIP_PROCESS = (1 << 0) +} dtmf_flag_t; + typedef struct { char digit; uint32_t duration; + int32_t flags; } switch_dtmf_t; typedef enum { @@ -1689,6 +1694,13 @@ typedef switch_status_t (*switch_input_callback_function_t) (switch_core_session typedef switch_status_t (*switch_read_frame_callback_function_t) (switch_core_session_t *session, switch_frame_t *frame, void *user_data); typedef struct switch_say_interface switch_say_interface_t; +#define DMACHINE_MAX_DIGIT_LEN 512 + +typedef enum { + DM_MATCH_POSITIVE, + DM_MATCH_NEGATIVE +} dm_match_type_t; + struct switch_ivr_dmachine; typedef struct switch_ivr_dmachine switch_ivr_dmachine_t; @@ -1696,6 +1708,7 @@ struct switch_ivr_dmachine_match { switch_ivr_dmachine_t *dmachine; const char *match_digits; int32_t match_key; + dm_match_type_t type; void *user_data; }; diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 675cde4884..f444f63cb2 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -96,12 +96,30 @@ SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt) } struct action_binding { + char *realm; char *input; char *string; char *value; switch_core_session_t *session; }; +static switch_status_t digit_nomatch_action_callback(switch_ivr_dmachine_match_t *match) +{ + switch_core_session_t *session = (switch_core_session_t *) match->user_data; + switch_channel_t *channel = switch_core_session_get_channel(session); + char str[DMACHINE_MAX_DIGIT_LEN + 2]; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Digit NOT match binding [%s]\n", + switch_channel_get_name(channel), match->match_digits); + + /* send it back around flagged to skip the dmachine */ + switch_snprintf(str, sizeof(str), "!%s", match->match_digits); + + switch_channel_queue_dtmf_string(channel, str); + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match) { struct action_binding *act = (struct action_binding *) match->user_data; @@ -141,32 +159,64 @@ static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match) return SWITCH_STATUS_SUCCESS; } -#define CLEAR_DIGIT_ACTION_USAGE "" +#define CLEAR_DIGIT_ACTION_USAGE "|all" SWITCH_STANDARD_APP(clear_digit_action_function) { //switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_dmachine_t *dmachine; - + char *realm = (char *) data; + if ((dmachine = switch_core_session_get_dmachine(session))) { - switch_core_session_set_dmachine(session, NULL); - switch_ivr_dmachine_destroy(&dmachine); + if (zstr(realm) || !strcasecmp(realm, "all")) { + switch_core_session_set_dmachine(session, NULL); + switch_ivr_dmachine_destroy(&dmachine); + } else { + switch_ivr_dmachine_clear_realm(dmachine, realm); + } } } -#define BIND_DIGIT_ACTION_USAGE ",," +#define DIGIT_ACTION_SET_REALM_USAGE "" +SWITCH_STANDARD_APP(digit_action_set_realm_function) +{ + switch_ivr_dmachine_t *dmachine; + char *realm = (char *) data; + + if (zstr(data)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", DIGIT_ACTION_SET_REALM_USAGE); + return; + } + + if ((dmachine = switch_core_session_get_dmachine(session))) { + switch_ivr_dmachine_set_realm(dmachine, realm); + } + +} + +#define BIND_DIGIT_ACTION_USAGE ",,," SWITCH_STANDARD_APP(bind_digit_action_function) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_dmachine_t *dmachine; char *mydata; int argc = 0; - char *argv[3] = { 0 }; + char *argv[4] = { 0 }; struct action_binding *act; if (zstr(data)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); return; } + + mydata = switch_core_session_strdup(session, data); + + argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 4 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2]) || zstr(argv[3])) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); + return; + } + if (!(dmachine = switch_core_session_get_dmachine(session))) { uint32_t digit_timeout = 1500; @@ -186,26 +236,19 @@ SWITCH_STANDARD_APP(bind_digit_action_function) input_timeout = tmp; } - switch_ivr_dmachine_create(&dmachine, NULL, digit_timeout, input_timeout); + switch_ivr_dmachine_create(&dmachine, "DPTOOLS", NULL, digit_timeout, input_timeout, NULL, digit_nomatch_action_callback, session); switch_core_session_set_dmachine(session, dmachine); } - mydata = switch_core_session_strdup(session, data); - - argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); - if (argc < 3 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2])) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); - return; - } - act = switch_core_session_alloc(session, sizeof(*act)); - act->input = argv[0]; - act->string = argv[1]; - act->value = argv[2]; + act->realm = argv[0]; + act->input = argv[1]; + act->string = argv[2]; + act->value = argv[3]; act->session = session; - switch_ivr_dmachine_bind(dmachine, act->input, 0, digit_action_callback, act); + switch_ivr_dmachine_bind(dmachine, act->realm, act->input, 0, digit_action_callback, act); } @@ -3398,6 +3441,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "clear_digit_action", "clear all digit bindings", "", clear_digit_action_function, CLEAR_DIGIT_ACTION_USAGE, SAF_SUPPORT_NOMEDIA); + SWITCH_ADD_APP(app_interface, "digit_action_set_realm", "change binding realm", "", + digit_action_set_realm_function, DIGIT_ACTION_SET_REALM_USAGE, SAF_SUPPORT_NOMEDIA); + SWITCH_ADD_APP(app_interface, "privacy", "Set privacy on calls", "Set caller privacy on calls.", privacy_function, "off|on|name|full|number", SAF_SUPPORT_NOMEDIA); diff --git a/src/switch_channel.c b/src/switch_channel.c index df5fd9627e..213f385b28 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -400,7 +400,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *chan SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf_string(switch_channel_t *channel, const char *dtmf_string) { char *p; - switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) }; + switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0), 0}; int sent = 0, dur; char *string; int i, argc; @@ -410,6 +410,11 @@ SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf_string(switch_channel_ return SWITCH_STATUS_FALSE; } + if (*dtmf_string == '!') { + dtmf_string++; + dtmf.flags = DTMF_FLAG_SKIP_PROCESS; + } + string = switch_core_session_strdup(channel->session, dtmf_string); argc = switch_separate_string(string, '+', argv, (sizeof(argv) / sizeof(argv[0]))); diff --git a/src/switch_core_io.c b/src/switch_core_io.c index 15b8f417d4..4e6d2fc6a2 100644 --- a/src/switch_core_io.c +++ b/src/switch_core_io.c @@ -1176,15 +1176,17 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_recv_dtmf(switch_core_sessio new_dtmf.duration = switch_core_default_dtmf_duration(0); } - if (session->dmachine && !switch_channel_test_flag(session->channel, CF_BROADCAST)) { - char str[2] = { dtmf->digit, '\0' }; - switch_ivr_dmachine_feed(session->dmachine, str, NULL); - fed = 1; - } + if (!switch_test_flag(dtmf, DTMF_FLAG_SKIP_PROCESS)) { + if (session->dmachine && !switch_channel_test_flag(session->channel, CF_BROADCAST)) { + char str[2] = { dtmf->digit, '\0' }; + switch_ivr_dmachine_feed(session->dmachine, str, NULL); + fed = 1; + } - for (ptr = session->event_hooks.recv_dtmf; ptr; ptr = ptr->next) { - if ((status = ptr->recv_dtmf(session, &new_dtmf, SWITCH_DTMF_RECV)) != SWITCH_STATUS_SUCCESS) { - return status; + for (ptr = session->event_hooks.recv_dtmf; ptr; ptr = ptr->next) { + if ((status = ptr->recv_dtmf(session, &new_dtmf, SWITCH_DTMF_RECV)) != SWITCH_STATUS_SUCCESS) { + return status; + } } } diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 57b4569346..9bfa27eae7 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -45,28 +45,41 @@ struct switch_ivr_dmachine_binding { }; typedef struct switch_ivr_dmachine_binding switch_ivr_dmachine_binding_t; -#define DM_MAX_DIGIT_LEN 512 +typedef struct { + switch_ivr_dmachine_binding_t *binding_list; + switch_ivr_dmachine_binding_t *tail; +} dm_binding_head_t; struct switch_ivr_dmachine { switch_memory_pool_t *pool; switch_byte_t my_pool; + char *name; uint32_t digit_timeout_ms; uint32_t input_timeout_ms; - switch_ivr_dmachine_binding_t *binding_list; - switch_ivr_dmachine_binding_t *tail; - switch_ivr_dmachine_binding_t *last_matching_binding; + switch_hash_t *binding_hash; switch_ivr_dmachine_match_t match; - char digits[DM_MAX_DIGIT_LEN]; - char last_matching_digits[DM_MAX_DIGIT_LEN]; + char digits[DMACHINE_MAX_DIGIT_LEN]; + char last_matching_digits[DMACHINE_MAX_DIGIT_LEN]; + char last_failed_digits[DMACHINE_MAX_DIGIT_LEN]; uint32_t cur_digit_len; uint32_t max_digit_len; switch_time_t last_digit_time; switch_byte_t is_match; + switch_ivr_dmachine_callback_t match_callback; + switch_ivr_dmachine_callback_t nonmatch_callback; + dm_binding_head_t *realm; + switch_ivr_dmachine_binding_t *last_matching_binding; + void *user_data; }; SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_create(switch_ivr_dmachine_t **dmachine_p, - switch_memory_pool_t *pool, - uint32_t digit_timeout_ms, uint32_t input_timeout_ms) + const char *name, + switch_memory_pool_t *pool, + uint32_t digit_timeout_ms, + uint32_t input_timeout_ms, + switch_ivr_dmachine_callback_t match_callback, + switch_ivr_dmachine_callback_t nonmatch_callback, + void *user_data) { switch_byte_t my_pool = !!pool; switch_ivr_dmachine_t *dmachine; @@ -81,8 +94,22 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_create(switch_ivr_dmachine_t dmachine->digit_timeout_ms = digit_timeout_ms; dmachine->input_timeout_ms = input_timeout_ms; dmachine->match.dmachine = dmachine; - *dmachine_p = dmachine; + dmachine->name = switch_core_strdup(dmachine->pool, name); + + switch_core_hash_init(&dmachine->binding_hash, dmachine->pool); + + if (match_callback) { + dmachine->match_callback = match_callback; + } + if (nonmatch_callback) { + dmachine->nonmatch_callback = nonmatch_callback; + } + + dmachine->user_data = user_data; + + *dmachine_p = dmachine; + return SWITCH_STATUS_SUCCESS; } @@ -105,12 +132,42 @@ SWITCH_DECLARE(void) switch_ivr_dmachine_destroy(switch_ivr_dmachine_t **dmachin pool = (*dmachine)->pool; + switch_core_hash_destroy(&(*dmachine)->binding_hash); + if ((*dmachine)->my_pool) { switch_core_destroy_memory_pool(&pool); } } +SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_set_realm(switch_ivr_dmachine_t *dmachine, const char *realm) +{ + dm_binding_head_t *headp = switch_core_hash_find(dmachine->binding_hash, realm); + + if (headp) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: Setting realm to %s\n", dmachine->name, realm); + dmachine->realm = headp; + return SWITCH_STATUS_SUCCESS; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error Setting realm to %s\n", dmachine->name, realm); + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_clear_realm(switch_ivr_dmachine_t *dmachine, const char *realm) +{ + if (zstr(realm)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error unknown realm: %s\n", dmachine->name, realm); + return SWITCH_STATUS_FALSE; + } + + /* pool alloc'd just ditch it and it will give back the memory when we destroy ourselves */ + switch_core_hash_delete(dmachine->binding_hash, realm); + return SWITCH_STATUS_SUCCESS; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_bind(switch_ivr_dmachine_t *dmachine, + const char *realm, const char *digits, int32_t key, switch_ivr_dmachine_callback_t callback, @@ -118,43 +175,59 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_bind(switch_ivr_dmachine_t * { switch_ivr_dmachine_binding_t *binding; switch_size_t len; + dm_binding_head_t *headp; - if (strlen(digits) > DM_MAX_DIGIT_LEN -1) { + if (strlen(digits) > DMACHINE_MAX_DIGIT_LEN -1) { return SWITCH_STATUS_FALSE; } + if (zstr(realm)) { + realm = "default"; + } + + if (!(headp = switch_core_hash_find(dmachine->binding_hash, realm))) { + headp = switch_core_alloc(dmachine->pool, sizeof(*headp)); + switch_core_hash_insert(dmachine->binding_hash, realm, headp); + } + binding = switch_core_alloc(dmachine->pool, sizeof(*binding)); + if (*digits == '~') { binding->is_regex = 1; digits++; } + binding->key = key; binding->digits = switch_core_strdup(dmachine->pool, digits); binding->callback = callback; binding->user_data = user_data; - if (dmachine->tail) { - dmachine->tail->next = binding; + if (headp->tail) { + headp->tail->next = binding; } else { - dmachine->binding_list = binding; + headp->binding_list = binding; } - dmachine->tail = binding; + headp->tail = binding; len = strlen(digits); - if (binding->is_regex && dmachine->max_digit_len != DM_MAX_DIGIT_LEN -1) { - dmachine->max_digit_len = DM_MAX_DIGIT_LEN -1; + if (dmachine->realm != headp) { + switch_ivr_dmachine_set_realm(dmachine, realm); + } + + if (binding->is_regex && dmachine->max_digit_len != DMACHINE_MAX_DIGIT_LEN -1) { + dmachine->max_digit_len = DMACHINE_MAX_DIGIT_LEN -1; } else if (len > dmachine->max_digit_len) { dmachine->max_digit_len = (uint32_t) len; } if (binding->is_regex) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "binding regex: %s key: %.4d callback: %p data: %p\n", - digits, key, (void *)(intptr_t) callback, user_data); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: binding realm: %s regex: %s key: %.4d callback: %p data: %p\n", + dmachine->name, realm, digits, key, (void *)(intptr_t) callback, user_data); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "binding digits: %4s key: %.4d callback: %p data: %p\n", - digits, key, (void *)(intptr_t) callback, user_data); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: binding realm %s digits: %4s key: %.4d callback: %p data: %p\n", + dmachine->name, realm, digits, key, (void *)(intptr_t) callback, user_data); } return SWITCH_STATUS_SUCCESS; @@ -175,9 +248,9 @@ static dm_match_t switch_ivr_dmachine_check_match(switch_ivr_dmachine_t *dmachin int exact_count = 0, partial_count = 0, both_count = 0; - if (!dmachine->cur_digit_len) goto end; + if (!dmachine->cur_digit_len || !dmachine->realm) goto end; - for(bp = dmachine->binding_list; bp; bp = bp->next) { + for(bp = dmachine->realm->binding_list; bp; bp = bp->next) { if (bp->is_regex) { switch_status_t r_status = switch_regex_match(dmachine->digits, bp->digits); @@ -259,12 +332,17 @@ SWITCH_DECLARE(switch_ivr_dmachine_match_t *) switch_ivr_dmachine_get_match(swit return NULL; } +SWITCH_DECLARE(const char *) switch_ivr_dmachine_get_failed_digits(switch_ivr_dmachine_t *dmachine) +{ + + return dmachine->last_failed_digits; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_ping(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_match_t **match_p) { switch_bool_t is_timeout = switch_ivr_dmachine_check_timeout(dmachine); dm_match_t is_match = switch_ivr_dmachine_check_match(dmachine, is_timeout); switch_status_t r; - int exec = 0; if (zstr(dmachine->digits) && !is_timeout) { r = SWITCH_STATUS_SUCCESS; @@ -282,7 +360,18 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_ping(switch_ivr_dmachine_t * } dmachine->is_match = 1; - exec = 1; + + dmachine->match.type = DM_MATCH_POSITIVE; + + if (dmachine->last_matching_binding->callback) { + dmachine->last_matching_binding->callback(&dmachine->match); + } + + if (dmachine->match_callback) { + dmachine->match.user_data = dmachine->user_data; + dmachine->match_callback(&dmachine->match); + } + } else if (is_timeout) { r = SWITCH_STATUS_TIMEOUT; } else if (dmachine->cur_digit_len == dmachine->max_digit_len) { @@ -291,14 +380,22 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_ping(switch_ivr_dmachine_t * r = SWITCH_STATUS_SUCCESS; } + if (r != SWITCH_STATUS_FOUND && r != SWITCH_STATUS_SUCCESS) { + switch_set_string(dmachine->last_failed_digits, dmachine->digits); + dmachine->match.match_digits = dmachine->last_failed_digits; + + dmachine->match.type = DM_MATCH_NEGATIVE; + + if (dmachine->nonmatch_callback) { + dmachine->match.user_data = dmachine->user_data; + dmachine->nonmatch_callback(&dmachine->match); + } + } + if (r != SWITCH_STATUS_SUCCESS) { switch_ivr_dmachine_clear(dmachine); } - if (exec && dmachine->last_matching_binding->callback) { - dmachine->last_matching_binding->callback(&dmachine->match); - } - return r; }