Add incomplete matching to PBX code and app_dial
authorTilghman Lesher <tilghman@meg.abyt.es>
Mon, 28 Apr 2008 16:37:45 +0000 (16:37 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Mon, 28 Apr 2008 16:37:45 +0000 (16:37 +0000)
(closes issue #12351)
 Reported by: Corydon76
 Patches:
       20080402__pbx_incomplete__3.diff.txt uploaded by Corydon76 (license 14)
       pbx_incomplete_with_timeout.diff uploaded by fabled (license 448)
 Tested by: Corydon76, fabled

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@114773 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c
include/asterisk/pbx.h
main/pbx.c

index 0cc865a..5730d99 100644 (file)
@@ -1619,6 +1619,13 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                } else { /* Nobody answered, next please? */
                        res = 0;
                }
+
+               /* SIP, in particular, sends back this error code to indicate an
+                * overlap dialled number needs more digits. */
+               if (chan->hangupcause == AST_CAUSE_INVALID_NUMBER_FORMAT) {
+                       res = AST_PBX_INCOMPLETE;
+               }
+
                /* almost done, although the 'else' block is 400 lines */
        } else {
                const char *number;
@@ -1958,7 +1965,7 @@ out:
        senddialendevent(chan, pa.status);
        ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
 
-       if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE)) {
+       if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE) && (res != AST_PBX_INCOMPLETE)) {
                if (calldurationlimit)
                        chan->whentohangup = 0;
                res = 0;
@@ -2059,9 +2066,9 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
                        }
                }
 
-               if (res < 0)
+               if (res < 0 || res == AST_PBX_INCOMPLETE) {
                        break;
-               else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
+               } else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
                        if (onedigit_goto(chan, context, (char) res, 1)) {
                                res = 0;
                                break;
index fa0ceb0..d38325b 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 #define AST_PBX_ERROR          1       /*!< Jump to the 'e' exten */
 #define AST_PBX_KEEPALIVE      10      /*!< Destroy the thread, but don't hang up the channel */
 #define AST_PBX_NO_HANGUP_PEER 11
+#define AST_PBX_INCOMPLETE     12      /*!< Return to PBX matching, allowing more digits for the extension */
 /*! } */
 
 #define PRIORITY_HINT  -1      /*!< Special Priority for a hint */
index fc2bc98..92588bd 100644 (file)
@@ -302,6 +302,7 @@ static int pbx_builtin_hangup(struct ast_channel *, void *);
 static int pbx_builtin_background(struct ast_channel *, void *);
 static int pbx_builtin_wait(struct ast_channel *, void *);
 static int pbx_builtin_waitexten(struct ast_channel *, void *);
+static int pbx_builtin_incomplete(struct ast_channel *, void *);
 static int pbx_builtin_keepalive(struct ast_channel *, void *);
 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
@@ -579,6 +580,23 @@ static struct pbx_builtin {
        "value.\n"
        },
 
+       { "Incomplete", pbx_builtin_incomplete,
+       "returns AST_PBX_INCOMPLETE value",
+       "  Incomplete([n]): Signals the PBX routines that the previous matched extension\n"
+       "is incomplete and that further input should be allowed before matching can\n"
+       "be considered to be complete.  Can be used within a pattern match when\n"
+       "certain criteria warrants a longer match.\n"
+       "  If the 'n' option is specified, then Incomplete will not attempt to answer\n"
+       "the channel first.  Note that most channel types need to be in Answer state\n"
+       "in order to receive DTMF.\n"
+       },
+
+       { "KeepAlive", pbx_builtin_keepalive,
+       "returns AST_PBX_KEEPALIVE value",
+       "  KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
+       "Please do not run it alone from the dialplan!\n"
+       },
+
        { "NoOp", pbx_builtin_noop,
        "Do Nothing (No Operation)",
        "  NoOp(): This application does nothing. However, it is useful for debugging\n"
@@ -696,12 +714,6 @@ static struct pbx_builtin {
        "See Also: Playback(application), Background(application).\n"
        },
 
-       { "KeepAlive", pbx_builtin_keepalive,
-       "returns AST_PBX_KEEPALIVE value",
-       "  KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
-       "Please do not run it alone from the dialplan!\n"
-       },
-
 };
 
 static struct ast_context *contexts;
@@ -3582,6 +3594,8 @@ static int __ast_pbx_run(struct ast_channel *c)
                char dst_exten[256];    /* buffer to accumulate digits */
                int pos = 0;            /* XXX should check bounds */
                int digit = 0;
+               int invalid = 0;
+               int timeout = 0;
 
                /* loop on priorities in this context/exten */
                while ( !(res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found,1))) {
@@ -3617,6 +3631,18 @@ static int __ast_pbx_run(struct ast_channel *c)
                                ast_debug(1, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
                                ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
                                error = 1;
+                       } else if (res == AST_PBX_INCOMPLETE) {
+                               ast_debug(1, "Spawn extension (%s,%s,%d) exited INCOMPLETE on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               ast_verb(2, "Spawn extension (%s, %s, %d) exited INCOMPLETE on '%s'\n", c->context, c->exten, c->priority, c->name);
+
+                               /* Don't cycle on incomplete - this will happen if the only extension that matches is our "incomplete" extension */
+                               if (!ast_matchmore_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+                                       invalid = 1;
+                               } else {
+                                       ast_copy_string(dst_exten, c->exten, sizeof(dst_exten));
+                                       digit = 1;
+                                       pos = strlen(dst_exten);
+                               }
                        } else {
                                ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
                                ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
@@ -3657,7 +3683,7 @@ static int __ast_pbx_run(struct ast_channel *c)
                 * hangup.  We have options, here.  We can either catch the failure
                 * and continue, or we can drop out entirely. */
 
-               if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
+               if (invalid || !ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
                        /*!\note
                         * If there is no match at priority 1, it is not a valid extension anymore.
                         * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
@@ -3701,11 +3727,13 @@ static int __ast_pbx_run(struct ast_channel *c)
 
                        if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
                                break;
-                       if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
+                       if (res == AST_PBX_INCOMPLETE && ast_strlen_zero(&dst_exten[pos]))
+                               timeout = 1;
+                       if (!timeout && ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
                                set_ext_pri(c, dst_exten, 1);
                        else {
                                /* No such extension */
-                               if (!ast_strlen_zero(dst_exten)) {
+                               if (!timeout && !ast_strlen_zero(dst_exten)) {
                                        /* An invalid extension */
                                        if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
                                                ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
@@ -7365,6 +7393,26 @@ static int pbx_builtin_keepalive(struct ast_channel *chan, void *data)
        return AST_PBX_KEEPALIVE;
 }
 
+static int pbx_builtin_incomplete(struct ast_channel *chan, void *data)
+{
+       char *options = data;
+       int answer = 1;
+
+       /* Some channels can receive DTMF in unanswered state; some cannot */
+       if (!ast_strlen_zero(options) && strchr(options, 'n')) {
+               answer = 0;
+       }
+
+       /* If the channel is hungup, stop waiting */
+       if (ast_check_hangup(chan)) {
+               return -1;
+       } else if (chan->_state != AST_STATE_UP && answer) {
+               __ast_answer(chan, 0);
+       }
+
+       return AST_PBX_INCOMPLETE;
+}
+
 AST_APP_OPTIONS(resetcdr_opts, {
        AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
        AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),