diff --git a/pbx.c b/pbx.c
index e70a23f0e97f6234a5588e8445670c2fa9c28a37..bbc5f22b89cea43129110ab8a1042496a700e5af 100755
--- 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,14 +56,17 @@ 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;
 };
 
@@ -78,6 +82,8 @@ struct ast_context {
 	struct ast_context *next;
 	/* Include other contexts */
 	struct ast_include *includes;
+	/* Registrar */
+	char *registrar;
 };
 
 
@@ -86,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;
 };
 
@@ -99,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 */
@@ -176,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;
 	}
@@ -186,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;
 }
 
@@ -195,7 +261,7 @@ static void pbx_destroy(struct ast_pbx *p)
 	free(p);
 }
 
-static inline int extension_match(char *pattern, char *data)
+int ast_extension_match(char *pattern, char *data)
 {
 	int match;
 	/* If they're the same return */
@@ -270,14 +336,17 @@ static int extension_close(char *pattern, char *data)
 struct ast_context *ast_context_find(char *name)
 {
 	struct ast_context *tmp;
-	pthread_mutex_lock(&conlock);
-	tmp = contexts;
-	while(tmp) {
-		if (!strcasecmp(name, tmp->name))
-			break;
-		tmp = tmp->next;
-	}
-	pthread_mutex_unlock(&conlock);
+	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;
 }
 
@@ -314,7 +383,7 @@ static struct ast_exten *pbx_find_extension(char *context, char *exten, int prio
 			eroot = tmp->root;
 			while(eroot) {
 				/* Match extension */
-				if (extension_match(eroot->exten, exten) ||
+				if (ast_extension_match(eroot->exten, exten) ||
 						((action == HELPER_CANMATCH) && (extension_close(eroot->exten, exten)))) {
 						e = eroot;
 						if (*status < STATUS_NO_PRIORITY)
@@ -355,9 +424,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
 	int status = 0;
 	char *incstack[AST_PBX_MAX_STACK];
 	int stacklen = 0;
-
-
-	if (pthread_mutex_lock(&conlock)) {
+	if (ast_pthread_mutex_lock(&conlock)) {
 		ast_log(LOG_WARNING, "Unable to obtain lock\n");
 		if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH))
 			return 0;
@@ -434,7 +501,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
 #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) {
@@ -447,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));
@@ -472,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);
@@ -484,30 +551,30 @@ 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;
@@ -522,7 +589,7 @@ int ast_pbx_longest_extension(char *context)
 	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;
 	}
@@ -531,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;
@@ -624,6 +691,11 @@ int ast_pbx_run(struct ast_channel *c)
 				}
 				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) {
 				digit = ast_waitstream(c, AST_DIGIT_ANY);
@@ -642,54 +714,68 @@ int ast_pbx_run(struct ast_channel *c)
 			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) 
@@ -718,12 +804,15 @@ static void *pbx_thread(void *data)
 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;
 	}
 	/* 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;
 	}
@@ -736,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;
 	}
@@ -747,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;
@@ -756,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;
 	}
@@ -784,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;
 		}
@@ -813,6 +1098,7 @@ 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;
@@ -823,11 +1109,11 @@ 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)
+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));
@@ -837,6 +1123,7 @@ int ast_context_add_include2(struct ast_context *con, char *value)
 	}
 	strncpy(inc->name, value, sizeof(inc->name));
 	inc->next = NULL;
+	inc->registrar = registrar;
 	pthread_mutex_lock(&con->lock);
 	incc = con->includes;
 	while(incc) {
@@ -859,7 +1146,8 @@ int ast_context_add_include2(struct ast_context *con, char *value)
 
 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) \
@@ -884,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);
@@ -926,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;
@@ -934,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) {
@@ -956,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;
@@ -967,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;
@@ -983,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;
@@ -997,22 +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;
 	struct ast_include *tmpi, *tmpil= NULL;
-	pthread_mutex_lock(&conlock);
+	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;
 			}
@@ -1022,7 +1312,7 @@ 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;
@@ -1031,16 +1321,57 @@ void ast_context_destroy(struct ast_context *con)
 				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);
 }
 
-int pbx_builtin_answer(struct ast_channel *chan, void *data)
+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;
+}
+
+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)
@@ -1050,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)) {
@@ -1077,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)) {
@@ -1086,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))
@@ -1097,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 */
@@ -1111,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);
@@ -1120,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);
@@ -1129,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;
@@ -1161,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));
@@ -1169,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;
@@ -1177,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;
 		}