Index: doc/tex/extensions.tex
===================================================================
--- doc/tex/extensions.tex	(revision 83637)
+++ doc/tex/extensions.tex	(working copy)
@@ -66,6 +66,10 @@
           timeout is reached.  See "show function TIMEOUT" for more
           information on setting timeouts.	
   \end{itemize}
+  \item e
+  \begin{itemize}
+    \item What to do if an exception occurs
+  \end{itemize}
 \end{itemize}
 
 And finally, the extension context "default" is used when either a) an 
Index: funcs/func_exception.c
===================================================================
--- funcs/func_exception.c	(revision 0)
+++ funcs/func_exception.c	(revision 0)
@@ -0,0 +1,131 @@
+/* vim: set noet sw=8:
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Exception info dialplan function
+ *
+ * \author Matthew Nicholson <mnicholson@digium.com>
+ * 
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 61702 $")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/logger.h"
+#include "asterisk/strings.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/exception.h"
+
+static int func_exception_read(struct ast_channel *chan, const char *function,
+			     char *data, char *buf, size_t len)
+{
+	int ret = 0;
+	struct ast_exception *exception = exception_store_alloc(chan);
+	if (!exception)
+		return -1;
+
+	if (!strcasecmp(data, "type"))
+		ast_copy_string(buf, exception->type, len);
+	else if (!strcasecmp(data, "exten"))
+		ast_copy_string(buf, exception->exten, len);
+	else if (!strcasecmp(data, "priority"))
+		ast_build_string(&buf, &len, "%i", exception->priority);
+	else if (!strcasecmp(data, "context"))
+		ast_copy_string(buf, exception->context, len);
+	else if (!strcasecmp(data, "data"))
+		ast_copy_string(buf, exception->data, len);
+	else {
+		ast_log(LOG_WARNING, "Unknown item requested: '%s'\n", data);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int func_exception_write(struct ast_channel *chan, const char *function,
+			      char *data, const char *value)
+{
+	int ret = 0;
+	struct ast_exception *exception = exception_store_alloc(chan);
+	if (!exception)
+		return -1;
+	
+	if (!strcasecmp(data, "type"))
+		ast_string_field_set(exception, type, value);
+	else if (!strcasecmp(data, "exten"))
+		ast_string_field_set(exception, exten, value);
+	else if (!strcasecmp(data, "priority"))
+		exception->priority = atoi(value);
+	else if (!strcasecmp(data, "context"))
+		ast_string_field_set(exception, context, value);
+	else if (!strcasecmp(data, "data"))
+		ast_string_field_set(exception, data, value);
+	else if (!strcasecmp(data, "all")) {
+		ast_string_field_set(exception, type, value);
+		ast_string_field_set(exception, exten, value);
+		exception->priority = atoi(value);
+		ast_string_field_set(exception, context, value);
+		ast_string_field_set(exception, data, value);
+	} else { /* clear all of the valirables */
+		ast_log(LOG_WARNING, "Unknown item requested: '%s'\n", data);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static struct ast_custom_function exception_function = {
+	.name = "EXCEPTION",
+	.synopsis = "Gets/clears information about the current exception if there is one.",
+	.syntax = "EXCEPTION(item)",
+	.desc = "Gets/clears information about the current exception if there is one.\n"
+		"Avaliable information items are:\n"
+		"R/W	type	 the type of exception\n"
+		"R/W	exten	 the extension that generated the exception\n"
+		"R/W	priority the priority that generated the exception\n"
+		"R/W	context  the context that generated the exception\n"
+		"R/W	data	 any additional data about this exception\n"
+		"W/O    all      all data (intended to be used for clearing values)\n"
+		"\n"
+		"Currently the type returned can be INVALID or ERROR.\n"
+		"If there is no existing exception, any items requested will return\n"
+		"return an empty string.\n",
+	.read = func_exception_read,
+	.write = func_exception_write,
+};
+
+static int unload_module(void)
+{
+	return ast_custom_function_unregister(&exception_function);
+}
+
+static int load_module(void)
+{
+	return ast_custom_function_register(&exception_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Exception information dialplan function");
Index: include/asterisk/pbx.h
===================================================================
--- include/asterisk/pbx.h	(revision 83637)
+++ include/asterisk/pbx.h	(working copy)
@@ -37,6 +37,9 @@
 #define AST_PBX_REPLACE 1
 
 /*! \brief Special return values from applications to the PBX { */
+#define AST_PBX_HANGUP	        -1	/*!< Jump to the 'h' exten */
+#define AST_PBX_OK	        0	/*!< No errors */
+#define AST_PBX_ERROR	        1	/*!< Jump to the 'e' exten */
 #define AST_PBX_KEEPALIVE	10	/*!< Destroy the thread, but don't hang up the channel */
 #define AST_PBX_NO_HANGUP_PEER	11
 /*! } */
Index: include/asterisk/exception.h
===================================================================
--- include/asterisk/exception.h	(revision 0)
+++ include/asterisk/exception.h	(revision 0)
@@ -0,0 +1,91 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Matthew A. Nicholson <mnicholson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! 
+ * \file
+ * \brief Exception info dialplan function
+ * \author Matthew A. Nicholson <mnicholson@digium.com>
+ * \ingroup functions
+ */
+
+#ifndef ASTERISK_EXCEPTION_H
+#define ASTERISK_EXCEPTION_H
+
+#include "asterisk/channel.h"
+#include "asterisk/stringfields.h"
+
+struct ast_exception {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(context);	/*!< Context associated with this exception */
+		AST_STRING_FIELD(exten);	/*!< Exten associated with this exception */
+		AST_STRING_FIELD(data);		/*!< Data associated with this exception */
+		AST_STRING_FIELD(type);		/*!< The type of exception */
+	);
+
+	int priority;				/*!< Priority associated with this exception */
+};
+
+struct ast_exception *exception_store_alloc(struct ast_channel *chan);
+struct ast_exception *exception_store_clear(struct ast_channel *chan);
+
+#endif /* !ASTERISK_EXCEPTION_H */
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Matthew A. Nicholson <mnicholson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! 
+ * \file
+ * \brief Exception info dialplan function
+ * \author Matthew A. Nicholson <mnicholson@digium.com>
+ * \ingroup functions
+ */
+
+#ifndef ASTERISK_EXCEPTION_H
+#define ASTERISK_EXCEPTION_H
+
+#include "asterisk/channel.h"
+#include "asterisk/stringfields.h"
+
+struct ast_exception {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(context);	/*!< Context associated with this exception */
+		AST_STRING_FIELD(exten);	/*!< Exten associated with this exception */
+		AST_STRING_FIELD(data);		/*!< Data associated with this exception */
+		AST_STRING_FIELD(type);		/*!< The type of exception */
+	);
+
+	int priority;				/*!< Priority associated with this exception */
+};
+
+struct ast_exception *exception_store_alloc(struct ast_channel *chan);
+
+#endif /* !ASTERISK_EXCEPTION_H */
Index: main/Makefile
===================================================================
--- main/Makefile	(revision 83637)
+++ main/Makefile	(working copy)
@@ -18,7 +18,7 @@
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
 OBJS=	io.o sched.o logger.o frame.o loader.o config.o channel.o \
-	translate.o file.o pbx.o cli.o md5.o term.o \
+	translate.o file.o pbx.o cli.o md5.o term.o exception.o \
 	ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
 	cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \
 	dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
Index: main/pbx.c
===================================================================
--- main/pbx.c	(revision 83637)
+++ main/pbx.c	(working copy)
@@ -68,6 +68,7 @@
 #include "asterisk/stringfields.h"
 #include "asterisk/event.h"
 #include "asterisk/module.h"
+#include "asterisk/exception.h"
 
 /*!
  * \note I M P O R T A N T :
@@ -2400,6 +2401,26 @@
 				}
 				ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
 				ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+
+ 				if ((res == AST_PBX_ERROR) && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ 					/* if we are already on the 'e' exten, don't jump to it again */
+ 					if (!strcmp(c->exten, "e")) {
+ 						if (option_verbose > 1)
+ 							ast_verbose(VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", c->context, c->exten, c->priority, c->name);
+ 						error = 1;
+ 					} else {
+ 						struct ast_exception *exception = exception_store_clear(c);
+ 						if (exception) {
+ 							ast_string_field_set(exception, type, "ERROR");
+ 							ast_string_field_set(exception, exten, c->exten);
+ 							exception->priority = c->priority;
+ 							ast_string_field_set(exception, context, c->context);
+ 						}
+ 						set_ext_pri(c, "e", 1);
+ 						continue;
+ 					}
+ 				}
+
 				if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
 					c->_softhangup =0;
 				} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
@@ -2436,7 +2457,25 @@
 			if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
 				ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
 				pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
+
+				struct ast_exception *exception = exception_store_clear(c);
+				if (exception) {
+					ast_string_field_set(exception, type, "INVALID");
+					ast_string_field_set(exception, exten, c->exten);
+					exception->priority = c->priority;
+					ast_string_field_set(exception, context, c->context);
+				}
+
 				set_ext_pri(c, "i", 1);
+			} else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+				struct ast_exception *exception = exception_store_clear(c);
+				if (exception) {
+					ast_string_field_set(exception, type, "INVALID");
+					ast_string_field_set(exception, exten, c->exten);
+					exception->priority = c->priority;
+					ast_string_field_set(exception, context, c->context);
+				}
+				set_ext_pri(c, "e", 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);
@@ -2478,7 +2517,25 @@
 					if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
 						ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
 						pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
+						
+						struct ast_exception *exception = exception_store_clear(c);
+						if (exception) {
+							ast_string_field_set(exception, type, "INVALID");
+							ast_string_field_set(exception, exten, c->exten);
+							exception->priority = c->priority;
+							ast_string_field_set(exception, context, c->context);
+						}
+
 						set_ext_pri(c, "i", 1);
+					} else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+						struct ast_exception *exception = exception_store_clear(c);
+						if (exception) {
+							ast_string_field_set(exception, type, "INVALID");
+							ast_string_field_set(exception, exten, c->exten);
+							exception->priority = c->priority;
+							ast_string_field_set(exception, context, c->context);
+						}
+						set_ext_pri(c, "e", 1);
 					} else {
 						ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
 						found = 1; /* XXX disable message */
Index: main/exception.c
===================================================================
--- main/exception.c	(revision 0)
+++ main/exception.c	(revision 0)
@@ -0,0 +1,111 @@
+/* vim: set noet sw=8:
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Exception info dialplan function
+ *
+ * \author Matthew Nicholson <mnicholson@digium.com>
+ * 
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 61702 $")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/logger.h"
+#include "asterisk/strings.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/exception.h"
+
+
+static void exception_store_free(void *data)
+{
+	struct ast_exception *exception = data;
+	ast_string_field_free_pools(exception);
+	ast_free(exception);
+}
+
+static struct ast_datastore_info exception_store_info = {
+	.type = "EXCEPTION",
+	.destroy = exception_store_free,
+};
+
+/*!
+  \brief Allocate or retrieve the given channel's ast_exception
+  \param chan Pointer to an asterisk channel
+  \return pointer to the the given channel's ast_exception or NULL on failure
+*/
+struct ast_exception *exception_store_alloc(struct ast_channel *chan)
+{
+	struct ast_datastore *exception_store = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+	struct ast_exception *exception = NULL;
+	if (!exception_store) {
+		exception_store = ast_channel_datastore_alloc(&exception_store_info, NULL);
+		if (!exception_store) {
+			ast_log(LOG_ERROR, "Unable to allocate new exception datastore.\n");
+			return NULL;
+		}
+
+		exception = ast_calloc(1, sizeof(*exception));
+		if (!exception) {
+			ast_log(LOG_ERROR, "Unable to allocate exception datastore data.\n");
+			ast_channel_datastore_free(exception_store);
+			return NULL;
+		}
+		exception_store->data = exception;
+		
+		if (ast_string_field_init(exception, 128)) {
+			ast_log(LOG_ERROR, "Unable to initilize exception datastore string fields.\n");
+			ast_channel_datastore_free(exception_store);
+			return NULL;
+		}
+
+		ast_channel_datastore_add(chan, exception_store);
+	}
+	return exception_store->data;
+}
+
+/*!
+  \brief Clear the given channel's ast_exception
+  \param chan Pointer to an asterisk channel
+  \return pointer to the the given channel's ast_exception or NULL on failure
+*/
+struct ast_exception *exception_store_clear(struct ast_channel *chan)
+{
+	struct ast_exception *exception = exception_store_alloc(chan);
+	if (!exception)
+		return NULL;
+
+	ast_string_field_set(exception, type, "");
+	ast_string_field_set(exception, exten, "");
+	exception->priority = 0;
+	ast_string_field_set(exception, context, "");
+	ast_string_field_set(exception, data, "");
+
+	return exception;
+}
+
+
