mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	add support for multiple-digit extensions in queue exit contexts (bug #4690)
add QUEUEAGENTCOUNT dialplan function (bug #4690) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6114 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -106,6 +106,12 @@ Queues: | |||||||
|   restore the original behavior, use "leavewhenempty=strict" or  |   restore the original behavior, use "leavewhenempty=strict" or  | ||||||
|   "joinwhenempty=strict" instead of "=yes" for those options. |   "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: | Extensions: | ||||||
|  |  | ||||||
| * By default, there is a new option called "autofallthrough" in | * By default, there is a new option called "autofallthrough" in | ||||||
|   | |||||||
| @@ -234,6 +234,7 @@ struct queue_ent { | |||||||
| 	char moh[80];			/* Name of musiconhold to be used */ | 	char moh[80];			/* Name of musiconhold to be used */ | ||||||
| 	char announce[80];		/* Announcement to play for member when call is answered */ | 	char announce[80];		/* Announcement to play for member when call is answered */ | ||||||
| 	char context[AST_MAX_CONTEXT];	/* Context when user exits queue */ | 	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 pos;			/* Where we are in the queue */ | ||||||
| 	int prio;			/* Our priority */ | 	int prio;			/* Our priority */ | ||||||
| 	int last_pos_said;              /* Last position we told the user */ | 	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) | 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)) | 	if (ast_strlen_zero(qe->context)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	tmp[0] = digit; |  | ||||||
| 	tmp[1] = '\0'; | 	/* If the extension is bad, then reset the digits to blank */ | ||||||
| 	if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->cid.cid_num)) { | 	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->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; | 		qe->chan->priority = 0; | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
| @@ -1979,11 +1996,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce | |||||||
| 			record_abandoned(qe); | 			record_abandoned(qe); | ||||||
| 			res = -1; | 			res = -1; | ||||||
| 		} else { | 		} else { | ||||||
| 			if (digit && valid_exit(qe, digit)) | 			res = digit; | ||||||
| 				res=digit; |  | ||||||
| 			else |  | ||||||
| 				/* Nobody answered, next please? */ |  | ||||||
| 				res=0; |  | ||||||
| 		} | 		} | ||||||
| 		if (option_debug) | 		if (option_debug) | ||||||
| 			ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); | 			ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); | ||||||
| @@ -2762,7 +2775,7 @@ check_turns: | |||||||
| 			if (!res)  | 			if (!res)  | ||||||
| 				break; | 				break; | ||||||
| 			if (valid_exit(&qe, res)) { | 			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; | 				break; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -2789,7 +2802,7 @@ check_turns: | |||||||
| 					if (qe.parent->announcefrequency && !ringing) | 					if (qe.parent->announcefrequency && !ringing) | ||||||
| 						res = say_position(&qe); | 						res = say_position(&qe); | ||||||
| 					if (res && valid_exit(&qe, res)) { | 					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; | 						break; | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| @@ -2804,7 +2817,7 @@ check_turns: | |||||||
|                                                         record_abandoned(&qe); |                                                         record_abandoned(&qe); | ||||||
| 							ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); | 							ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); | ||||||
| 					} else if (res > 0) | 					} 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; | 					break; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| @@ -2846,7 +2859,7 @@ check_turns: | |||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
| 				if (res && valid_exit(&qe, res)) { | 				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; | 					break; | ||||||
| 				} | 				} | ||||||
| 				/* exit after 'timeout' cycle if 'n' option enabled */ | 				/* exit after 'timeout' cycle if 'n' option enabled */ | ||||||
| @@ -2895,6 +2908,54 @@ check_turns: | |||||||
| 	return res; | 	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) | static void reload_queues(void) | ||||||
| { | { | ||||||
| 	struct ast_call_queue *q, *ql, *qn; | 	struct ast_call_queue *q, *ql, *qn; | ||||||
| @@ -3557,6 +3618,7 @@ int unload_module(void) | |||||||
| 	ast_unregister_application(app_rqm); | 	ast_unregister_application(app_rqm); | ||||||
| 	ast_unregister_application(app_pqm); | 	ast_unregister_application(app_pqm); | ||||||
| 	ast_unregister_application(app_upqm); | 	ast_unregister_application(app_upqm); | ||||||
|  | 	ast_custom_function_unregister(&queueagentcount_function); | ||||||
| 	return ast_unregister_application(app); | 	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_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_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ; | ||||||
| 		ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ; | 		ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ; | ||||||
|  | 		ast_custom_function_register(&queueagentcount_function); | ||||||
| 	} | 	} | ||||||
| 	reload_queues(); | 	reload_queues(); | ||||||
| 	 | 	 | ||||||
|   | |||||||
| @@ -174,6 +174,21 @@ void ast_unregister_atexit(void (*func)(void)); | |||||||
| 	ast_update_use_count(); \ | 	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) { \ | #define LOCAL_USER_REMOVE(u) { \ | ||||||
| 	struct localuser *uc, *ul = NULL; \ | 	struct localuser *uc, *ul = NULL; \ | ||||||
| 	ast_mutex_lock(&localuser_lock); \ | 	ast_mutex_lock(&localuser_lock); \ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user