Version 0.1.3 from FTP
[asterisk/asterisk.git] / pbx.c
diff --git a/pbx.c b/pbx.c
index a95d415..3c8c17c 100755 (executable)
--- a/pbx.c
+++ b/pbx.c
@@ -3,7 +3,7 @@
  *
  * Core PBX routines.
  * 
  *
  * Core PBX routines.
  * 
- * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ * Copyright (C) 1999, Mark Spencer
  *
  * Mark Spencer <markster@linux-support.net>
  *
  *
  * Mark Spencer <markster@linux-support.net>
  *
@@ -23,7 +23,7 @@
 #include <stdio.h>
 #include <setjmp.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <setjmp.h>
 #include <ctype.h>
-
+#include "asterisk.h"
 
 /*
  * I M P O R T A N T :
 
 /*
  * I M P O R T A N T :
@@ -82,6 +82,8 @@ struct ast_app {
        struct ast_app *next;
 };
 
        struct ast_app *next;
 };
 
+static int pbx_builtin_prefix(struct ast_channel *, void *);
+static int pbx_builtin_stripmsd(struct ast_channel *, void *);
 static int pbx_builtin_answer(struct ast_channel *, void *);
 static int pbx_builtin_goto(struct ast_channel *, void *);
 static int pbx_builtin_hangup(struct ast_channel *, void *);
 static int pbx_builtin_answer(struct ast_channel *, void *);
 static int pbx_builtin_goto(struct ast_channel *, void *);
 static int pbx_builtin_hangup(struct ast_channel *, void *);
@@ -104,6 +106,8 @@ static struct pbx_builtin {
        { "ResponseTimeout", pbx_builtin_rtimeout },
        { "BackGround", pbx_builtin_background },
        { "Wait", pbx_builtin_wait },
        { "ResponseTimeout", pbx_builtin_rtimeout },
        { "BackGround", pbx_builtin_background },
        { "Wait", pbx_builtin_wait },
+       { "StripMSD", pbx_builtin_stripmsd },
+       { "Prefix", pbx_builtin_prefix },
 };
 
 /* Lock for the application list */
 };
 
 /* Lock for the application list */
@@ -155,6 +159,7 @@ static int pbx_exec(struct ast_channel *c, /* Channel */
 #define HELPER_EXISTS 0
 #define HELPER_SPAWN 1
 #define HELPER_EXEC 2
 #define HELPER_EXISTS 0
 #define HELPER_SPAWN 1
 #define HELPER_EXEC 2
+#define HELPER_CANMATCH 3
 
 static struct ast_app *pbx_findapp(char *app) 
 {
 
 static struct ast_app *pbx_findapp(char *app) 
 {
@@ -178,7 +183,7 @@ static void pbx_destroy(struct ast_pbx *p)
        free(p);
 }
 
        free(p);
 }
 
-int extension_match(char *pattern, char *data)
+static int extension_match(char *pattern, char *data)
 {
        int match;
        /* If they're the same return */
 {
        int match;
        /* If they're the same return */
@@ -213,15 +218,52 @@ int extension_match(char *pattern, char *data)
        return match;
 }
 
        return match;
 }
 
+static int extension_close(char *pattern, char *data)
+{
+       int match;
+       /* If "data" is longer, it can'be a subset of pattern */
+       if (strlen(pattern) < strlen(data)) 
+               return 0;
+       
+       if (!strncasecmp(pattern, data, strlen(data))) {
+               return 1;
+       }
+       /* All patterns begin with _ */
+       if (pattern[0] != '_') 
+               return 0;
+       /* Start optimistic */
+       match=1;
+       pattern++;
+       while(match && *data && *pattern) {
+               switch(toupper(*pattern)) {
+               case 'N':
+                       if ((*data < '2') || (*data > '9'))
+                               match=0;
+                       break;
+               case 'X':
+                       if ((*data < '0') || (*data > '9'))
+                               match = 0;
+                       break;
+               default:
+                       if (*data != *pattern)
+                               match =0;
+               }
+               data++;
+               pattern++;
+       }
+       return match;
+}
+
 static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, int action) 
 {
        struct ast_context *tmp;
        struct ast_exten *e;
        struct ast_app *app;
        int newstack = 0;
 static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, int action) 
 {
        struct ast_context *tmp;
        struct ast_exten *e;
        struct ast_app *app;
        int newstack = 0;
+       int res;
        if (pthread_mutex_lock(&conlock)) {
                ast_log(LOG_WARNING, "Unable to obtain lock\n");
        if (pthread_mutex_lock(&conlock)) {
                ast_log(LOG_WARNING, "Unable to obtain lock\n");
-               if (action == HELPER_EXISTS)
+               if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH))
                        return 0;
                else
                        return -1;
                        return 0;
                else
                        return -1;
@@ -229,27 +271,32 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
        tmp = contexts;
        while(tmp) {
                if (!strcasecmp(tmp->name, context)) {
        tmp = contexts;
        while(tmp) {
                if (!strcasecmp(tmp->name, context)) {
+#if 0
                        /* By locking tmp, not only can the state of its entries not
                           change, but it cannot be destroyed either. */
                        pthread_mutex_lock(&tmp->lock);
                        /* By locking tmp, not only can the state of its entries not
                           change, but it cannot be destroyed either. */
                        pthread_mutex_lock(&tmp->lock);
-                       /* But we can relieve the conlock, as tmp will not change */
-                       pthread_mutex_unlock(&conlock);
+#endif
                        e = tmp->root;
                        while(e) {
                        e = tmp->root;
                        while(e) {
-                               if (extension_match(e->exten, exten)) {
+                               if (extension_match(e->exten, exten) || 
+                                       ((action == HELPER_CANMATCH) && extension_close(e->exten, exten))) {
                                        while(e) {
                                                if (e->priority == priority) {
                                        while(e) {
                                                if (e->priority == priority) {
-                                                       pthread_mutex_unlock(&tmp->lock);
                                                        /* We have a winner! Maybe there are some races
                                                           in here though. XXX */
                                                        switch(action) {
                                                        /* We have a winner! Maybe there are some races
                                                           in here though. XXX */
                                                        switch(action) {
+                                                       case HELPER_CANMATCH:
+                                                               pthread_mutex_unlock(&conlock);
+                                                               return -1;
                                                        case HELPER_EXISTS:
                                                        case HELPER_EXISTS:
+                                                               pthread_mutex_unlock(&conlock);
                                                                return -1;
                                                        case HELPER_SPAWN:
                                                                newstack++;
                                                                /* Fall through */
                                                        case HELPER_EXEC:
                                                                app = pbx_findapp(e->app);
                                                                return -1;
                                                        case HELPER_SPAWN:
                                                                newstack++;
                                                                /* Fall through */
                                                        case HELPER_EXEC:
                                                                app = pbx_findapp(e->app);
+                                                               pthread_mutex_unlock(&conlock);
                                                                if (app) {
                                                                        strncpy(c->context, context, sizeof(c->context));
                                                                        strncpy(c->exten, exten, sizeof(c->exten));
                                                                if (app) {
                                                                        strncpy(c->context, context, sizeof(c->context));
                                                                        strncpy(c->exten, exten, sizeof(c->exten));
@@ -259,7 +306,13 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                                        else if (option_verbose > 2)
                                                                                ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", 
                                                                                                app->name, c->name, (e->data ? (char *)e->data : NULL), (newstack ? "in new stack" : "in same stack"));
                                                                        else if (option_verbose > 2)
                                                                                ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", 
                                                                                                app->name, c->name, (e->data ? (char *)e->data : NULL), (newstack ? "in new stack" : "in same stack"));
-                                                                       return pbx_exec(c, app->execute, e->data, newstack);
+                                                                       c->appl = app->name;
+                                                                       c->data = e->data;              
+                                                                       res = pbx_exec(c, app->execute, e->data, newstack);
+                                                                       c->appl = NULL;
+                                                                       c->data = NULL;
+                                                                       pthread_mutex_unlock(&conlock);
+                                                                       return res;
                                                                } else {
                                                                        ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
                                                                        return -1;
                                                                } else {
                                                                        ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
                                                                        return -1;
@@ -271,20 +324,25 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                e = e->peer;
                                        }
                                        pthread_mutex_unlock(&tmp->lock);
                                                e = e->peer;
                                        }
                                        pthread_mutex_unlock(&tmp->lock);
-                                       if (action != HELPER_EXISTS) {
+                                       if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH)) {
                                                ast_log(LOG_WARNING, "No such priority '%d' in '%s' in '%s'\n", priority, exten, context);
                                                ast_log(LOG_WARNING, "No such priority '%d' in '%s' in '%s'\n", priority, exten, context);
+                                               pthread_mutex_unlock(&conlock);
                                                return -1;
                                                return -1;
-                                       } else
+                                       } else {
+                                               pthread_mutex_unlock(&conlock);
                                                return 0;
                                                return 0;
+                                       }
                                }
                                e = e->next;
                        }
                                }
                                e = e->next;
                        }
-                       pthread_mutex_unlock(&tmp->lock);
-                       if (action != HELPER_EXISTS) {
+                       if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH)) {
+                               pthread_mutex_unlock(&conlock);
                                ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context);
                                return -1;
                                ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context);
                                return -1;
-                       } else
+                       } else {
+                               pthread_mutex_unlock(&conlock);
                                return 0;
                                return 0;
+                       }
                }
                tmp = tmp->next;
        }
                }
                tmp = tmp->next;
        }
@@ -332,6 +390,11 @@ int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int
        return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS);
 }
 
        return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS);
 }
 
+int ast_canmatch_extension(struct ast_channel *c, char *context, char *exten, int priority)
+{
+       return pbx_extension_helper(c, context, exten, priority, HELPER_CANMATCH);
+}
+
 int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int priority) 
 {
        return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN);
 int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int priority) 
 {
        return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN);
@@ -376,14 +439,13 @@ static void *pbx_thread(void *data)
                                goto out;
                        }
                        /* If we're playing something in the background, wait for it to finish or for a digit */
                                goto out;
                        }
                        /* If we're playing something in the background, wait for it to finish or for a digit */
-                       if (c->stream) {
+                       if (c->stream || (c->trans && c->trans->stream)) {
                                digit = ast_waitstream(c, AST_DIGIT_ANY);
                                ast_stopstream(c);
                                /* Hang up if something goes wrong */
                                if (digit < 0)
                                        goto out;
                                else if (digit) {
                                digit = ast_waitstream(c, AST_DIGIT_ANY);
                                ast_stopstream(c);
                                /* Hang up if something goes wrong */
                                if (digit < 0)
                                        goto out;
                                else if (digit) {
-                                       ast_stopstream(c);
                                        exten[pos++] = digit;
                                        break;
                                }
                                        exten[pos++] = digit;
                                        break;
                                }
@@ -396,8 +458,8 @@ static void *pbx_thread(void *data)
                        waittime = c->pbx->dtimeout;
                else
                        waittime = c->pbx->rtimeout;
                        waittime = c->pbx->dtimeout;
                else
                        waittime = c->pbx->rtimeout;
-               while(!ast_exists_extension(c, c->context, exten, 1) && (
-                      strlen(exten) < ast_pbx_longest_extension(c->context))) {
+               while(!ast_exists_extension(c, c->context, exten, 1) && 
+                     ast_canmatch_extension(c, c->context, exten, 1)) {
                        /* As long as we're willing to wait, and as long as it's not defined, 
                           keep reading digits until we can't possibly get a right answer anymore.  */
                        digit = ast_waitfordigit(c, waittime * 1000);
                        /* As long as we're willing to wait, and as long as it's not defined, 
                           keep reading digits until we can't possibly get a right answer anymore.  */
                        digit = ast_waitfordigit(c, waittime * 1000);
@@ -466,6 +528,9 @@ int ast_pbx_start(struct ast_channel *c)
                return -1;
        }
        memset(c->pbx, 0, sizeof(struct ast_pbx));
                return -1;
        }
        memset(c->pbx, 0, sizeof(struct ast_pbx));
+       /* Set reasonable defaults */
+       c->pbx->rtimeout = 10;
+       c->pbx->dtimeout = 5;
        /* Start a new thread, and get something handling this channel. */
        if (pthread_create(&t, NULL, pbx_thread, c)) {
                ast_log(LOG_WARNING, "Failed to create new channel thread\n");
        /* Start a new thread, and get something handling this channel. */
        if (pthread_create(&t, NULL, pbx_thread, c)) {
                ast_log(LOG_WARNING, "Failed to create new channel thread\n");
@@ -748,7 +813,8 @@ void ast_context_destroy(struct ast_context *con)
 int pbx_builtin_answer(struct ast_channel *chan, void *data)
 {
        if (chan->state != AST_STATE_RING) {
 int pbx_builtin_answer(struct ast_channel *chan, void *data)
 {
        if (chan->state != AST_STATE_RING) {
-               ast_log(LOG_WARNING, "Ignoring answer request since line is not ringing\n");
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Ignoring answer request since line is not ringing\n");
                return 0;
        } else
                return ast_answer(chan);
                return 0;
        } else
                return ast_answer(chan);
@@ -760,6 +826,32 @@ int pbx_builtin_hangup(struct ast_channel *chan, void *data)
        return -1;
 }
 
        return -1;
 }
 
+int pbx_builtin_stripmsd(struct ast_channel *chan, void *data)
+{
+       char newexten[AST_MAX_EXTENSION] = "";
+       if (!data || !atoi(data)) {
+               ast_log(LOG_DEBUG, "Ignoring, since number of digits to strip is 0\n");
+               return 0;
+       }
+       if (strlen(chan->exten) > atoi(data)) {
+               strncpy(newexten, chan->exten + atoi(data), sizeof(newexten));
+       }
+       strncpy(chan->exten, newexten, sizeof(chan->exten));
+       return 0;
+}
+
+int pbx_builtin_prefix(struct ast_channel *chan, void *data)
+{
+       char newexten[AST_MAX_EXTENSION] = "";
+       if (!data || !strlen(data)) {
+               ast_log(LOG_DEBUG, "Ignoring, since there is no prefix to add\n");
+               return 0;
+       }
+       snprintf(newexten, sizeof(newexten), "%s%s", (char *)data, chan->exten);
+       strncpy(chan->exten, newexten, sizeof(chan->exten));
+       return 0;
+}
+
 int pbx_builtin_wait(struct ast_channel *chan, void *data)
 {
        /* Wait for "n" seconds */
 int pbx_builtin_wait(struct ast_channel *chan, void *data)
 {
        /* Wait for "n" seconds */
@@ -836,7 +928,6 @@ int pbx_builtin_goto(struct ast_channel *chan, void *data)
                ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
        return 0;
 }
                ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
        return 0;
 }
-
 int load_pbx(void)
 {
        int x;
 int load_pbx(void)
 {
        int x;