summaryrefslogtreecommitdiff
path: root/main/pbx.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/pbx.c')
-rw-r--r--main/pbx.c299
1 files changed, 212 insertions, 87 deletions
diff --git a/main/pbx.c b/main/pbx.c
index a8059a441..419b42cfc 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -314,6 +314,7 @@ static int pbx_builtin_saynumber(struct ast_channel *, void *);
static int pbx_builtin_saydigits(struct ast_channel *, void *);
static int pbx_builtin_saycharacters(struct ast_channel *, void *);
static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
+static int matchcid(const char *cidpattern, const char *callerid);
int pbx_builtin_setvar(struct ast_channel *, void *);
void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
static int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
@@ -408,6 +409,7 @@ AST_RWLOCK_DEFINE_STATIC(globalslock);
static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
static int autofallthrough = 1;
+static int extenpatternmatchnew = 0;
/*! \brief Subscription for device state change events */
static struct ast_event_sub *device_state_sub;
@@ -909,6 +911,12 @@ static struct ast_exten *trie_find_next_match(struct match_char *node)
struct match_char *m4;
struct ast_exten *e3;
+ if (node && node->x[0] == '.' && !node->x[1]) /* dot and ! will ALWAYS be next match in a matchmore */
+ return node->exten;
+
+ if (node && node->x[0] == '!' && !node->x[1])
+ return node->exten;
+
if (!node || !node->next_char)
return NULL;
@@ -931,9 +939,15 @@ static struct ast_exten *trie_find_next_match(struct match_char *node)
static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid)
{
struct match_char *p; /* note minimal stack storage requirements */
+#ifdef DEBUG_THIS
+ if (tree)
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree %s\n", str, tree->x);
+ else
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree NULL\n", str);
+#endif
for (p=tree; p; p=p->alt_char) {
if (p->x[0] == 'N' && p->x[1] == 0 && *str >= '2' && *str <= '9' ) {
- if (p->exten) /* if a shorter pattern matches along the way, might as well report it */
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
@@ -948,7 +962,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
return;
}
} else if (p->x[0] == 'Z' && p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
- if (p->exten) /* if a shorter pattern matches along the way, might as well report it */
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
@@ -963,7 +977,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
return;
}
} else if (p->x[0] == 'X' && p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
- if (p->exten) /* if a shorter pattern matches along the way, might as well report it */
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
@@ -980,10 +994,11 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
} else if (p->x[0] == '.' && p->x[1] == 0) {
/* how many chars will the . match against? */
int i = 0;
- while (*str++) {
+ const char *str2 = str;
+ while (*str2++) {
i++;
}
- if (p->exten)
+ if (p->exten && !(*(str+1)))
update_scoreboard(score, length+i, spec+(i*p->specificity), p->exten, '.', callerid, p->deleted, p);
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
@@ -992,10 +1007,11 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
} else if (p->x[0] == '!' && p->x[1] == 0) {
/* how many chars will the . match against? */
int i = 0;
- while (*str++) {
+ const char *str2 = str;
+ while (*str2++) {
i++;
}
- if (p->exten)
+ if (p->exten && !(*(str+1)))
update_scoreboard(score, length+1, spec+(p->specificity*i), p->exten, '!', callerid, p->deleted, p);
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
@@ -1007,7 +1023,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
new_find_extension(callerid, score, p->next_char, length+1, spec, callerid);
}
} else if (index(p->x, *str)) {
- if (p->exten) /* if a shorter pattern matches along the way, might as well report it */
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
@@ -1153,10 +1169,19 @@ static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, str
*s2 = 0; /* null terminate the exploded range */
specif = strlen(buf);
} else {
+
if (*s1 == '\\') {
s1++;
buf[0] = *s1;
} else {
+ if (pattern) {
+ if (*s1 == 'n') /* make sure n,x,z patterns are canonicalized to N,X,Z */
+ *s1 = 'N';
+ else if (*s1 == 'x')
+ *s1 = 'X';
+ else if (*s1 == 'z')
+ *s1 = 'Z';
+ }
buf[0] = *s1;
}
buf[1] = 0;
@@ -1566,6 +1591,17 @@ struct ast_context *ast_context_find(const char *name)
#define STATUS_NO_LABEL 4
#define STATUS_SUCCESS 5
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (ast_strlen_zero(callerid))
+ return ast_strlen_zero(cidpattern) ? 1 : 0;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
struct ast_exten *pbx_find_extension(struct ast_channel *chan,
struct ast_context *bypass, struct pbx_find_info *q,
const char *context, const char *exten, int priority,
@@ -1639,96 +1675,117 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
#ifdef NEED_DEBUG
ast_log(LOG_NOTICE,"The Trie we are searching in:\n");
log_match_char_tree(tmp->pattern_tree, ":: ");
-#endif
- new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid);
- eroot = score.exten;
-
- if (score.last_char == '!' && action == E_MATCHMORE) {
- /* We match an extension ending in '!'.
- * The decision in this case is final and is NULL (no match).
- */
- return NULL;
- }
-
- if (!eroot && (action == E_CANMATCH || action == E_MATCHMORE) && score.canmatch_exten) {
- q->status = STATUS_SUCCESS;
- return score.canmatch_exten;
- }
-
- if (action == E_MATCHMORE && eroot) {
- if (score.node) {
- struct ast_exten *z = trie_find_next_match(score.node);
- return z;
- }
- return NULL; /* according to the code, complete matches are null matches in MATCHMORE mode */
- }
-
-
- if (eroot) {
- /* found entry, now look for the right priority */
- if (q->status < STATUS_NO_PRIORITY)
- q->status = STATUS_NO_PRIORITY;
- e = NULL;
- if (action == E_FINDLABEL && label ) {
- if (q->status < STATUS_NO_LABEL)
- q->status = STATUS_NO_LABEL;
- e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
- } else {
- e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
- }
- if (e) { /* found a valid match */
- q->status = STATUS_SUCCESS;
- q->foundcontext = context;
- return e;
- }
- }
-
-#ifdef NOTNOW2
- /* scan the list trying to match extension and CID */
- eroot = NULL;
- while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
- int match = extension_match_core(eroot->exten, exten, action);
- /* 0 on fail, 1 on match, 2 on earlymatch */
+#endif
- if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
- continue; /* keep trying */
- if (match == 2 && action == E_MATCHMORE) {
+ if (extenpatternmatchnew) {
+ new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid);
+ eroot = score.exten;
+
+ if (score.last_char == '!' && action == E_MATCHMORE) {
/* We match an extension ending in '!'.
* The decision in this case is final and is NULL (no match).
*/
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning MATCHMORE NULL with exclamation point.\n");
+#endif
return NULL;
}
- /* found entry, now look for the right priority */
- if (q->status < STATUS_NO_PRIORITY)
- q->status = STATUS_NO_PRIORITY;
- e = NULL;
- if (action == E_FINDLABEL && label ) {
- if (q->status < STATUS_NO_LABEL)
- q->status = STATUS_NO_LABEL;
- e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
- } else {
- e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+
+ if (!eroot && (action == E_CANMATCH || action == E_MATCHMORE) && score.canmatch_exten) {
+ q->status = STATUS_SUCCESS;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH exten %s\n", score.canmatch_exten->exten);
+#endif
+ return score.canmatch_exten;
}
-#ifdef NOTNOW
- while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
- /* Match label or priority */
- if (action == E_FINDLABEL) {
+
+ if ((action == E_MATCHMORE || action == E_CANMATCH) && eroot) {
+ if (score.node) {
+ struct ast_exten *z = trie_find_next_match(score.node);
+#ifdef NEED_DEBUG
+ if (z)
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten %s\n", z->exten);
+ else
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten NULL\n");
+#endif
+ return z;
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE NULL (no next_match)\n");
+#endif
+ return NULL; /* according to the code, complete matches are null matches in MATCHMORE mode */
+ }
+
+ if (eroot) {
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
if (q->status < STATUS_NO_LABEL)
q->status = STATUS_NO_LABEL;
- if (label && e->label && !strcmp(label, e->label))
- break; /* found it */
- } else if (e->priority == priority) {
- break; /* found it */
- } /* else keep searching */
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning complete match of exten %s\n", e->exten);
+#endif
+ return e;
+ }
}
+ } else {
+
+ /* scan the list trying to match extension and CID */
+ eroot = NULL;
+ while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
+ int match = extension_match_core(eroot->exten, exten, action);
+ /* 0 on fail, 1 on match, 2 on earlymatch */
+
+ if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
+ continue; /* keep trying */
+ if (match == 2 && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+ return NULL;
+ }
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+#ifdef NOTNOW
+ while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
+ /* Match label or priority */
+ if (action == E_FINDLABEL) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label))
+ break; /* found it */
+ } else if (e->priority == priority) {
+ break; /* found it */
+ } /* else keep searching */
+ }
#endif
- if (e) { /* found a valid match */
- q->status = STATUS_SUCCESS;
- q->foundcontext = context;
- return e;
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+ return e;
+ }
}
}
-#endif
+
+
/* Check alternative switches */
AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
struct ast_switch *asw = pbx_findswitch(sw->name);
@@ -1765,9 +1822,12 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
/* Now try any includes we have in this context */
for (i = tmp->includes; i; i = i->next) {
if (include_valid(i)) {
- if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
+ if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
+#endif
return e;
-
+ }
if (q->swo)
return NULL;
}
@@ -3497,6 +3557,13 @@ int pbx_set_autofallthrough(int newval)
return oldval;
}
+int pbx_set_extenpatternmatchnew(int newval)
+{
+ int oldval = extenpatternmatchnew;
+ extenpatternmatchnew = newval;
+ return oldval;
+}
+
/*!
* \brief lookup for a context with a given name,
* \retval with conlock held if found.
@@ -4777,6 +4844,62 @@ static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_
return CLI_SUCCESS;
}
+static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpaternmatchnew true";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(1);
+
+ if (oldval)
+ ast_cli(a->fd, "\n -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpaternmatchnew false";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(0);
+
+ if (!oldval)
+ ast_cli(a->fd, "\n -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
/*
* CLI entries for upper commands ...
*/
@@ -4790,6 +4913,8 @@ static struct ast_cli_entry pbx_cli[] = {
AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
+ AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
+ AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
};
static void unreference_cached_app(struct ast_app *app)