diff -Nru a/apps/app_queue.c b/apps/app_queue.c --- a/apps/app_queue.c 2005-03-22 10:37:08 -07:00 +++ b/apps/app_queue.c 2005-03-22 10:37:08 -07:00 @@ -3,7 +3,7 @@ * * True call queues with optional send URL on answer * - * Copyright (C) 1999-2004, Digium, Inc. + * Copyright (C) 1999-2005, Digium, Inc. * * Mark Spencer * @@ -14,8 +14,8 @@ * Each dynamic agent in each queue is now stored in the astdb. * When asterisk is restarted, each agent will be automatically * readded into their recorded queues. This feature can be - * configured with the 'peristent_members=<1|0>' KVP under the - * '[general]' group in queues.conf. The default is on. + * configured with the 'persistent_members=<1|0>' setting in the + * '[general]' category in queues.conf. The default is on. * * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). * @@ -123,7 +123,10 @@ " The optional URL will be sent to the called party if the channel supports\n" "it.\n" " The timeout will cause the queue to fail out after a specified number of\n" -"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"; +"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n" +" This application sets the following channel variable upon completion:\n" +" QUEUESTATUS The status of the call as a text string, one of\n" +" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n"; /* PHM 06/26/03 */ static char *app_aqm = "AddQueueMember" ; @@ -197,6 +200,31 @@ #define QUEUE_FLAG_REPORTHOLDTIME (1 << 12) /* Should we report caller hold time to answering member? */ #define QUEUE_FLAG_WRAPPED (1 << 13) /* Round Robin - wrapped around? */ #define QUEUE_FLAG_TIMEOUTRESTART (1 << 14) /* Restart timer when member call */ +#define QUEUE_FLAG_JOINUNAVAILABLE (1 << 15) /* Do we care if the queue has no reachable members? */ +#define QUEUE_FLAG_LEAVEUNAVAILABLE (1 << 16) /* If all agents in the queue are unreachable, remove callers */ + +enum queue_result { + QUEUE_UNKNOWN = 0, + QUEUE_TIMEOUT = 1, + QUEUE_JOINEMPTY = 2, + QUEUE_LEAVEEMPTY = 3, + QUEUE_JOINUNAVAIL = 4, + QUEUE_LEAVEUNAVAIL = 5, + QUEUE_FULL = 6, +}; + +const struct { + enum queue_result id; + char *text; +} queue_results[] = { + { QUEUE_UNKNOWN, "UNKNOWN" }, + { QUEUE_TIMEOUT, "TIMEOUT" }, + { QUEUE_JOINEMPTY,"JOINEMPTY" }, + { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" }, + { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" }, + { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" }, + { QUEUE_FULL, "FULL" }, +}; /* We define a custom "local user" structure because we use it not only for keeping track of what is in use but @@ -291,6 +319,17 @@ static struct ast_call_queue *queues = NULL; AST_MUTEX_DEFINE_STATIC(qlock); +static void set_queue_result(struct ast_channel *chan, enum queue_result res) +{ + int i; + + for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) { + if (queue_results[i].id == res) { + pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); + return; + } + } +} static char *int2strat(int strategy) { @@ -333,24 +372,31 @@ new->opos = *pos; } -static int has_no_members(struct ast_call_queue *q) +enum queue_member_status { + QUEUE_NO_MEMBERS, + QUEUE_NO_REACHABLE_MEMBERS, + QUEUE_NORMAL +}; + +static enum queue_member_status get_member_status(const struct ast_call_queue *q) { struct member *member; - int empty = 1; - member = q->members; - while(empty && member) { - switch(member->status) { - case AST_DEVICE_UNAVAILABLE: + enum queue_member_status result = QUEUE_NO_MEMBERS; + + for (member = q->members; member; member = member->next) { + switch (member->status) { case AST_DEVICE_INVALID: - /* Not logged on, etc */ + /* nothing to do */ + break; + case AST_DEVICE_UNAVAILABLE: + result = QUEUE_NO_REACHABLE_MEMBERS; break; default: - /* Not empty */ - empty = 0; + return QUEUE_NORMAL; } - member = member->next; } - return empty; + + return result; } struct statechange { @@ -432,7 +478,7 @@ return 0; } -static int join_queue(char *queuename, struct queue_ent *qe) +static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason) { struct ast_call_queue *q; struct queue_ent *cur, *prev = NULL; @@ -443,9 +489,17 @@ ast_mutex_lock(&qlock); for (q = queues; q; q = q->next) { if (!strcasecmp(q->name, queuename)) { + enum queue_member_status stat; /* This is our one */ ast_mutex_lock(&q->lock); - if ((!has_no_members(q) || ast_test_flag(q, QUEUE_FLAG_JOINEMPTY)) && (!q->maxlen || (q->count < q->maxlen))) { + stat = get_member_status(q); + if (!ast_test_flag(q, QUEUE_FLAG_JOINEMPTY) && (stat == QUEUE_NO_MEMBERS)) + *reason = QUEUE_JOINEMPTY; + else if (!ast_test_flag(q, QUEUE_FLAG_JOINUNAVAILABLE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) + *reason = QUEUE_JOINUNAVAIL; + else if (q->maxlen && (q->count >= q->maxlen)) + *reason = QUEUE_FULL; + else { /* There's space for us, put us at the right position inside * the queue. * Take into account the priority of the calling user */ @@ -1264,21 +1318,35 @@ return res; } -static int wait_our_turn(struct queue_ent *qe, int ringing) +static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason) { int res = 0; /* This is the holding pen for callers 2 through maxlen */ for (;;) { + enum queue_member_status stat; + if (is_our_turn(qe)) break; /* If we have timed out, break out */ - if (qe->expire && (time(NULL) > qe->expire)) + if (qe->expire && (time(NULL) > qe->expire)) { + *reason = QUEUE_TIMEOUT; break; + } + + stat = get_member_status(qe->parent); /* leave the queue if no agents, if enabled */ - if (ast_test_flag(qe->parent, QUEUE_FLAG_LEAVEWHENEMPTY) && has_no_members(qe->parent)) { + if (ast_test_flag(qe->parent, QUEUE_FLAG_LEAVEWHENEMPTY) && (stat == QUEUE_NO_MEMBERS)) { + *reason = QUEUE_LEAVEEMPTY; + leave_queue(qe); + break; + } + + /* leave the queue if no reachable agents, if enabled */ + if (ast_test_flag(qe->parent, QUEUE_FLAG_LEAVEUNAVAILABLE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { + *reason = QUEUE_LEAVEUNAVAIL; leave_queue(qe); break; } @@ -2236,6 +2304,7 @@ char *user_priority; int prio; char *queuetimeoutstr = NULL; + enum queue_result reason = QUEUE_UNKNOWN; /* whether to exit Queue application after the timeout hits */ int go_on = 0; @@ -2300,7 +2369,7 @@ qe.prio = (int)prio; qe.last_pos_said = 0; qe.last_pos = 0; - if (!join_queue(queuename, &qe)) { + if (!join_queue(queuename, &qe, &reason)) { ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->cid.cid_num ? chan->cid.cid_num : ""); /* Start music on hold */ check_turns: @@ -2312,7 +2381,7 @@ for (;;) { /* This is the wait loop for callers 2 through maxlen */ - res = wait_our_turn(&qe, ringing); + res = wait_our_turn(&qe, ringing, &reason); /* If they hungup, return immediately */ if (res < 0) { /* Record this abandoned call */ @@ -2339,8 +2408,11 @@ /* they may dial a digit from the queue context; */ /* or, they may timeout. */ + enum queue_member_status stat; + /* Leave if we have exceeded our queuetimeout */ if (qe.expire && (time(NULL) > qe.expire)) { + reason = QUEUE_TIMEOUT; res = 0; break; } @@ -2363,14 +2435,25 @@ break; } + stat = get_member_status(qe.parent); + /* leave the queue if no agents, if enabled */ - if (ast_test_flag(qe.parent, QUEUE_FLAG_LEAVEWHENEMPTY) && has_no_members(qe.parent)) { + if (ast_test_flag(qe.parent, QUEUE_FLAG_LEAVEWHENEMPTY) && (stat == QUEUE_NO_MEMBERS)) { + reason = QUEUE_LEAVEEMPTY; + res = 0; + break; + } + + /* leave the queue if no reachable agents, if enabled */ + if (ast_test_flag(qe.parent, QUEUE_FLAG_LEAVEUNAVAILABLE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { + reason = QUEUE_LEAVEUNAVAIL; res = 0; break; } /* Leave if we have exceeded our queuetimeout */ if (qe.expire && (time(NULL) > qe.expire)) { + reason = QUEUE_TIMEOUT; res = 0; break; } @@ -2396,6 +2479,7 @@ res = -1; } ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); + reason = QUEUE_TIMEOUT; res = 0; break; } @@ -2422,9 +2506,12 @@ ast_stopstream(chan); } leave_queue(&qe); + if (reason != QUEUE_UNKNOWN) + set_queue_result(chan, reason); } else { ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename); - res = 0; + set_queue_result(chan, reason); + res = 0; } LOCAL_USER_REMOVE(u); return res; @@ -2594,6 +2681,10 @@ ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_JOINEMPTY); } else if (!strcasecmp(var->name, "leavewhenempty")) { ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_LEAVEWHENEMPTY); + } else if (!strcasecmp(var->name, "joinunavailable")) { + ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_JOINUNAVAILABLE); + } else if (!strcasecmp(var->name, "leavewhenunavailable")) { + ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_LEAVEUNAVAILABLE); } else if (!strcasecmp(var->name, "eventwhencalled")) { ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_EVENTWHENCALLED); } else if (!strcasecmp(var->name, "reportholdtime")) { diff -Nru a/configs/queues.conf.sample b/configs/queues.conf.sample --- a/configs/queues.conf.sample 2005-03-22 10:37:08 -07:00 +++ b/configs/queues.conf.sample 2005-03-22 10:37:08 -07:00 @@ -126,9 +126,17 @@ ; ; joinempty = yes ; +; If you wish to allow queues that have no reachable members currently to be joined, set this to yes +; +; joinunavailable = yes +; ; If you wish to remove callers from the queue if there are no agents present, set this to yes ; ; leavewhenempty = yes +; +; If you wish to remove callers from the queue if there are no reachable agents present, set this to yes +; +; leavewhenunavailable = yes ; ; Asterisk can generate AgentCalled events when an agent is rung, if this is turned on ; (may generate a LOT of extra manager events)