Version 0.1.7 from FTP
[asterisk/asterisk.git] / pbx.c
diff --git a/pbx.c b/pbx.c
index a95d415..c84784b 100755 (executable)
--- a/pbx.c
+++ b/pbx.c
@@ -3,7 +3,7 @@
  *
  * Core PBX routines.
  * 
- * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ * Copyright (C) 1999, Mark Spencer
  *
  * Mark Spencer <markster@linux-support.net>
  *
@@ -23,7 +23,7 @@
 #include <stdio.h>
 #include <setjmp.h>
 #include <ctype.h>
-
+#include "asterisk.h"
 
 /*
  * I M P O R T A N T :
@@ -82,6 +82,8 @@ struct ast_app {
        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 *);
@@ -89,6 +91,7 @@ static int pbx_builtin_background(struct ast_channel *, void *);
 static int pbx_builtin_dtimeout(struct ast_channel *, void *);
 static int pbx_builtin_rtimeout(struct ast_channel *, void *);
 static int pbx_builtin_wait(struct ast_channel *, void *);
+static int pbx_builtin_setlanguage(struct ast_channel *, void *);
 
 static struct pbx_builtin {
        char name[AST_MAX_APP];
@@ -104,6 +107,9 @@ static struct pbx_builtin {
        { "ResponseTimeout", pbx_builtin_rtimeout },
        { "BackGround", pbx_builtin_background },
        { "Wait", pbx_builtin_wait },
+       { "StripMSD", pbx_builtin_stripmsd },
+       { "Prefix", pbx_builtin_prefix },
+       { "SetLanguage", pbx_builtin_setlanguage },
 };
 
 /* Lock for the application list */
@@ -155,6 +161,7 @@ static int pbx_exec(struct ast_channel *c, /* Channel */
 #define HELPER_EXISTS 0
 #define HELPER_SPAWN 1
 #define HELPER_EXEC 2
+#define HELPER_CANMATCH 3
 
 static struct ast_app *pbx_findapp(char *app) 
 {
@@ -178,7 +185,7 @@ static void pbx_destroy(struct ast_pbx *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 */
@@ -213,15 +220,67 @@ int extension_match(char *pattern, char *data)
        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 (!strlen((char *)data) || !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;
+}
+
+struct ast_context *ast_context_find(char *name)
+{
+       struct ast_context *tmp;
+       pthread_mutex_lock(&conlock);
+       tmp = contexts;
+       while(tmp) {
+               if (!strcasecmp(name, tmp->name))
+                       break;
+               tmp = tmp->next;
+       }
+       pthread_mutex_unlock(&conlock);
+       return tmp;
+}
+
 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_exten *e, *reale;
        struct ast_app *app;
        int newstack = 0;
+       int res;
        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;
@@ -229,27 +288,33 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
        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);
-                       /* But we can relieve the conlock, as tmp will not change */
-                       pthread_mutex_unlock(&conlock);
+#endif
                        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))) {
+                                       reale = e;
                                        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) {
+                                                       case HELPER_CANMATCH:
+                                                               pthread_mutex_unlock(&conlock);
+                                                               return -1;
                                                        case HELPER_EXISTS:
+                                                               pthread_mutex_unlock(&conlock);
                                                                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));
@@ -259,7 +324,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"));
-                                                                       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;
@@ -271,20 +342,25 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                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);
+                                               pthread_mutex_unlock(&conlock);
                                                return -1;
-                                       } else
+                                       } else if (action != HELPER_CANMATCH) {
+                                               pthread_mutex_unlock(&conlock);
                                                return 0;
+                                       } else e = reale; /* Keep going */
                                }
                                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;
-                       } else
+                       } else {
+                               pthread_mutex_unlock(&conlock);
                                return 0;
+                       }
                }
                tmp = tmp->next;
        }
@@ -332,6 +408,11 @@ int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int
        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);
@@ -350,6 +431,7 @@ static void *pbx_thread(void *data)
        char exten[256];
        int pos;
        int waittime;
+       int res=0;
        if (option_debug)
                ast_log(LOG_DEBUG, "PBX_THREAD(%s)\n", c->name);
        else if (option_verbose > 1)
@@ -363,27 +445,38 @@ static void *pbx_thread(void *data)
                c->priority = 1;
        }
        for(;;) {
-               memset(exten, 0, sizeof(exten));
                pos = 0;
                digit = 0;
                while(ast_exists_extension(c, c->context, c->exten, c->priority)) {
-                       if (ast_spawn_extension(c, c->context, c->exten, c->priority)) {
+                       memset(exten, 0, sizeof(exten));
+                       if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority))) {
                                /* Something bad happened, or a hangup has been requested. */
-                               if (option_debug)
-                                       ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
-                               else if (option_verbose > 1)
-                                       ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               switch(res) {
+                               case AST_PBX_KEEPALIVE:
+                                       if (option_debug)
+                                               ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+                                       else if (option_verbose > 1)
+                                               ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               break;
+                               default:
+                                       if (option_debug)
+                                               ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+                                       else if (option_verbose > 1)
+                                               ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+                               }
                                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)
+                               if (digit < 0) {
+                                       if (option_verbose > 2)
+                                               ast_verbose(VERBOSE_PREFIX_3 "Lost connection on %s\n", c->name);
                                        goto out;
+                               }
                                else if (digit) {
-                                       ast_stopstream(c);
                                        exten[pos++] = digit;
                                        break;
                                }
@@ -396,8 +489,8 @@ static void *pbx_thread(void *data)
                        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);
@@ -446,7 +539,8 @@ static void *pbx_thread(void *data)
 out:
        pbx_destroy(c->pbx);
        c->pbx = NULL;
-       ast_hangup(c);
+       if (res != AST_PBX_KEEPALIVE)
+               ast_hangup(c);
        pthread_exit(NULL);
        
 }
@@ -466,6 +560,9 @@ int ast_pbx_start(struct ast_channel *c)
                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");
@@ -748,18 +845,52 @@ void ast_context_destroy(struct ast_context *con)
 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);
 }
 
+int pbx_builtin_setlanguage(struct ast_channel *chan, void *data)
+{
+       /* Copy the language as specified */
+       strncpy(chan->language, (char *)data, sizeof(chan->language));
+       return 0;
+}
+
 int pbx_builtin_hangup(struct ast_channel *chan, void *data)
 {
        /* Just return non-zero and it will hang up */
        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 */
@@ -771,10 +902,14 @@ int pbx_builtin_wait(struct ast_channel *chan, void *data)
 int pbx_builtin_background(struct ast_channel *chan, void *data)
 {
        int res;
+       /* Answer if need be */
+       if (chan->state != AST_STATE_UP)
+               if (ast_answer(chan))
+                       return -1;
        /* Stop anything playing */
        ast_stopstream(chan);
        /* Stream a file */
-       res = ast_streamfile(chan, (char *)data);
+       res = ast_streamfile(chan, (char *)data, chan->language);
        return res;
 }
 
@@ -836,7 +971,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;
 }
-
 int load_pbx(void)
 {
        int x;