Version 0.1.0 from FTP
authorMark Spencer <markster@digium.com>
Sat, 4 Dec 1999 20:45:45 +0000 (20:45 +0000)
committerMark Spencer <markster@digium.com>
Sat, 4 Dec 1999 20:45:45 +0000 (20:45 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@83 65c4cc65-6c06-0410-ace0-fbb531ad65f3

codecs/g723_slin_ex.h [new file with mode: 0755]
pbx.c [new file with mode: 0755]

diff --git a/codecs/g723_slin_ex.h b/codecs/g723_slin_ex.h
new file mode 100755 (executable)
index 0000000..df88b7b
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+  * 8-bit raw data
+  *
+  * Source: g723.example
+  *
+  * Copyright (C) 1999, Mark Spencer and Linux Support Services
+  *
+  * Distributed under the terms of the GNU General Public License
+  *
+  */
+
+static unsigned char g723_slin_ex[] = {
+0x4c, 0x34, 0xc2, 0xd9, 0x81, 0x80, 0xa8, 0x50, 0xd7, 0x8d, 
+0x8, 0x80, 0xf0, 0xb4, 0x40, 0x53, 0xe3, 0xe1, 0x63, 0x4e, 
+0x1a, 0x37, 0xd6, 0x0037 };
diff --git a/pbx.c b/pbx.c
new file mode 100755 (executable)
index 0000000..a95d415
--- /dev/null
+++ b/pbx.c
@@ -0,0 +1,858 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Core PBX routines.
+ * 
+ * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <pthread.h>
+#include <asterisk/pbx.h>
+#include <asterisk/channel.h>
+#include <asterisk/options.h>
+#include <asterisk/logger.h>
+#include <asterisk/file.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <ctype.h>
+
+
+/*
+ * I M P O R T A N T :
+ *
+ *             The speed of extension handling will likely be among the most important
+ * aspects of this PBX.  The switching scheme as it exists right now isn't
+ * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
+ * of priorities, but a constant search time here would be great ;-) 
+ *
+ */
+
+
+struct ast_context;
+
+struct ast_pbx {
+       int dtimeout;                                   /* Timeout between digits (seconds) */
+       int rtimeout;                                   /* Timeout for response (seconds) */
+};
+
+/* An extension */
+struct ast_exten {
+       char exten[AST_MAX_EXTENSION];
+       int priority;
+       /* An extension */
+       struct ast_context *parent;
+       /* Application to execute */
+       char app[AST_MAX_EXTENSION];
+       /* Data to use */
+       void *data;
+       /* Data destructor */
+       void (*datad)(void *);
+       /* Next highest priority with our extension */
+       struct ast_exten *peer;
+       /* Extension with a greater ID */
+       struct ast_exten *next;
+};
+
+/* An extension context */
+struct ast_context {
+       /* Name of the context */
+       char name[AST_MAX_EXTENSION];
+       /* A lock to prevent multiple threads from clobbering the context */
+       pthread_mutex_t lock;
+       /* The root of the list of extensions */
+       struct ast_exten *root;
+       /* Link them together */
+       struct ast_context *next;
+};
+
+
+/* An application */
+struct ast_app {
+       /* Name of the application */
+       char name[AST_MAX_APP];
+       int (*execute)(struct ast_channel *chan, void *data);
+       struct ast_app *next;
+};
+
+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_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 struct pbx_builtin {
+       char name[AST_MAX_APP];
+       int (*execute)(struct ast_channel *chan, void *data);
+} builtins[] = 
+{
+       /* These applications are built into the PBX core and do not
+          need separate modules */
+       { "Answer", pbx_builtin_answer },
+       { "Goto", pbx_builtin_goto },
+       { "Hangup", pbx_builtin_hangup },
+       { "DigitTimeout", pbx_builtin_dtimeout },
+       { "ResponseTimeout", pbx_builtin_rtimeout },
+       { "BackGround", pbx_builtin_background },
+       { "Wait", pbx_builtin_wait },
+};
+
+/* Lock for the application list */
+static pthread_mutex_t applock = PTHREAD_MUTEX_INITIALIZER;
+static struct ast_context *contexts = NULL;
+/* Lock for the ast_context list */
+static pthread_mutex_t conlock = PTHREAD_MUTEX_INITIALIZER;
+static struct ast_app *apps = NULL;
+
+static int pbx_exec(struct ast_channel *c, /* Channel */
+                                       int (*execute)(struct ast_channel *chan, void *data), 
+                                       void *data,                             /* Data for execution */
+                                       int newstack)                   /* Force stack increment */
+{
+       /* This function is special.  It saves the stack so that no matter
+          how many times it is called, it returns to the same place */
+       int res;
+       int stack = c->stack;
+       if (newstack && stack > AST_CHANNEL_MAX_STACK - 2) {
+               /* Don't allow us to go over the max number of stacks we
+                  permit saving. */
+               ast_log(LOG_WARNING, "Stack overflow, cannot create another stack\n");
+               return -1;
+       }
+       if (newstack && (res = setjmp(c->jmp[++c->stack]))) {
+               /* Okay, here's where it gets weird.  If newstack is non-zero, 
+                  then we increase the stack increment, but setjmp is not going
+                  to return until longjmp is called -- when the application
+                  exec'd is finished running. */
+               if (res == 1)
+                       res = 0;
+               if (c->stack != stack + 1) 
+                       ast_log(LOG_WARNING, "Stack returned to an unexpected place!\n");
+               else if (c->app[c->stack])
+                       ast_log(LOG_WARNING, "Application may have forgotten to free its memory\n");
+               c->stack = stack;
+               return res;
+       } else {
+               res = execute(c, data);
+               /* Any application that returns, we longjmp back, just in case. */
+               if (c->stack != stack + 1)
+                       ast_log(LOG_WARNING, "Stack is not at expected value\n");
+               longjmp(c->jmp[stack+1], res);
+               /* Never returns */
+       }
+}
+
+
+#define HELPER_EXISTS 0
+#define HELPER_SPAWN 1
+#define HELPER_EXEC 2
+
+static struct ast_app *pbx_findapp(char *app) 
+{
+       struct ast_app *tmp;
+       if (pthread_mutex_lock(&applock)) {
+               ast_log(LOG_WARNING, "Unable to obtain application lock\n");
+               return NULL;
+       }
+       tmp = apps;
+       while(tmp) {
+               if (!strcasecmp(tmp->name, app))
+                       break;
+               tmp = tmp->next;
+       }
+       pthread_mutex_unlock(&applock);
+       return tmp;
+}
+
+static void pbx_destroy(struct ast_pbx *p)
+{
+       free(p);
+}
+
+int extension_match(char *pattern, char *data)
+{
+       int match;
+       /* If they're the same return */
+       if (!strcasecmp(pattern, data))
+               return 1;
+       /* All patterns begin with _ */
+       if (pattern[0] != '_') 
+               return 0;
+       /* Obviously must be the same length */
+       if (strlen(pattern) != strlen(data) + 1)
+               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;
+       if (pthread_mutex_lock(&conlock)) {
+               ast_log(LOG_WARNING, "Unable to obtain lock\n");
+               if (action == HELPER_EXISTS)
+                       return 0;
+               else
+                       return -1;
+       }
+       tmp = contexts;
+       while(tmp) {
+               if (!strcasecmp(tmp->name, context)) {
+                       /* 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);
+                       e = tmp->root;
+                       while(e) {
+                               if (extension_match(e->exten, exten)) {
+                                       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_EXISTS:
+                                                               return -1;
+                                                       case HELPER_SPAWN:
+                                                               newstack++;
+                                                               /* Fall through */
+                                                       case HELPER_EXEC:
+                                                               app = pbx_findapp(e->app);
+                                                               if (app) {
+                                                                       strncpy(c->context, context, sizeof(c->context));
+                                                                       strncpy(c->exten, exten, sizeof(c->exten));
+                                                                       c->priority = priority;
+                                                                       if (option_debug)
+                                                                               ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
+                                                                       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);
+                                                               } else {
+                                                                       ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+                                                                       return -1;
+                                                               }
+                                                       default:
+                                                               ast_log(LOG_WARNING, "Huh (%d)?\n", action);
+                                                       }
+                                               }
+                                               e = e->peer;
+                                       }
+                                       pthread_mutex_unlock(&tmp->lock);
+                                       if (action != HELPER_EXISTS) {
+                                               ast_log(LOG_WARNING, "No such priority '%d' in '%s' in '%s'\n", priority, exten, context);
+                                               return -1;
+                                       } else
+                                               return 0;
+                               }
+                               e = e->next;
+                       }
+                       pthread_mutex_unlock(&tmp->lock);
+                       if (action != HELPER_EXISTS) {
+                               ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context);
+                               return -1;
+                       } else
+                               return 0;
+               }
+               tmp = tmp->next;
+       }
+       pthread_mutex_unlock(&conlock);
+       if (action != HELPER_EXISTS) {
+               ast_log(LOG_WARNING, "No such context '%s'\n", context);
+               return -1;
+       } else
+               return 0;
+}
+int ast_pbx_longest_extension(char *context) 
+{
+       struct ast_context *tmp;
+       struct ast_exten *e;
+       int len = 0;
+       if (pthread_mutex_lock(&conlock)) {
+               ast_log(LOG_WARNING, "Unable to obtain lock\n");
+               return -1;
+       }
+       tmp = contexts;
+       while(tmp) {
+               if (!strcasecmp(tmp->name, context)) {
+                       /* 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);
+                       e = tmp->root;
+                       while(e) {
+                               if (strlen(e->exten) > len)
+                                       len = strlen(e->exten);
+                               e = e->next;
+                       }
+                       pthread_mutex_unlock(&tmp->lock);
+                       return len;
+               }
+               tmp = tmp->next;
+       }
+       ast_log(LOG_WARNING, "No such context '%s'\n", context);
+       return -1;
+}
+
+int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int priority) 
+{
+       return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS);
+}
+
+int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int priority) 
+{
+       return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN);
+}
+
+static void *pbx_thread(void *data)
+{
+       /* Oh joyeous kernel, we're a new thread, with nothing to do but
+          answer this channel and get it going.  The setjmp stuff is fairly
+          confusing, but necessary to get smooth transitions between
+          the execution of different applications (without the use of
+          additional threads) */
+       struct ast_channel *c = data;
+       int firstpass = 1;
+       char digit;
+       char exten[256];
+       int pos;
+       int waittime;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "PBX_THREAD(%s)\n", c->name);
+       else if (option_verbose > 1)
+               ast_verbose( VERBOSE_PREFIX_2 "Accepting call on '%s'\n", c->name);
+               
+       
+       /* Start by trying whatever the channel is set to */
+       if (!ast_exists_extension(c, c->context, c->exten, c->priority)) {
+               strncpy(c->context, "default", sizeof(c->context));
+               strncpy(c->exten, "s", sizeof(c->exten));
+               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)) {
+                               /* 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);
+                               goto out;
+                       }
+                       /* If we're playing something in the background, wait for it to finish or for a digit */
+                       if (c->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) {
+                                       ast_stopstream(c);
+                                       exten[pos++] = digit;
+                                       break;
+                               }
+                       }
+                       firstpass = 0;
+                       c->priority++;
+               }
+               /* Done, wait for an extension */
+               if (digit)
+                       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))) {
+                       /* 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);
+                       if (!digit)
+                               /* No entry */
+                               break;
+                       if (digit < 0)
+                               /* Error, maybe a  hangup */
+                               goto out;
+                       exten[pos++] = digit;
+                       waittime = c->pbx->dtimeout;
+               }
+               if (ast_exists_extension(c, c->context, exten, 1)) {
+                       /* Prepare the next cycle */
+                       strncpy(c->exten, exten, sizeof(c->exten));
+                       c->priority = 1;
+               } else {
+                       /* No such extension */
+                       if (strlen(exten)) {
+                               /* An invalid extension */
+                               if (ast_exists_extension(c, c->context, "i", 1)) {
+                                       if (option_verbose > 2)
+                                               ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", exten, c->context, c->name);
+                                       strncpy(c->exten, "i", sizeof(c->exten));
+                                       c->priority = 1;
+                               } else {
+                                       ast_log(LOG_WARNING, "Invalid extension, but no rule 'i' in context '%s'\n", c->context);
+                                       goto out;
+                               }
+                       } else {
+                               /* A simple timeout */
+                               if (ast_exists_extension(c, c->context, "t", 1)) {
+                                       if (option_verbose > 2)
+                                               ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
+                                       strncpy(c->exten, "t", sizeof(c->exten));
+                                       c->priority = 1;
+                               } else {
+                                       ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
+                                       goto out;
+                               }
+                       }       
+               }
+       }
+       if (firstpass) 
+               ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
+out:
+       pbx_destroy(c->pbx);
+       c->pbx = NULL;
+       ast_hangup(c);
+       pthread_exit(NULL);
+       
+}
+
+int ast_pbx_start(struct ast_channel *c)
+{
+       pthread_t t;
+       if (!c) {
+               ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
+               return -1;
+       }
+       if (c->pbx)
+               ast_log(LOG_WARNING, "%s already has PBX structure??\n");
+       c->pbx = malloc(sizeof(struct ast_pbx));
+       if (!c->pbx) {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               return -1;
+       }
+       memset(c->pbx, 0, sizeof(struct ast_pbx));
+       /* 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");
+               return -1;
+       }
+       return 0;
+}
+#if 0
+int ast_remove_extension(struct ast_context *con, char *extension, int priority)
+{
+       /* XXX Implement me XXX */
+       return -1;
+}
+#endif
+int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *))
+{
+       struct ast_app *tmp;
+       if (pthread_mutex_lock(&applock)) {
+               ast_log(LOG_ERROR, "Unable to lock application list\n");
+               return -1;
+       }
+       tmp = apps;
+       while(tmp) {
+               if (!strcasecmp(app, tmp->name)) {
+                       ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
+                       pthread_mutex_unlock(&applock);
+                       return -1;
+               }
+               tmp = tmp->next;
+       }
+       tmp = malloc(sizeof(struct ast_app));
+       if (tmp) {
+               strncpy(tmp->name, app, sizeof(tmp->name));
+               tmp->execute = execute;
+               tmp->next = apps;
+               apps = tmp;
+       } else {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               pthread_mutex_unlock(&applock);
+               return -1;
+       }
+       if (option_verbose > 1)
+               ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", tmp->name);
+       pthread_mutex_unlock(&applock);
+       return 0;
+}
+
+int ast_unregister_application(char *app) {
+       struct ast_app *tmp, *tmpl = NULL;
+       if (pthread_mutex_lock(&applock)) {
+               ast_log(LOG_ERROR, "Unable to lock application list\n");
+               return -1;
+       }
+       tmp = apps;
+       while(tmp) {
+               if (!strcasecmp(app, tmp->name)) {
+                       if (tmpl)
+                               tmpl->next = tmp->next;
+                       else
+                               apps = tmp->next;
+                       if (option_verbose > 1)
+                               ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
+                       pthread_mutex_unlock(&applock);
+                       return 0;
+               }
+               tmpl = tmp;
+               tmp = tmp->next;
+       }
+       pthread_mutex_unlock(&applock);
+       return -1;
+}
+
+struct ast_context *ast_context_create(char *name)
+{
+       struct ast_context *tmp;
+       
+       pthread_mutex_lock(&conlock);
+       tmp = contexts;
+       while(tmp) {
+               if (!strcasecmp(tmp->name, name)) {
+                       pthread_mutex_unlock(&conlock);
+                       ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+                       return NULL;
+               }
+               tmp = tmp->next;
+       }
+       tmp = malloc(sizeof(struct ast_context));
+       if (tmp) {
+               pthread_mutex_init(&tmp->lock, NULL);
+               strncpy(tmp->name, name, sizeof(tmp->name));
+               tmp->root = NULL;
+               tmp->next = contexts;
+               contexts = tmp;
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
+               else if (option_verbose > 2)
+                       ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
+       } else
+               ast_log(LOG_WARNING, "Out of memory\n");
+       
+       pthread_mutex_unlock(&conlock);
+       return tmp;
+}
+
+int ast_add_extension2(struct ast_context *con,
+                                         int replace, char *extension, int priority,
+                                         char *application, void *data, void (*datad)(void *))
+{
+
+#define LOG {  if (option_debug) \
+               ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
+       else if (option_verbose > 2) \
+               ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
+               }
+
+       /*
+        * This is a fairly complex routine.  Different extensions are kept
+        * in order by the extension number.  Then, extensions of different
+        * priorities (same extension) are kept in a list, according to the
+        * peer pointer.
+        */
+       struct ast_exten *tmp, *e, *el = NULL, *ep = NULL;
+       int res;
+       /* Be optimistic:  Build the extension structure first */
+       tmp = malloc(sizeof(struct ast_exten));
+       if (tmp) {
+               strncpy(tmp->exten, extension, sizeof(tmp->exten));
+               tmp->priority = priority;
+               strncpy(tmp->app, application, sizeof(tmp->app));
+               tmp->data = data;
+               tmp->datad = datad;
+               tmp->peer = NULL;
+               tmp->next =  NULL;
+       } else {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               return -1;
+       }
+       if (pthread_mutex_lock(&con->lock)) {
+               free(tmp);
+               /* And properly destroy the data */
+               datad(data);
+               ast_log(LOG_WARNING, "Failed to lock context '%s'\n", con->name);
+               return -1;
+       }
+       e = con->root;
+       while(e) {
+               res= strcasecmp(e->exten, extension);
+               if (res == 0) {
+                       /* We have an exact match, now we find where we are
+                          and be sure there's no duplicates */
+                       while(e) {
+                               if (e->priority == tmp->priority) {
+                                       /* Can't have something exactly the same.  Is this a
+                                          replacement?  If so, replace, otherwise, bonk. */
+                                       if (replace) {
+                                               if (ep) {
+                                                       /* We're in the peer list, insert ourselves */
+                                                       ep->peer = tmp;
+                                                       tmp->peer = e->peer;
+                                               } else if (el) {
+                                                       /* We're the first extension. Take over e's functions */
+                                                       el->next = tmp;
+                                                       tmp->next = e->next;
+                                                       tmp->peer = e->peer;
+                                               } else {
+                                                       /* We're the very first extension.  */
+                                                       con->root = tmp;
+                                                       tmp->next = e->next;
+                                                       tmp->peer = e->peer;
+                                               }
+                                               /* Destroy the old one */
+                                               e->datad(e->data);
+                                               free(e);
+                                               pthread_mutex_unlock(&con->lock);
+                                               /* And immediately return success. */
+                                               LOG;
+                                               return 0;
+                                       } else {
+                                               ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+                                               tmp->datad(tmp->data);
+                                               free(tmp);
+                                               pthread_mutex_unlock(&con->lock);
+                                               return -1;
+                                       }
+                               } else if (e->priority > tmp->priority) {
+                                       /* Slip ourselves in just before e */
+                                       if (ep) {
+                                               /* Easy enough, we're just in the peer list */
+                                               ep->peer = tmp;
+                                               tmp->peer = e;
+                                       } else if (el) {
+                                               /* We're the first extension in this peer list */
+                                               el->next = tmp;
+                                               tmp->next = e->next;
+                                               e->next = NULL;
+                                               tmp->peer = e;
+                                       } else {
+                                               /* We're the very first extension altogether */
+                                               tmp->next = con->root;
+                                               /* Con->root must always exist or we couldn't get here */
+                                               tmp->peer = con->root->peer;
+                                               con->root = tmp;
+                                       }
+                                       pthread_mutex_unlock(&con->lock);
+                                       /* And immediately return success. */
+                                       LOG;
+                                       return 0;
+                               }
+                               ep = e;
+                               e = e->peer;
+                       }
+                       /* If we make it here, then it's time for us to go at the very end.
+                          ep *must* be defined or we couldn't have gotten here. */
+                       ep->peer = tmp;
+                       pthread_mutex_unlock(&con->lock);
+                       /* And immediately return success. */
+                       LOG;
+                       return 0;
+                               
+               } else if (res > 0) {
+                       /* Insert ourselves just before 'e'.  We're the first extension of
+                          this kind */
+                       tmp->next = e;
+                       if (el) {
+                               /* We're in the list somewhere */
+                               el->next = tmp;
+                       } else {
+                               /* We're at the top of the list */
+                               con->root = tmp;
+                       }
+                       pthread_mutex_unlock(&con->lock);
+                       /* And immediately return success. */
+                       LOG;
+                       return 0;
+               }                       
+                       
+               el = e;
+               e = e->next;
+       }
+       /* If we fall all the way through to here, then we need to be on the end. */
+       if (el)
+               el->next = tmp;
+       else
+               con->root = tmp;
+       pthread_mutex_unlock(&con->lock);
+       LOG;
+       return 0;       
+}
+
+void ast_context_destroy(struct ast_context *con)
+{
+       struct ast_context *tmp, *tmpl=NULL;
+       pthread_mutex_lock(&conlock);
+       tmp = contexts;
+       while(tmp) {
+               if (tmp == con) {
+                       /* Okay, let's lock the structure to be sure nobody else
+                          is searching through it. */
+                       if (pthread_mutex_lock(&tmp->lock)) {
+                               ast_log(LOG_WARNING, "Unable to lock context lock\n");
+                               return;
+                       }
+                       if (tmpl)
+                               tmpl->next = tmp->next;
+                       else
+                               contexts = tmp->next;
+                       /* Okay, now we're safe to let it go -- in a sense, we were
+                          ready to let it go as soon as we locked it. */
+                       pthread_mutex_unlock(&tmp->lock);
+                       free(tmp);
+                       pthread_mutex_unlock(&conlock);
+                       return;
+               }
+               tmpl = tmp;
+               tmp = tmp->next;
+       }
+       pthread_mutex_unlock(&conlock);
+}
+
+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");
+               return 0;
+       } else
+               return ast_answer(chan);
+}
+
+int pbx_builtin_hangup(struct ast_channel *chan, void *data)
+{
+       /* Just return non-zero and it will hang up */
+       return -1;
+}
+
+int pbx_builtin_wait(struct ast_channel *chan, void *data)
+{
+       /* Wait for "n" seconds */
+       if (data && atoi((char *)data))
+               sleep(atoi((char *)data));
+       return 0;
+}
+
+int pbx_builtin_background(struct ast_channel *chan, void *data)
+{
+       int res;
+       /* Stop anything playing */
+       ast_stopstream(chan);
+       /* Stream a file */
+       res = ast_streamfile(chan, (char *)data);
+       return res;
+}
+
+int pbx_builtin_rtimeout(struct ast_channel *chan, void *data)
+{
+       /* Set the timeout for how long to wait between digits */
+       chan->pbx->rtimeout = atoi((char *)data);
+       if (option_verbose > 2)
+               ast_verbose( VERBOSE_PREFIX_3 "Set Response Timeout to %d\n", chan->pbx->rtimeout);
+       return 0;
+}
+
+int pbx_builtin_dtimeout(struct ast_channel *chan, void *data)
+{
+       /* Set the timeout for how long to wait between digits */
+       chan->pbx->dtimeout = atoi((char *)data);
+       if (option_verbose > 2)
+               ast_verbose( VERBOSE_PREFIX_3 "Set Digit Timeout to %d\n", chan->pbx->dtimeout);
+       return 0;
+}
+
+int pbx_builtin_goto(struct ast_channel *chan, void *data)
+{
+       char *s;
+       char *exten, *pri, *context;
+       if (!data) {
+               ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
+               return -1;
+       }
+       s = strdup((void *) data);
+       context = strtok(s, "|");
+       exten = strtok(NULL, "|");
+       if (!exten) {
+               /* Only a priority in this one */
+               pri = context;
+               exten = NULL;
+               context = NULL;
+       } else {
+               pri = strtok(NULL, "|");
+               if (!pri) {
+                       /* Only an extension and priority in this one */
+                       pri = exten;
+                       exten = context;
+                       context = NULL;
+               }
+       }
+       if (atoi(pri) < 0) {
+               ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", pri);
+               free(s);
+               return -1;
+       }
+       /* At this point we have a priority and maybe an extension and a context */
+       chan->priority = atoi(pri) - 1;
+       if (exten)
+               strncpy(chan->exten, exten, sizeof(chan->exten));
+       if (context)
+               strncpy(chan->context, context, sizeof(chan->context));
+       if (option_verbose > 2)
+               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;
+       /* Initialize the PBX */
+       if (option_verbose) {
+               ast_verbose( "Asterisk PBX Core Initializing\n");
+               ast_verbose( "Registering builtin applications:\n");
+       }
+       for (x=0;x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
+               if (option_verbose)
+                       ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
+               if (ast_register_application(builtins[x].name, builtins[x].execute)) {
+                       ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+                       return -1;
+               }
+       }
+       return 0;
+}
+