]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
improve dmachine stuff some more
authorAnthony Minessale <anthm@freeswitch.org>
Fri, 8 Oct 2010 18:50:15 +0000 (13:50 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Fri, 8 Oct 2010 18:50:15 +0000 (13:50 -0500)
src/include/switch_ivr.h
src/include/switch_types.h
src/mod/applications/mod_dptools/mod_dptools.c
src/switch_channel.c
src/switch_core_io.c
src/switch_ivr_async.c

index 1fed73ca670aec7ea39a3892f74b53e080987940..fb023848eca77b6ca187f1706b31aab1fc93793d 100644 (file)
@@ -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);
 
 /** @} */
 
index d67d972e92beddb8451063e9b3d5df6e0d732cd3..b1fc6273548787ce2043f66856a23d5fcac9983c 100644 (file)
@@ -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;
 };
 
index 675cde48840a935b62459b0afa631f4fc31d3ca1..f444f63cb287b1704ce3c5f074605459593eb813 100755 (executable)
@@ -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 "<realm>|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))) {
+               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 DIGIT_ACTION_SET_REALM_USAGE "<realm>"
+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_core_session_set_dmachine(session, NULL);
-               switch_ivr_dmachine_destroy(&dmachine);
+               switch_ivr_dmachine_set_realm(dmachine, realm);
        }
+
 }
 
-#define BIND_DIGIT_ACTION_USAGE "<digits|~regex>,<string>,<value>"
+#define BIND_DIGIT_ACTION_USAGE "<realm>,<digits|~regex>,<string>,<value>"
 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);
index df5fd9627ee962c0e02dd840593ac5f271e635de..213f385b28c8dffadb33d0df0f4a52b54fa9fe19 100644 (file)
@@ -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])));
 
index 15b8f417d4730c964a5c4bd5fd6f21f9a55d3aa7..4e6d2fc6a2dc013b669485ac5427c34a964baee7 100644 (file)
@@ -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;
+                       }
                }
        }
 
index 57b45693466cae2877a37fcab725ff232a34feff..9bfa27eae77c0c6b9d3d7e303e2ad2fe7303f37f 100644 (file)
@@ -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;
 }