Version 0.1.8 from FTP
[asterisk/asterisk.git] / pbx.c
diff --git a/pbx.c b/pbx.c
index 99d09ea..bbc5f22 100755 (executable)
--- a/pbx.c
+++ b/pbx.c
@@ -12,6 +12,7 @@
  */
 
 #include <pthread.h>
+#include <asterisk/cli.h>
 #include <asterisk/pbx.h>
 #include <asterisk/channel.h>
 #include <asterisk/options.h>
@@ -55,12 +56,20 @@ struct ast_exten {
        void *data;
        /* Data destructor */
        void (*datad)(void *);
-       /* Next highest priority with our extension */
+       /* Next higher priority with our extension */
        struct ast_exten *peer;
+       /* Registrar */
+       char *registrar;
        /* Extension with a greater ID */
        struct ast_exten *next;
 };
 
+struct ast_include {
+       char name[AST_MAX_EXTENSION];
+       char *registrar;
+       struct ast_include *next;
+};
+
 /* An extension context */
 struct ast_context {
        /* Name of the context */
@@ -71,6 +80,10 @@ struct ast_context {
        struct ast_exten *root;
        /* Link them together */
        struct ast_context *next;
+       /* Include other contexts */
+       struct ast_include *includes;
+       /* Registrar */
+       char *registrar;
 };
 
 
@@ -79,6 +92,8 @@ struct ast_app {
        /* Name of the application */
        char name[AST_MAX_APP];
        int (*execute)(struct ast_channel *chan, void *data);
+       char *synopsis;
+       char *description;
        struct ast_app *next;
 };
 
@@ -92,24 +107,82 @@ 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 int pbx_builtin_ringing(struct ast_channel *, void *);
+static int pbx_builtin_congestion(struct ast_channel *, void *);
+static int pbx_builtin_busy(struct ast_channel *, void *);
 
 static struct pbx_builtin {
        char name[AST_MAX_APP];
        int (*execute)(struct ast_channel *chan, void *data);
+       char *synopsis;
+       char *description;
 } 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 },
-       { "StripMSD", pbx_builtin_stripmsd },
-       { "Prefix", pbx_builtin_prefix },
-       { "SetLanguage", pbx_builtin_setlanguage },
+       { "Answer", pbx_builtin_answer, 
+                       "Answer a channel if ringing", 
+                       "  Answer(): If the channel is ringing, answer it, otherwise do nothing.  Returns 0 unless it\n"
+                       "  tries to answer the channel and fails.\n"   },
+       { "Goto", pbx_builtin_goto, 
+                       "Goto a particular priority, extension, or context",
+                       "  Goto([[context|]extension|]priority): Set the priority to the specified value, optionally setting\n"
+                       "  the extension and optionally the context as well.  The extension BYEXTENSION is special in that it\n"
+                       "  uses the current extension, thus permitting you to go to a different context, without specifying a\n"
+                       "  specific extension.  Always returns 0, even if the given context, extension, or priority is invalid.\n" },
+       { "Hangup", pbx_builtin_hangup,
+                       "Unconditional hangup",
+                       "  Hangup(): Unconditionally hangs up a given channel by returning -1 always.\n" },
+       { "DigitTimeout", pbx_builtin_dtimeout,
+                       "Set maximum timeout between digits",
+                       "  DigitTimeout(seconds): Set the maximum amount of time permitted between digits when the user is\n" 
+                       "  typing in an extension.  When this timeout expires, after the user has started to type in an\n"
+                       "  extension, the extension will be considered complete, and will be interpreted.  Note that if an\n"
+                       "  extension typed in is valid, it will not have to timeout to be tested, so typically at the expiry\n"
+                       "  of this timeout, the extension will be considered invalid (and thus control would be passed to the\n"
+                       "  'i' extension, or if it doesn't exist the call would be terminated).  Always returns 0.\n" },
+       { "ResponseTimeout", pbx_builtin_rtimeout,
+                       "Set maximum timeout awaiting response",
+                       "  ResponseTimeout(seconds): Set the maximum amount of time permitted after falling through a series\n"
+                       "  of priorities for a channel in which the user may begin typing an extension.  If the user does not\n"
+                       "  type an extension in this amount of time, control will pass to the 't' extension if it exists, and\n"
+                       "  if not the call would be terminated.  Always returns 0.\n"  },
+       { "BackGround", pbx_builtin_background,
+                       "Play a file while awaiting extension",
+                       "  Background(filename): Plays a given file, while simultaneously waiting for the user to begin typing\n"
+                       "  an extension.  The timeouts do not count until the last BackGround application as ended.  Always\n"
+                       "  returns 0.\n" },
+       { "Wait", pbx_builtin_wait, 
+                       "Waits for some time", 
+                       "  Wait(seconds): Waits for a specified number of seconds, then returns 0.\n" },
+       { "StripMSD", pbx_builtin_stripmsd, "Strip leading digits",
+                       "  StripMSD(count): Strips the leading 'count' digits from the channel's associated extension.  For\n"
+                       "  example, the number 5551212 when stripped with a count of 3 would be changed to 1212.  This app\n"
+                       "  always returns 0, and the PBX will continue processing at the next priority for the *new* extension.\n"
+                       "  So, for example, if priority 3 of 5551212 is StripMSD 3, the next step executed will be priority 4 of\n"
+                       "  1212.  If you switch into an extension which has no first step, the PBX will treat it as though\n"
+                       "  the user dialed an invalid extension.\n" },
+       { "Prefix", pbx_builtin_prefix, "Prepend leading digits",
+                       "  Prefix(digits): Prepends the digit string specified by digits to the channel's associated\n"
+                       "  extension.  For example, the number 1212 when prefixed with '555' will become 5551212.  This app\n"
+                       "  always returns 0, and the PBX will continue processing at the next priority for the *new* extension.\n"
+                       "  So, for example, if priority 3 of 1212 is Prefix 555, the next step executed will be priority 4 of\n"
+                       "  5551212.  If you switch into an extension which has no first step, the PBX will treat it as though\n"
+                       "  the user dialed an invalid extension.\n" },
+       { "SetLanguage", pbx_builtin_setlanguage, "Sets user language",
+                       "  SetLanguage(language): Set the channel language to 'language'.  This information is used for the\n"
+                       "  generation of numbers, and to select a natural language file when available.  For example, if\n"
+                       "  language is set to 'fr' and the file 'demo-congrats' is requested to be played, if the file \n"
+                       "  'demo-congrats-fr' exists, then it will play that file, and if not will play the normal \n"
+                       "  'demo-congrats'.  Always returns 0.\n"  },
+       { "Ringing", pbx_builtin_ringing, "Indicate ringing tone",
+                       "  Ringing(): Request that the channel indicate ringing tone to the user.  Always returns 0.\n" },
+       { "Congestion", pbx_builtin_congestion, "Indicate congestion and stop",
+                       "  Congestion(): Requests that the channel indicate congestion and then waits for the user to\n"
+                       "  hang up.  Always returns -1." },
+       { "Busy", pbx_builtin_busy, "Indicate busy condition and stop",
+                       "  Busy(): Requests that the channel indicate busy condition and then waits for the user to\n"
+                       "  hang up.  Always returns -1." },
 };
 
 /* Lock for the application list */
@@ -158,6 +231,9 @@ static int pbx_exec(struct ast_channel *c, /* Channel */
 }
 
 
+/* Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK      64
+
 #define HELPER_EXISTS 0
 #define HELPER_SPAWN 1
 #define HELPER_EXEC 2
@@ -166,7 +242,7 @@ static int pbx_exec(struct ast_channel *c, /* Channel */
 static struct ast_app *pbx_findapp(char *app) 
 {
        struct ast_app *tmp;
-       if (pthread_mutex_lock(&applock)) {
+       if (ast_pthread_mutex_lock(&applock)) {
                ast_log(LOG_WARNING, "Unable to obtain application lock\n");
                return NULL;
        }
@@ -176,7 +252,7 @@ static struct ast_app *pbx_findapp(char *app)
                        break;
                tmp = tmp->next;
        }
-       pthread_mutex_unlock(&applock);
+       ast_pthread_mutex_unlock(&applock);
        return tmp;
 }
 
@@ -185,7 +261,7 @@ static void pbx_destroy(struct ast_pbx *p)
        free(p);
 }
 
-static int extension_match(char *pattern, char *data)
+int ast_extension_match(char *pattern, char *data)
 {
        int match;
        /* If they're the same return */
@@ -260,38 +336,172 @@ static int extension_close(char *pattern, char *data)
 struct ast_context *ast_context_find(char *name)
 {
        struct ast_context *tmp;
-       pthread_mutex_lock(&conlock);
+       ast_pthread_mutex_lock(&conlock);
+       if (name) {
+               tmp = contexts;
+               while(tmp) {
+                       if (!strcasecmp(name, tmp->name))
+                               break;
+                       tmp = tmp->next;
+               }
+       } else
+               tmp = contexts;
+       ast_pthread_mutex_unlock(&conlock);
+       return tmp;
+}
+
+#define STATUS_NO_CONTEXT   1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY  3
+#define STATUS_SUCCESS     4
+
+static struct ast_exten *pbx_find_extension(char *context, char *exten, int priority, int action, char *incstack[], int *stacklen, int *status)
+{
+       int x;
+       struct ast_context *tmp;
+       struct ast_exten *e, *eroot;
+       struct ast_include *i;
+       /* Initialize status if appropriate */
+       if (!*stacklen)
+               *status = STATUS_NO_CONTEXT;
+       /* Check for stack overflow */
+       if (*stacklen >= AST_PBX_MAX_STACK) {
+               ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+               return NULL;
+       }
+       /* Check first to see if we've already been checked */
+       for (x=0;x<*stacklen;x++) {
+               if (!strcasecmp(incstack[x], context))
+                       return NULL;
+       }
        tmp = contexts;
        while(tmp) {
-               if (!strcasecmp(name, tmp->name))
-                       break;
+               /* Match context */
+               if (!strcasecmp(tmp->name, context)) {
+                       if (*status < STATUS_NO_EXTENSION)
+                               *status = STATUS_NO_EXTENSION;
+                       eroot = tmp->root;
+                       while(eroot) {
+                               /* Match extension */
+                               if (ast_extension_match(eroot->exten, exten) ||
+                                               ((action == HELPER_CANMATCH) && (extension_close(eroot->exten, exten)))) {
+                                               e = eroot;
+                                               if (*status < STATUS_NO_PRIORITY)
+                                                       *status = STATUS_NO_PRIORITY;
+                                               while(e) {
+                                                       /* Match priority */
+                                                       if (e->priority == priority) {
+                                                               *status = STATUS_SUCCESS;
+                                                               return e;
+                                                       }
+                                                       e = e->peer;
+                                               }
+                               }
+                               eroot = eroot->next;
+                       }
+                       /* Setup the stack */
+                       incstack[*stacklen] = tmp->name;
+                       (*stacklen)++;
+                       /* Now try any includes we have in this context */
+                       i = tmp->includes;
+                       while(i) {
+                               if ((e = pbx_find_extension(i->name, exten, priority, action, incstack, stacklen, status))) 
+                                       return e;
+                               i = i->next;
+                       }
+               }
                tmp = tmp->next;
        }
-       pthread_mutex_unlock(&conlock);
-       return tmp;
+       return NULL;
 }
 
 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, *reale;
+       struct ast_exten *e;
        struct ast_app *app;
        int newstack = 0;
        int res;
-       if (pthread_mutex_lock(&conlock)) {
+       int status = 0;
+       char *incstack[AST_PBX_MAX_STACK];
+       int stacklen = 0;
+       if (ast_pthread_mutex_lock(&conlock)) {
                ast_log(LOG_WARNING, "Unable to obtain lock\n");
                if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH))
                        return 0;
                else
                        return -1;
        }
+       e = pbx_find_extension(context, exten, priority, action, incstack, &stacklen, &status);
+       if (e) {
+               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));
+                               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"));
+                               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;
+                       }
+               default:
+                       ast_log(LOG_WARNING, "Huh (%d)?\n", action);
+                       return -1;
+               }
+       } else {
+               pthread_mutex_unlock(&conlock);
+               switch(status) {
+               case STATUS_NO_CONTEXT:
+                       if (action != HELPER_EXISTS)
+                               ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+                       break;
+               case STATUS_NO_EXTENSION:
+                       if ((action != HELPER_EXISTS) && (action !=  HELPER_CANMATCH))
+                               ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+                       break;
+               case STATUS_NO_PRIORITY:
+                       if ((action != HELPER_EXISTS) && (action !=  HELPER_CANMATCH))
+                               ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+                       break;
+               default:
+                       ast_log(LOG_DEBUG, "Shouldn't happen!\n");
+               }
+               if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH))
+                       return -1;
+               else
+                       return 0;
+       }
+
+#if 0          
        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);
+                       ast_pthread_mutex_lock(&tmp->lock);
 #endif
                        e = tmp->root;
                        while(e) {
@@ -304,17 +514,17 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                           in here though. XXX */
                                                        switch(action) {
                                                        case HELPER_CANMATCH:
-                                                               pthread_mutex_unlock(&conlock);
+                                                               ast_pthread_mutex_unlock(&conlock);
                                                                return -1;
                                                        case HELPER_EXISTS:
-                                                               pthread_mutex_unlock(&conlock);
+                                                               ast_pthread_mutex_unlock(&conlock);
                                                                return -1;
                                                        case HELPER_SPAWN:
                                                                newstack++;
                                                                /* Fall through */
                                                        case HELPER_EXEC:
                                                                app = pbx_findapp(e->app);
-                                                               pthread_mutex_unlock(&conlock);
+                                                               ast_pthread_mutex_unlock(&conlock);
                                                                if (app) {
                                                                        strncpy(c->context, context, sizeof(c->context));
                                                                        strncpy(c->exten, exten, sizeof(c->exten));
@@ -329,7 +539,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                                        res = pbx_exec(c, app->execute, e->data, newstack);
                                                                        c->appl = NULL;
                                                                        c->data = NULL;
-                                                                       pthread_mutex_unlock(&conlock);
+                                                                       ast_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);
@@ -341,42 +551,45 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
                                                }
                                                e = e->peer;
                                        }
-                                       pthread_mutex_unlock(&tmp->lock);
+                                       ast_pthread_mutex_unlock(&tmp->lock);
                                        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);
+                                               ast_pthread_mutex_unlock(&conlock);
                                                return -1;
                                        } else if (action != HELPER_CANMATCH) {
-                                               pthread_mutex_unlock(&conlock);
+                                               ast_pthread_mutex_unlock(&conlock);
                                                return 0;
                                        } else e = reale; /* Keep going */
                                }
                                e = e->next;
                        }
                        if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH)) {
-                               pthread_mutex_unlock(&conlock);
+                               ast_pthread_mutex_unlock(&conlock);
                                ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context);
                                return -1;
                        } else {
-                               pthread_mutex_unlock(&conlock);
+                               ast_pthread_mutex_unlock(&conlock);
                                return 0;
                        }
                }
                tmp = tmp->next;
        }
-       pthread_mutex_unlock(&conlock);
+       ast_pthread_mutex_unlock(&conlock);
        if (action != HELPER_EXISTS) {
                ast_log(LOG_WARNING, "No such context '%s'\n", context);
                return -1;
        } else
                return 0;
+#endif
 }
+
 int ast_pbx_longest_extension(char *context) 
 {
+       /* XXX Not include-aware XXX */
        struct ast_context *tmp;
        struct ast_exten *e;
        int len = 0;
-       if (pthread_mutex_lock(&conlock)) {
+       if (ast_pthread_mutex_lock(&conlock)) {
                ast_log(LOG_WARNING, "Unable to obtain lock\n");
                return -1;
        }
@@ -385,16 +598,16 @@ int ast_pbx_longest_extension(char *context)
                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);
+                       ast_pthread_mutex_lock(&tmp->lock);
                        /* But we can relieve the conlock, as tmp will not change */
-                       pthread_mutex_unlock(&conlock);
+                       ast_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);
+                       ast_pthread_mutex_unlock(&tmp->lock);
                        return len;
                }
                tmp = tmp->next;
@@ -418,24 +631,36 @@ int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int p
        return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN);
 }
 
-static void *pbx_thread(void *data)
+int ast_pbx_run(struct ast_channel *c)
 {
-       /* 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;
        int res=0;
+
+       /* A little initial setup here */
+       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));
+       /* Set reasonable defaults */
+       c->pbx->rtimeout = 10;
+       c->pbx->dtimeout = 5;
+
        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);
+       else if (option_verbose > 1) {
+               if (c->callerid)
+                       ast_verbose( VERBOSE_PREFIX_2 "Accepting call on '%s' (%s)\n", c->name, c->callerid);
+               else
+                       ast_verbose( VERBOSE_PREFIX_2 "Accepting call on '%s'\n", c->name);
+       }
                
        
        /* Start by trying whatever the channel is set to */
@@ -466,13 +691,21 @@ static void *pbx_thread(void *data)
                                }
                                goto out;
                        }
+                       if (c->softhangup) {
+                               ast_log(LOG_WARNING, "Extension %s, priority %d returned normally even though call was hung up\n",
+                                       c->exten, c->priority);
+                               goto out;
+                       }
                        /* If we're playing something in the background, wait for it to finish or for a digit */
-                       if (c->stream || (c->trans && c->trans->stream)) {
+                       if (c->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) {
                                        exten[pos++] = digit;
                                        break;
@@ -481,54 +714,68 @@ static void *pbx_thread(void *data)
                        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) && 
-                     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);
-                       if (!digit)
-                               /* No entry */
-                               break;
-                       if (digit < 0)
-                               /* Error, maybe a  hangup */
+               if (!ast_exists_extension(c, c->context, c->exten, 1)) {
+                       /* It's not a valid extension anymore */
+                       if (ast_exists_extension(c, c->context, "i", 1)) {
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
+                               strncpy(c->exten, "i", sizeof(c->exten));
+                               c->priority = 1;
+                       } else {
+                               ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
+                                       c->name, c->exten, c->context);
                                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);
+                       /* 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) && 
+                              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);
+                               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 {
-                               /* 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;
+                               /* 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 {
-                                       ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
-                                       goto out;
-                               }
-                       }       
+                                       /* 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) 
@@ -538,30 +785,34 @@ out:
        c->pbx = NULL;
        if (res != AST_PBX_KEEPALIVE)
                ast_hangup(c);
+       return 0;
+}
+
+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;
+       ast_pbx_run(c);
        pthread_exit(NULL);
-       
+       return NULL;
 }
 
 int ast_pbx_start(struct ast_channel *c)
 {
        pthread_t t;
+       pthread_attr_t attr;
        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));
-       /* 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)) {
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       if (pthread_create(&t, &attr, pbx_thread, c)) {
                ast_log(LOG_WARNING, "Failed to create new channel thread\n");
                return -1;
        }
@@ -574,10 +825,10 @@ int ast_remove_extension(struct ast_context *con, char *extension, int priority)
        return -1;
 }
 #endif
-int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *))
+int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *), char *synopsis, char *description)
 {
        struct ast_app *tmp;
-       if (pthread_mutex_lock(&applock)) {
+       if (ast_pthread_mutex_lock(&applock)) {
                ast_log(LOG_ERROR, "Unable to lock application list\n");
                return -1;
        }
@@ -585,7 +836,7 @@ int ast_register_application(char *app, int (*execute)(struct ast_channel *, voi
        while(tmp) {
                if (!strcasecmp(app, tmp->name)) {
                        ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
-                       pthread_mutex_unlock(&applock);
+                       ast_pthread_mutex_unlock(&applock);
                        return -1;
                }
                tmp = tmp->next;
@@ -594,22 +845,218 @@ int ast_register_application(char *app, int (*execute)(struct ast_channel *, voi
        if (tmp) {
                strncpy(tmp->name, app, sizeof(tmp->name));
                tmp->execute = execute;
+               tmp->synopsis = synopsis;
+               tmp->description = description;
                tmp->next = apps;
                apps = tmp;
        } else {
                ast_log(LOG_WARNING, "Out of memory\n");
-               pthread_mutex_unlock(&applock);
+               ast_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);
+       ast_pthread_mutex_unlock(&applock);
        return 0;
 }
 
+static char app_help[] = 
+"Usage: show application <application>\n"
+"       Describes a particular application.\n";
+
+static char apps_help[] =
+"Usage: show applications\n"
+"       List applications which are currently available.\n";
+
+static char dialplan_help[] =
+"Usage: show dialplan [[exten@]context]\n"
+"       Displays dialplan.  Optionally takes a context (possibly preceeded by\n"
+"       an extension) to limit the scope of the plan that is displayed.\n";
+
+static int handle_show_applications(int fd, int argc, char *argv[])
+{
+       struct ast_app *tmp;
+       char buf[256];
+       if (ast_pthread_mutex_lock(&applock)) {
+               ast_log(LOG_ERROR, "Unable to lock application list\n");
+               return -1;
+       }
+       tmp = apps;
+       ast_cli(fd, "\n    -= Registered Asterisk Applications =-\n");
+       while(tmp) {
+               snprintf(buf, sizeof(buf), "  %15s: %s\n", tmp->name, tmp->synopsis ? tmp->synopsis : "<Synopsis not available>");
+               ast_cli(fd, buf);
+               tmp = tmp->next;
+       }
+       ast_pthread_mutex_unlock(&applock);
+       return RESULT_SUCCESS;
+}
+
+static char *complete_app(char *line, char *word, int pos, int state)
+{
+       struct ast_app *tmp;
+       char *ret;
+       int which = 0;
+       if (ast_pthread_mutex_lock(&applock)) {
+               ast_log(LOG_ERROR, "Unable to lock application list\n");
+               return NULL;
+       }
+       tmp = apps;
+       while(tmp) {
+               if (!strncasecmp(word, tmp->name, strlen(word))) {
+                       if (++which > state)
+                               break;
+               }
+               tmp = tmp->next;
+       }
+       if (tmp)
+               ret = tmp->name;
+       else
+               ret = NULL;
+       ast_pthread_mutex_unlock(&applock);
+       
+       return ret ? strdup(ret) : ret;
+}
+
+static char *complete_context(char *line, char *word, int pos, int state)
+{
+       struct ast_context *tmp;
+       char *ret;
+       int which = 0;
+       if (ast_pthread_mutex_lock(&conlock)) {
+               ast_log(LOG_ERROR, "Unable to lock context list\n");
+               return NULL;
+       }
+       tmp = contexts;
+       while(tmp) {
+               if (!strncasecmp(word, tmp->name, strlen(word))) {
+                       if (++which > state)
+                               break;
+               }
+               tmp = tmp->next;
+       }
+       if (tmp)
+               ret = tmp->name;
+       else
+               ret = NULL;
+       ast_pthread_mutex_unlock(&conlock);
+       
+       return ret ? strdup(ret) : ret;
+}
+
+static int handle_show_application(int fd, int argc, char *argv[])
+{
+       struct ast_app *tmp;
+       char buf[2048];
+       if (argc != 3) 
+               return RESULT_SHOWUSAGE;
+       if (ast_pthread_mutex_lock(&applock)) {
+               ast_log(LOG_ERROR, "Unable to lock application list\n");
+               return -1;
+       }
+       tmp = apps;
+       while(tmp) {
+               if (!strcasecmp(tmp->name, argv[2])) {
+                       snprintf(buf, sizeof(buf), "\n  -= About Application '%s' =- \n\n"
+                                                                          "[Synopsis]:\n  %s\n\n"
+                                                                          "[Description:]\n%s\n\n", tmp->name, tmp->synopsis ? 
+                                                                                       tmp->synopsis : "Not available", tmp->description ?
+                                                                                               tmp->description : "Not available\n");
+                       break;
+               }
+               tmp = tmp->next;
+       }
+       ast_pthread_mutex_unlock(&applock);
+       if (!tmp) 
+               snprintf(buf, sizeof(buf), "No such application '%s' is registered.\n", argv[2]);
+       ast_cli(fd, buf);
+       return RESULT_SUCCESS;
+}
+
+static int handle_dialplan(int fd, int argc, char *argv[])
+{
+       struct ast_context *con;
+       struct ast_exten *eroot, *e;
+       struct ast_include *inc;
+       char tmp[512];
+       char cpy[512];
+       char tmp2[512];
+       char *context;
+       char *exten;
+       int spaces;
+       
+       if ((argc < 2) || (argc > 3))
+               return RESULT_SHOWUSAGE;
+
+       if (argc > 2) {
+               strncpy(cpy, argv[2], sizeof(cpy));
+               if ((context = strchr(cpy, '@'))) {
+                       *context = '\0';
+                       context++;
+                       exten = cpy;
+               } else {
+                       context = cpy;
+                       exten = NULL;
+               }
+       } else {
+               context = NULL;
+               exten = NULL;
+       }
+       ast_pthread_mutex_lock(&conlock);
+       con = contexts;
+       while(con) {
+               if (!context || (!strcasecmp(context, con->name))) {
+                       ast_cli(fd, "\n [ Context '%s' created by '%s']\n", con->name, con->registrar);
+                       eroot = con->root;
+                       while(eroot) {
+                               if (!exten || (!strcasecmp(exten, eroot->exten))) {
+                                       memset(tmp, ' ', sizeof(tmp));
+                                       snprintf(tmp, sizeof(tmp), "  '%s' => ", eroot->exten);
+                                       spaces = strlen(tmp);
+                                       tmp[spaces] = ' ';
+                                       if (spaces < 19)
+                                               spaces = 19;
+                                       snprintf(tmp2, sizeof(tmp2), "%d. %s(%s)", eroot->priority, eroot->app, (char *)eroot->data);
+                                       snprintf(tmp + spaces, sizeof(tmp) - spaces,     "%-45s [%s]\n", 
+                                                       tmp2, eroot->registrar);
+                                       ast_cli(fd, tmp);
+                                       memset(tmp, ' ', spaces);
+                                       e = eroot->peer;
+                                       while(e) {
+                                               snprintf(tmp2, sizeof(tmp2), "%d. %s(%s)", e->priority, e->app, (char *)e->data);
+                                               snprintf(tmp + spaces, sizeof(tmp) - spaces,     "%-45s [%s]\n", 
+                                                       tmp2, e->registrar);
+                                               ast_cli(fd, tmp);
+                                               e = e->peer;
+                                       }
+                               }
+                               eroot = eroot->next;
+                       }
+                       inc = con->includes;
+                       while(inc) {
+                               snprintf(tmp, sizeof(tmp), "   Include =>    '%s'", inc->name);
+                               ast_cli(fd, "%s [%s]\n", tmp, inc->registrar);
+                               inc = inc->next;
+                       }
+               }
+               con = con->next;
+       }
+       ast_pthread_mutex_unlock(&conlock);
+       return RESULT_SUCCESS;
+}
+
+static struct ast_cli_entry showapps = { { "show", "applications", NULL }, 
+       handle_show_applications, "Shows registered applications", apps_help };
+
+static struct ast_cli_entry showdialplan = { { "show", "dialplan", NULL }, 
+       handle_dialplan, "Displays all or part of dialplan", dialplan_help, complete_context };
+
+static struct ast_cli_entry showapp = { { "show", "application", NULL }, 
+       handle_show_application, "Describe a specific application", app_help, complete_app };
+       
 int ast_unregister_application(char *app) {
        struct ast_app *tmp, *tmpl = NULL;
-       if (pthread_mutex_lock(&applock)) {
+       if (ast_pthread_mutex_lock(&applock)) {
                ast_log(LOG_ERROR, "Unable to lock application list\n");
                return -1;
        }
@@ -622,25 +1069,25 @@ int ast_unregister_application(char *app) {
                                apps = tmp->next;
                        if (option_verbose > 1)
                                ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
-                       pthread_mutex_unlock(&applock);
+                       ast_pthread_mutex_unlock(&applock);
                        return 0;
                }
                tmpl = tmp;
                tmp = tmp->next;
        }
-       pthread_mutex_unlock(&applock);
+       ast_pthread_mutex_unlock(&applock);
        return -1;
 }
 
-struct ast_context *ast_context_create(char *name)
+struct ast_context *ast_context_create(char *name, char *registrar)
 {
        struct ast_context *tmp;
        
-       pthread_mutex_lock(&conlock);
+       ast_pthread_mutex_lock(&conlock);
        tmp = contexts;
        while(tmp) {
                if (!strcasecmp(tmp->name, name)) {
-                       pthread_mutex_unlock(&conlock);
+                       ast_pthread_mutex_unlock(&conlock);
                        ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
                        return NULL;
                }
@@ -651,7 +1098,9 @@ struct ast_context *ast_context_create(char *name)
                pthread_mutex_init(&tmp->lock, NULL);
                strncpy(tmp->name, name, sizeof(tmp->name));
                tmp->root = NULL;
+               tmp->registrar = registrar;
                tmp->next = contexts;
+               tmp->includes = NULL;
                contexts = tmp;
                if (option_debug)
                        ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
@@ -660,13 +1109,45 @@ struct ast_context *ast_context_create(char *name)
        } else
                ast_log(LOG_WARNING, "Out of memory\n");
        
-       pthread_mutex_unlock(&conlock);
+       ast_pthread_mutex_unlock(&conlock);
        return tmp;
 }
 
+int ast_context_add_include2(struct ast_context *con, char *value, char *registrar)
+{
+       struct ast_include *inc, *incc, *incl = NULL;
+       inc = malloc(sizeof(struct ast_include));
+       if (!inc) {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               return -1;
+       }
+       strncpy(inc->name, value, sizeof(inc->name));
+       inc->next = NULL;
+       inc->registrar = registrar;
+       pthread_mutex_lock(&con->lock);
+       incc = con->includes;
+       while(incc) {
+               incl = incc;
+               if (!strcasecmp(incc->name, value)) {
+                       /* Already there */
+                       pthread_mutex_unlock(&con->lock);
+                       return 0;
+               }
+               incc = incc->next;
+       }
+       if (incl) 
+               incl->next = inc;
+       else
+               con->includes = inc;
+       pthread_mutex_unlock(&con->lock);
+       return 0;
+       
+}
+
 int ast_add_extension2(struct ast_context *con,
                                          int replace, char *extension, int priority,
-                                         char *application, void *data, void (*datad)(void *))
+                                         char *application, void *data, void (*datad)(void *),
+                                         char *registrar)
 {
 
 #define LOG {  if (option_debug) \
@@ -691,13 +1172,14 @@ int ast_add_extension2(struct ast_context *con,
                strncpy(tmp->app, application, sizeof(tmp->app));
                tmp->data = data;
                tmp->datad = datad;
+               tmp->registrar = registrar;
                tmp->peer = NULL;
                tmp->next =  NULL;
        } else {
                ast_log(LOG_WARNING, "Out of memory\n");
                return -1;
        }
-       if (pthread_mutex_lock(&con->lock)) {
+       if (ast_pthread_mutex_lock(&con->lock)) {
                free(tmp);
                /* And properly destroy the data */
                datad(data);
@@ -733,7 +1215,7 @@ int ast_add_extension2(struct ast_context *con,
                                                /* Destroy the old one */
                                                e->datad(e->data);
                                                free(e);
-                                               pthread_mutex_unlock(&con->lock);
+                                               ast_pthread_mutex_unlock(&con->lock);
                                                /* And immediately return success. */
                                                LOG;
                                                return 0;
@@ -741,7 +1223,7 @@ int ast_add_extension2(struct ast_context *con,
                                                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);
+                                               ast_pthread_mutex_unlock(&con->lock);
                                                return -1;
                                        }
                                } else if (e->priority > tmp->priority) {
@@ -763,7 +1245,7 @@ int ast_add_extension2(struct ast_context *con,
                                                tmp->peer = con->root->peer;
                                                con->root = tmp;
                                        }
-                                       pthread_mutex_unlock(&con->lock);
+                                       ast_pthread_mutex_unlock(&con->lock);
                                        /* And immediately return success. */
                                        LOG;
                                        return 0;
@@ -774,7 +1256,7 @@ int ast_add_extension2(struct ast_context *con,
                        /* 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);
+                       ast_pthread_mutex_unlock(&con->lock);
                        /* And immediately return success. */
                        LOG;
                        return 0;
@@ -790,7 +1272,7 @@ int ast_add_extension2(struct ast_context *con,
                                /* We're at the top of the list */
                                con->root = tmp;
                        }
-                       pthread_mutex_unlock(&con->lock);
+                       ast_pthread_mutex_unlock(&con->lock);
                        /* And immediately return success. */
                        LOG;
                        return 0;
@@ -804,21 +1286,23 @@ int ast_add_extension2(struct ast_context *con,
                el->next = tmp;
        else
                con->root = tmp;
-       pthread_mutex_unlock(&con->lock);
+       ast_pthread_mutex_unlock(&con->lock);
        LOG;
        return 0;       
 }
 
-void ast_context_destroy(struct ast_context *con)
+void ast_context_destroy(struct ast_context *con, char *registrar)
 {
        struct ast_context *tmp, *tmpl=NULL;
-       pthread_mutex_lock(&conlock);
+       struct ast_include *tmpi, *tmpil= NULL;
+       ast_pthread_mutex_lock(&conlock);
        tmp = contexts;
        while(tmp) {
-               if (tmp == con) {
+               if (((tmp == con) || !con) &&
+                   (!registrar || !strcasecmp(registrar, tmp->registrar))) {
                        /* Okay, let's lock the structure to be sure nobody else
                           is searching through it. */
-                       if (pthread_mutex_lock(&tmp->lock)) {
+                       if (ast_pthread_mutex_lock(&tmp->lock)) {
                                ast_log(LOG_WARNING, "Unable to lock context lock\n");
                                return;
                        }
@@ -828,18 +1312,66 @@ void ast_context_destroy(struct ast_context *con)
                                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);
+                       ast_pthread_mutex_unlock(&tmp->lock);
+                       for (tmpi = tmp->includes; tmpi; ) {
+                               /* Free includes */
+                               tmpil = tmpi;
+                               tmpi = tmpi->next;
+                               free(tmpil);
+                               tmpil = tmpi;
+                       }
                        free(tmp);
-                       pthread_mutex_unlock(&conlock);
+                       if (!con) {
+                               /* Might need to get another one -- restart */
+                               tmp = contexts;
+                               tmpl = NULL;
+                               tmpil = NULL;
+                               continue;
+                       }
+                       ast_pthread_mutex_unlock(&conlock);
                        return;
                }
                tmpl = tmp;
                tmp = tmp->next;
        }
-       pthread_mutex_unlock(&conlock);
+       ast_pthread_mutex_unlock(&conlock);
+}
+
+static void wait_for_hangup(struct ast_channel *chan)
+{
+       int res;
+       struct ast_frame *f;
+       do {
+               res = ast_waitfor(chan, -1);
+               if (res < 0)
+                       return;
+               f = ast_read(chan);
+               if (f)
+                       ast_frfree(f);
+       } while(f);
+}
+
+static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
+{
+       ast_indicate(chan, AST_CONTROL_RINGING);
+       return 0;
 }
 
-int pbx_builtin_answer(struct ast_channel *chan, void *data)
+static int pbx_builtin_busy(struct ast_channel *chan, void *data)
+{
+       ast_indicate(chan, AST_CONTROL_BUSY);           
+       wait_for_hangup(chan);
+       return -1;
+}
+
+static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
+{
+       ast_indicate(chan, AST_CONTROL_CONGESTION);
+       wait_for_hangup(chan);
+       return -1;
+}
+
+static int pbx_builtin_answer(struct ast_channel *chan, void *data)
 {
        if (chan->state != AST_STATE_RING) {
                if (option_debug)
@@ -849,20 +1381,20 @@ int pbx_builtin_answer(struct ast_channel *chan, void *data)
                return ast_answer(chan);
 }
 
-int pbx_builtin_setlanguage(struct ast_channel *chan, void *data)
+static 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)
+static 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)
+static int pbx_builtin_stripmsd(struct ast_channel *chan, void *data)
 {
        char newexten[AST_MAX_EXTENSION] = "";
        if (!data || !atoi(data)) {
@@ -876,7 +1408,7 @@ int pbx_builtin_stripmsd(struct ast_channel *chan, void *data)
        return 0;
 }
 
-int pbx_builtin_prefix(struct ast_channel *chan, void *data)
+static int pbx_builtin_prefix(struct ast_channel *chan, void *data)
 {
        char newexten[AST_MAX_EXTENSION] = "";
        if (!data || !strlen(data)) {
@@ -885,10 +1417,12 @@ int pbx_builtin_prefix(struct ast_channel *chan, void *data)
        }
        snprintf(newexten, sizeof(newexten), "%s%s", (char *)data, chan->exten);
        strncpy(chan->exten, newexten, sizeof(chan->exten));
+       if (option_verbose > 2)
+               ast_verbose(VERBOSE_PREFIX_3 "Prepended prefix, new extension is %s\n", chan->exten);
        return 0;
 }
 
-int pbx_builtin_wait(struct ast_channel *chan, void *data)
+static int pbx_builtin_wait(struct ast_channel *chan, void *data)
 {
        /* Wait for "n" seconds */
        if (data && atoi((char *)data))
@@ -896,7 +1430,7 @@ int pbx_builtin_wait(struct ast_channel *chan, void *data)
        return 0;
 }
 
-int pbx_builtin_background(struct ast_channel *chan, void *data)
+static int pbx_builtin_background(struct ast_channel *chan, void *data)
 {
        int res;
        /* Answer if need be */
@@ -910,7 +1444,7 @@ int pbx_builtin_background(struct ast_channel *chan, void *data)
        return res;
 }
 
-int pbx_builtin_rtimeout(struct ast_channel *chan, void *data)
+static 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);
@@ -919,7 +1453,7 @@ int pbx_builtin_rtimeout(struct ast_channel *chan, void *data)
        return 0;
 }
 
-int pbx_builtin_dtimeout(struct ast_channel *chan, void *data)
+static 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);
@@ -928,7 +1462,7 @@ int pbx_builtin_dtimeout(struct ast_channel *chan, void *data)
        return 0;
 }
 
-int pbx_builtin_goto(struct ast_channel *chan, void *data)
+static int pbx_builtin_goto(struct ast_channel *chan, void *data)
 {
        char *s;
        char *exten, *pri, *context;
@@ -960,7 +1494,7 @@ int pbx_builtin_goto(struct ast_channel *chan, void *data)
        }
        /* At this point we have a priority and maybe an extension and a context */
        chan->priority = atoi(pri) - 1;
-       if (exten)
+       if (exten && strcasecmp(exten, "BYEXTENSION"))
                strncpy(chan->exten, exten, sizeof(chan->exten));
        if (context)
                strncpy(chan->context, context, sizeof(chan->context));
@@ -968,6 +1502,7 @@ 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;
@@ -976,10 +1511,13 @@ int load_pbx(void)
                ast_verbose( "Asterisk PBX Core Initializing\n");
                ast_verbose( "Registering builtin applications:\n");
        }
+       ast_cli_register(&showapps);
+       ast_cli_register(&showapp);
+       ast_cli_register(&showdialplan);
        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)) {
+               if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
                        ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
                        return -1;
                }