diff --git a/UPGRADE.txt b/UPGRADE.txt
index f613c39a44301fee4f172986cbc9fa487a9762a2..835b987b08da3502f039a2c63bf0f2a3b197fe29 100755
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -106,6 +106,12 @@ Queues:
   restore the original behavior, use "leavewhenempty=strict" or 
   "joinwhenempty=strict" instead of "=yes" for those options.
 
+* It is now possible to use multi-digit extensions in the exit context
+  for a queue (although you should not have overlapping extensions,
+  as there is no digit timeout). This means that the EXITWITHKEY event
+  in queue_log can now contain a key field with more than a single
+  character in it.
+
 Extensions:
 
 * By default, there is a new option called "autofallthrough" in
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 2d992735fc31d235b46c289e1f3cb26adb43479b..c0dea942625faba9af8245cfeb17664ccc5f36c0 100755
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -234,6 +234,7 @@ struct queue_ent {
 	char moh[80];			/* Name of musiconhold to be used */
 	char announce[80];		/* Announcement to play for member when call is answered */
 	char context[AST_MAX_CONTEXT];	/* Context when user exits queue */
+	char digits[AST_MAX_EXTENSION];	/* Digits entered while in queue */
 	int pos;			/* Where we are in the queue */
 	int prio;			/* Our priority */
 	int last_pos_said;              /* Last position we told the user */
@@ -977,15 +978,31 @@ static int play_file(struct ast_channel *chan, char *filename)
 
 static int valid_exit(struct queue_ent *qe, char digit)
 {
-	char tmp[2];
+	int digitlen = strlen(qe->digits);
 
+	/* Prevent possible buffer overflow */
+	if (digitlen < sizeof(qe->digits) - 2) {
+		qe->digits[digitlen] = digit;
+		qe->digits[digitlen + 1] = '\0';
+	} else {
+		qe->digits[0] = '\0';
+		return 0;
+	}
+
+ 	/* If there's no context to goto, short-circuit */
 	if (ast_strlen_zero(qe->context))
 		return 0;
-	tmp[0] = digit;
-	tmp[1] = '\0';
-	if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->cid.cid_num)) {
+
+	/* If the extension is bad, then reset the digits to blank */
+	if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
+		qe->digits[0] = '\0';
+		return 0;
+	}
+
+	/* We have an exact match */
+	if (ast_exists_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
 		ast_copy_string(qe->chan->context, qe->context, sizeof(qe->chan->context));
-		ast_copy_string(qe->chan->exten, tmp, sizeof(qe->chan->exten));
+		ast_copy_string(qe->chan->exten, qe->digits, sizeof(qe->chan->exten));
 		qe->chan->priority = 0;
 		return 1;
 	}
@@ -1979,11 +1996,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
 			record_abandoned(qe);
 			res = -1;
 		} else {
-			if (digit && valid_exit(qe, digit))
-				res=digit;
-			else
-				/* Nobody answered, next please? */
-				res=0;
+			res = digit;
 		}
 		if (option_debug)
 			ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
@@ -2762,7 +2775,7 @@ check_turns:
 			if (!res) 
 				break;
 			if (valid_exit(&qe, res)) {
-				ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
+				ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
 				break;
 			}
 		}
@@ -2789,7 +2802,7 @@ check_turns:
 					if (qe.parent->announcefrequency && !ringing)
 						res = say_position(&qe);
 					if (res && valid_exit(&qe, res)) {
-						ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
+						ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
 						break;
 					}
 
@@ -2804,7 +2817,7 @@ check_turns:
                                                         record_abandoned(&qe);
 							ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
 					} else if (res > 0)
-						ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
+						ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
 					break;
 				}
 
@@ -2846,7 +2859,7 @@ check_turns:
 					break;
 				}
 				if (res && valid_exit(&qe, res)) {
-					ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
+					ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
 					break;
 				}
 				/* exit after 'timeout' cycle if 'n' option enabled */
@@ -2895,6 +2908,54 @@ check_turns:
 	return res;
 }
 
+static char *queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+	int count = 0;
+	struct ast_call_queue *q;
+	struct localuser *u;
+	struct member *m;
+
+	if (!data || ast_strlen_zero(data)) {
+		ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
+		return "0";
+	}
+	
+	LOCAL_USER_ACF_ADD(u);
+
+	ast_mutex_lock(&qlock);
+
+	/* Find the right queue */
+	for (q = queues; q; q = q->next) {
+		if (!strcasecmp(q->name, data)) {
+			ast_mutex_lock(&q->lock);
+			break;
+		}
+	}
+
+	ast_mutex_unlock(&qlock);
+
+	if (q) {
+		for (m = q->members; m; m = m->next) {
+			/* Count the agents who are logged in and presently answering calls */
+			if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
+				count++;
+			}
+		}
+		ast_mutex_unlock(&q->lock);
+	}
+
+	snprintf(buf, len, "%d", count);
+	LOCAL_USER_REMOVE(u);
+	return buf;
+}
+
+static struct ast_custom_function queueagentcount_function = {
+	.name = "QUEUEAGENTCOUNT",
+	.synopsis = "Count number of agents answering a queue",
+	.syntax = "QUEUEAGENTCOUNT(<queuename>)",
+	.read = queue_function_qac,
+};
+
 static void reload_queues(void)
 {
 	struct ast_call_queue *q, *ql, *qn;
@@ -3557,6 +3618,7 @@ int unload_module(void)
 	ast_unregister_application(app_rqm);
 	ast_unregister_application(app_pqm);
 	ast_unregister_application(app_upqm);
+	ast_custom_function_unregister(&queueagentcount_function);
 	return ast_unregister_application(app);
 }
 
@@ -3579,6 +3641,7 @@ int load_module(void)
 		ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
 		ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
 		ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
+		ast_custom_function_register(&queueagentcount_function);
 	}
 	reload_queues();
 	
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index b6a17982c2effde5849fd24ef4bca75ecfc7177c..19e31ee9d9ceb959b1c1d03ba3b3ff38fd4f9120 100755
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -174,6 +174,21 @@ void ast_unregister_atexit(void (*func)(void));
 	ast_update_use_count(); \
 }
 
+#define LOCAL_USER_ACF_ADD(u) { \
+ \
+	if (!(u=(struct localuser *)malloc(sizeof(struct localuser)))) { \
+		ast_log(LOG_WARNING, "Out of memory\n"); \
+		return ""; \
+	} \
+	ast_mutex_lock(&localuser_lock); \
+	u->chan = chan; \
+	u->next = localusers; \
+	localusers = u; \
+	localusecnt++; \
+	ast_mutex_unlock(&localuser_lock); \
+	ast_update_use_count(); \
+}
+
 #define LOCAL_USER_REMOVE(u) { \
 	struct localuser *uc, *ul = NULL; \
 	ast_mutex_lock(&localuser_lock); \