diff --git a/CHANGES b/CHANGES
index 334632a8fe976ddc59f7fe6fe905e60e2d875fb7..e9e651b2b35ef1f25f5d92db34e4f9eb6544f436 100644
--- a/CHANGES
+++ b/CHANGES
@@ -723,8 +723,12 @@ Queue changes
     is typically placed.
   * The configuration method for the "joinempty" and "leavewhenempty" options has
     changed to a comma-separated list of methods of determining member availability
-	instead of vague terms such as "yes," "loose," "no," and "strict." These old four
-	values are still accepted for backwards-compatibility, though.
+    instead of vague terms such as "yes," "loose," "no," and "strict." These old four
+    values are still accepted for backwards-compatibility, though.
+  * The average talktime is now calculated on queues. This information is reported via the
+    CLI commands "queue show" and "queues show"; through the AMI events AgentComplete, QueueSummary,
+    and QueueParams; and through the channelvariable QUEUETALKTIME if setinterfacevar=yes is set for
+    the queue.
 
 MeetMe Changes
 --------------
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 75915cdb34320769854cdb7072d92174b031f340..0ce20174a72def91d18af082a56efd9360769947 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -764,6 +764,7 @@ struct call_queue {
 	int randomperiodicannounce;         /*!< Are periodic announcments randomly chosen */
 	int roundingseconds;                /*!< How many seconds do we round to? */
 	int holdtime;                       /*!< Current avg holdtime, based on an exponential average */
+	int talktime;                       /*!< Current avg talktime, based on the same exponential average */
 	int callscompleted;                 /*!< Number of queue calls completed */
 	int callsabandoned;                 /*!< Number of queue calls abandoned */
 	int servicelevel;                   /*!< seconds setting for servicelevel*/
@@ -885,8 +886,8 @@ static void set_queue_variables(struct queue_ent *qe)
 			sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
 
 		snprintf(interfacevar, sizeof(interfacevar),
-			"QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
-			qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
+			"QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+			qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->talktime, qe->parent->callscompleted,
 			qe->parent->callsabandoned,  qe->parent->servicelevel, sl);
 	
 		pbx_builtin_setvar_multiple(qe->chan, interfacevar); 
@@ -3139,8 +3140,10 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
  * \brief update the queue status
  * \retval Always 0
 */
-static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
 {
+	int oldtalktime;
+
 	struct member *mem;
 	struct call_queue *qtmp;
 	struct ao2_iterator queue_iter;	
@@ -3169,6 +3172,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
 	q->callscompleted++;
 	if (callcompletedinsl)
 		q->callscompletedinsl++;
+	/* Calculate talktime using the same exponential average as holdtime code*/
+	oldtalktime = q->talktime;
+	q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
 	ao2_unlock(q);
 	return 0;
 }
@@ -3324,7 +3330,7 @@ static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struc
 				new_chan->exten, new_chan->context, (long) (callstart - qe->start),
 				(long) (time(NULL) - callstart), qe->opos);
 
-	update_queue(qe->parent, member, callcompletedinsl);
+	update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
 	
 	if ((datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
 		ast_channel_datastore_remove(new_chan, datastore);
@@ -4093,7 +4099,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
 				ast_channel_datastore_remove(qe->chan, tds);
 			}
 			ast_channel_unlock(qe->chan);
-			update_queue(qe->parent, member, callcompletedinsl);
+			update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
 		}
 
 		if (transfer_ds) {
@@ -5114,8 +5120,8 @@ static int queue_function_var(struct ast_channel *chan, const char *cmd, char *d
 			}
 
 			snprintf(interfacevar, sizeof(interfacevar),
-				"QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
-				q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
+				"QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+				q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
 
 			pbx_builtin_setvar_multiple(chan, interfacevar);
 		}
@@ -5753,8 +5759,8 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
 		sl = 0;
 		if (q->callscompleted > 0)
 			sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
-		ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
-			int2strat(q->strategy), q->holdtime, q->weight,
+		ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
+			int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
 			q->callscompleted, q->callsabandoned,sl,q->servicelevel);
 		do_print(s, fd, ast_str_buffer(out));
 		if (!ao2_container_count(q->members))
@@ -5951,10 +5957,11 @@ static int manager_queues_summary(struct mansession *s, const struct message *m)
 				"Available: %d\r\n"
 				"Callers: %d\r\n" 
 				"HoldTime: %d\r\n"
+				"TalkTime: %d\r\n"
 				"LongestHoldTime: %d\r\n"
 				"%s"
 				"\r\n",
-				q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
+				q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
 		}
 		ao2_unlock(q);
 		queue_unref(q);
@@ -6001,6 +6008,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 				"Strategy: %s\r\n"
 				"Calls: %d\r\n"
 				"Holdtime: %d\r\n"
+				"TalkTime: %d\r\n"
 				"Completed: %d\r\n"
 				"Abandoned: %d\r\n"
 				"ServiceLevel: %d\r\n"
@@ -6008,7 +6016,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 				"Weight: %d\r\n"
 				"%s"
 				"\r\n",
-				q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
+				q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
 				q->callsabandoned, q->servicelevel, sl, q->weight, idText);
 			/* List Queue Members */
 			mem_iter = ao2_iterator_init(q->members, 0);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index f96322c3d69ab55b64595f69380212a19c8dd41e..4418b9ad7e29b70b47b670dc51bdfb12dea7d608 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -13221,6 +13221,76 @@ static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 #undef FORMAT2
 }
 
+/*! \brief  CLI Command 'SIP Show Users' */
+static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	regex_t regexbuf;
+	int havepattern = FALSE;
+	struct ao2_iterator user_iter;
+	struct sip_peer *user;
+
+#define FORMAT  "%-25.25s  %-15.15s  %-15.15s  %-15.15s  %-5.5s%-10.10s\n"
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sip show users";
+		e->usage =
+			"Usage: sip show users [like <pattern>]\n"
+			"       Lists all known SIP users.\n"
+			"       Optional regular expression pattern is used to filter the user list.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	switch (a->argc) {
+	case 5:
+		if (!strcasecmp(a->argv[3], "like")) {
+			if (regcomp(&regexbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
+				return CLI_SHOWUSAGE;
+			havepattern = TRUE;
+		} else
+			return CLI_SHOWUSAGE;
+	case 3:
+		break;
+	default:
+		return CLI_SHOWUSAGE;
+	}
+
+	ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT");
+
+	user_iter = ao2_iterator_init(peers, 0);
+	while ((user = ao2_iterator_next(&user_iter))) {
+		ao2_lock(user);
+		if (user->onlymatchonip == FALSE) {
+			ao2_unlock(user);
+			unref_peer(user, "sip show users");
+			continue;
+		}
+
+		if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0)) {
+			ao2_unlock(user);
+			unref_peer(user, "sip show users");
+			continue;
+		}
+
+		ast_cli(a->fd, FORMAT, user->name, 
+			user->secret, 
+			user->accountcode,
+			user->context,
+			cli_yesno(user->ha != NULL),
+			nat2str(ast_test_flag(&user->flags[0], SIP_NAT)));
+		ao2_unlock(user);
+		unref_peer(user, "sip show users");
+	}
+
+	if (havepattern)
+		regfree(&regexbuf);
+
+	return CLI_SUCCESS;
+#undef FORMAT
+}
+
 /*! \brief Manager Action SIPShowRegistry description */
 static char mandescr_show_registry[] =
 "Description: Lists all registration requests and status\n"
@@ -14244,6 +14314,117 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
 	return CLI_SUCCESS;
 }
 
+/*! \brief Do completion on user name */
+static char *complete_sip_user(const char *word, int state)
+{
+	char *result = NULL;
+	int wordlen = strlen(word);
+	int which = 0;
+	struct ao2_iterator user_iter;
+	struct sip_peer *user;
+
+	user_iter = ao2_iterator_init(peers, 0);
+	while ((user = ao2_iterator_next(&user_iter))) {
+		ao2_lock(user);
+		if (user->onlymatchonip == FALSE) {
+			ao2_unlock(user);
+			unref_peer(user, "complete sip user");
+			continue;
+		}
+		/* locking of the object is not required because only the name and flags are being compared */
+		if (!strncasecmp(word, user->name, wordlen) && ++which > state) {
+			result = ast_strdup(user->name);
+		}
+		ao2_unlock(user);
+		unref_peer(user, "complete sip user");
+	}
+	return result;
+}
+/*! \brief Support routine for 'sip show user' CLI */
+static char *complete_sip_show_user(const char *line, const char *word, int pos, int state)
+{
+	if (pos == 3)
+		return complete_sip_user(word, state);
+
+	return NULL;
+}
+
+/*! \brief Show one user in detail */
+static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char cbuf[256];
+	struct sip_peer *user;
+	struct ast_variable *v;
+	int load_realtime;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sip show user";
+		e->usage =
+			"Usage: sip show user <name> [load]\n"
+			"       Shows all details on one SIP user and the current status.\n"
+			"       Option \"load\" forces lookup of peer in realtime storage.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return complete_sip_show_user(a->line, a->word, a->pos, a->n);
+	}
+
+	if (a->argc < 4)
+		return CLI_SHOWUSAGE;
+
+	/* Load from realtime storage? */
+	load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
+
+	if ((user = find_peer(a->argv[3], NULL, load_realtime, TRUE, FALSE))) {
+		ao2_lock(user);
+		ast_cli(a->fd, "\n\n");
+		ast_cli(a->fd, "  * Name       : %s\n", user->name);
+		ast_cli(a->fd, "  Secret       : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>");
+		ast_cli(a->fd, "  MD5Secret    : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>");
+		ast_cli(a->fd, "  Context      : %s\n", user->context);
+		ast_cli(a->fd, "  Language     : %s\n", user->language);
+		if (!ast_strlen_zero(user->accountcode))
+			ast_cli(a->fd, "  Accountcode  : %s\n", user->accountcode);
+		ast_cli(a->fd, "  AMA flags    : %s\n", ast_cdr_flags2str(user->amaflags));
+		ast_cli(a->fd, "  Transfer mode: %s\n", transfermode2str(user->allowtransfer));
+		ast_cli(a->fd, "  MaxCallBR    : %d kbps\n", user->maxcallbitrate);
+		ast_cli(a->fd, "  CallingPres  : %s\n", ast_describe_caller_presentation(user->callingpres));
+		ast_cli(a->fd, "  Call limit   : %d\n", user->call_limit);
+		ast_cli(a->fd, "  Callgroup    : ");
+		print_group(a->fd, user->callgroup, 0);
+		ast_cli(a->fd, "  Pickupgroup  : ");
+		print_group(a->fd, user->pickupgroup, 0);
+		ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
+		ast_cli(a->fd, "  ACL          : %s\n", cli_yesno(user->ha != NULL));
+ 		ast_cli(a->fd, "  Sess-Timers  : %s\n", stmode2str(user->stimer.st_mode_oper));
+ 		ast_cli(a->fd, "  Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
+ 		ast_cli(a->fd, "  Sess-Expires : %d secs\n", user->stimer.st_max_se);
+ 		ast_cli(a->fd, "  Sess-Min-SE  : %d secs\n", user->stimer.st_min_se);
+
+		ast_cli(a->fd, "  Codec Order  : (");
+		print_codec_to_cli(a->fd, &user->prefs);
+		ast_cli(a->fd, ")\n");
+
+		ast_cli(a->fd, "  Auto-Framing:  %s \n", cli_yesno(user->autoframing));
+		if (user->chanvars) {
+ 			ast_cli(a->fd, "  Variables    :\n");
+			for (v = user->chanvars ; v ; v = v->next)
+ 				ast_cli(a->fd, "                 %s = %s\n", v->name, v->value);
+		}
+
+		ast_cli(a->fd, "\n");
+
+		ao2_unlock(user);
+		unref_peer(user, "sip show user");
+	} else {
+		ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
+		ast_cli(a->fd, "\n");
+	}
+
+	return CLI_SUCCESS;
+}
+
+
 static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char cbuf[2256];
@@ -24002,6 +24183,8 @@ static struct ast_cli_entry cli_sip[] = {
 	AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
 	AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
 	AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
+	AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
+	AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
 	AST_CLI_DEFINE(sip_qualify_peer, "Send an OPTIONS packet to a peer"),
 	AST_CLI_DEFINE(sip_show_sched, "Present a report on the status of the sched queue"),
 	AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),