Newer
Older
goto out;
}
if (peer) {
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with -1 so that it is hung up properly after the
conversation. */
Mark Spencer
committed
if (!strcmp(qe->chan->type,"Zap"))
ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
if (!strcmp(peer->type,"Zap"))
ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
/* Update parameters for the queue */
member = lpeer->member;
Mark Spencer
committed
hangupcalls(outgoing, peer);
Mark Spencer
committed
if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
res2 = ast_autoservice_start(qe->chan);
if (!res2) {
if (qe->parent->memberdelay) {
ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
}
if (!res2 && announce) {
if (play_file(peer, announce))
ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
}
Mark Spencer
committed
if (!res2 && qe->parent->reportholdtime) {
if (!play_file(peer, qe->parent->sound_reporthold)) {
int holdtime;
time_t now;
time(&now);
holdtime = abs((now - qe->start) / 60);
if (holdtime < 2) {
play_file(peer, qe->parent->sound_lessthan);
ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
} else
ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
play_file(peer, qe->parent->sound_minutes);
}
}
}
res2 |= ast_autoservice_stop(qe->chan);
if (peer->_softhangup) {
/* Agent must have hung up */
ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
record_abandoned(qe);
Mark Spencer
committed
if (qe->parent->eventwhencalled) {
manager_event(EVENT_FLAG_AGENT, "AgentDump",
Mark Spencer
committed
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface);
goto out;
} else if (res2) {
/* Caller must have hung up just before being connected*/
ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
record_abandoned(qe);
ast_hangup(peer);
/* Stop music on hold */
ast_moh_stop(qe->chan);
/* If appropriate, log that we have a destination channel */
if (qe->chan->cdr)
ast_cdr_setdestchan(qe->chan->cdr, peer->name);
/* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
record_abandoned(qe);
/* Begin Monitoring */
if (qe->parent->monfmt && *qe->parent->monfmt) {
monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
Mark Spencer
committed
if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
which = qe->chan;
else
which = peer;
Mark Spencer
committed
if (monitorfilename)
ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
Mark Spencer
committed
else
ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
Mark Spencer
committed
if (qe->parent->monjoin)
ast_monitor_setjoinfiles(which, 1);
/* Drop out of the queue at this point, to prepare for next caller */
leave_queue(qe);
Mark Spencer
committed
if (url && !ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
if (option_debug)
ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
Mark Spencer
committed
ast_channel_sendurl(peer, url);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
Mark Spencer
committed
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentConnect",
Mark Spencer
committed
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"Holdtime: %ld\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface,
(long)time(NULL) - qe->start);
Kevin P. Fleming
committed
ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
Mark Spencer
committed
bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
} else if (qe->chan->_softhangup) {
Mark Spencer
committed
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
(long)(callstart - qe->start), (long)(time(NULL) - callstart));
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
Mark Spencer
committed
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"HoldTime: %ld\r\n"
"TalkTime: %ld\r\n"
"Reason: caller\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface,
(long)(callstart - qe->start), (long)(time(NULL) - callstart));
} else {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
Mark Spencer
committed
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
Mark Spencer
committed
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"HoldTime: %ld\r\n"
"TalkTime: %ld\r\n"
"Reason: agent\r\n",
queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
(long)(time(NULL) - callstart));
if(bridge != AST_PBX_NO_HANGUP_PEER)
ast_hangup(peer);
update_queue(qe->parent, member);
if (bridge == 0)
res = 1; /* JDG: bridge successfull, leave app_queue */
else
res = bridge; /* bridge error, stay in the queue */
Mark Spencer
committed
hangupcalls(outgoing, NULL);
return res;
}
static int wait_a_bit(struct queue_ent *qe)
{
/* Don't need to hold the lock while we setup the outgoing calls */
int retrywait = qe->parent->retry * 1000;
Mark Spencer
committed
return ast_waitfordigit(qe->chan, retrywait);
}
static struct member * interface_exists(struct ast_call_queue *q, char *interface)
if (q)
for (mem = q->members; mem; mem = mem->next)
if (!strcmp(interface, mem->interface))
return mem;
/* Dump all members in a specific queue to the databse
*
* <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
*
*/
static void dump_queue_members(struct ast_call_queue *pm_queue)
{
char value[PM_MAX_LEN];
int value_len = 0;
int res;
memset(value, 0, sizeof(value));
for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
if (!cur_member->dynamic)
continue;
res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
cur_member->interface, cur_member->penalty, cur_member->paused,
cur_member->next ? "|" : "");
if (res != strlen(value + value_len)) {
ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
break;
}
value_len += res;
if (value_len && !cur_member) {
if (ast_db_put(pm_family, pm_queue->name, value))
ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
} else
/* Delete the entry if the queue is empty or there is an error */
ast_db_del(pm_family, pm_queue->name);
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
static int remove_from_queue(char *queuename, char *interface)
{
struct ast_call_queue *q;
struct member *last_member, *look;
int res = RES_NOSUCHQUEUE;
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (!strcmp(q->name, queuename)) {
if ((last_member = interface_exists(q, interface))) {
if ((look = q->members) == last_member) {
q->members = last_member->next;
} else {
while (look != NULL) {
if (look->next == last_member) {
look->next = last_member->next;
break;
} else {
look = look->next;
}
}
}
manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
"Queue: %s\r\n"
"Location: %s\r\n",
q->name, last_member->interface);
free(last_member);
if (queue_persistent_members)
dump_queue_members(q);
res = RES_OKAY;
} else {
res = RES_EXISTS;
}
ast_mutex_unlock(&q->lock);
break;
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
return res;
}
static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
{
struct ast_call_queue *q;
struct member *new_member;
int res = RES_NOSUCHQUEUE;
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (!strcmp(q->name, queuename)) {
if (interface_exists(q, interface) == NULL) {
new_member = create_queue_member(interface, penalty, paused);
if (new_member != NULL) {
new_member->dynamic = 1;
new_member->next = q->members;
q->members = new_member;
manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
"Queue: %s\r\n"
"Membership: %s\r\n"
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %ld\r\n"
"Status: %d\r\n"
"Paused: %d\r\n",
q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
new_member->penalty, new_member->calls, new_member->lastcall, new_member->status, new_member->paused);
res = RES_OKAY;
} else {
res = RES_OUTOFMEMORY;
}
} else {
res = RES_EXISTS;
}
ast_mutex_unlock(&q->lock);
break;
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
return res;
}
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
static int set_member_paused(char *queuename, char *interface, int paused)
{
int found = 0;
struct ast_call_queue *q;
struct member *mem;
/* Special event for when all queues are paused - individual events still generated */
if (ast_strlen_zero(queuename))
ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (ast_strlen_zero(queuename) || !strcmp(q->name, queuename)) {
if ((mem = interface_exists(q, interface))) {
found++;
if (mem->paused == paused)
ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
mem->paused = paused;
if (queue_persistent_members)
dump_queue_members(q);
ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
"Location: %s\r\n"
"Paused: %d\r\n",
q->name, mem->interface, paused);
}
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
if (found)
return RESULT_SUCCESS;
else
return RESULT_FAILURE;
}
/* Reload dynamic queue members persisted into the astdb */
static void reload_queue_members(void)
{
char *cur_ptr;
char *queue_name;
char *member;
char *interface;
char *penalty_tok;
int penalty = 0;
char *paused_tok;
int paused = 0;
struct ast_db_entry *db_tree;
struct ast_db_entry *entry;
struct ast_call_queue *cur_queue;
char queue_data[PM_MAX_LEN];
ast_mutex_lock(&qlock);
/* Each key in 'pm_family' is the name of a queue */
db_tree = ast_db_gettree(pm_family, NULL);
for (entry = db_tree; entry; entry = entry->next) {
queue_name = entry->key + strlen(pm_family) + 2;
cur_queue = queues;
while (cur_queue) {
ast_mutex_lock(&cur_queue->lock);
if (!strcmp(queue_name, cur_queue->name))
ast_mutex_unlock(&cur_queue->lock);
cur_queue = cur_queue->next;
}
if (!cur_queue) {
/* If the queue no longer exists, remove it from the
* database */
ast_db_del(pm_family, queue_name);
continue;
} else
ast_mutex_unlock(&cur_queue->lock);
if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
continue;
cur_ptr = queue_data;
while ((member = strsep(&cur_ptr, "|"))) {
if (ast_strlen_zero(member))
continue;
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
interface = strsep(&member, ";");
penalty_tok = strsep(&member, ";");
paused_tok = strsep(&member, ";");
if (!penalty_tok) {
ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
break;
}
penalty = strtol(penalty_tok, NULL, 10);
if (errno == ERANGE) {
ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
break;
}
if (!paused_tok) {
ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
break;
}
paused = strtol(paused_tok, NULL, 10);
if ((errno == ERANGE) || paused < 0 || paused > 1) {
ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused);
if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
break;
}
}
}
ast_mutex_unlock(&qlock);
if (db_tree) {
ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
ast_db_freetree(db_tree);
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
static int pqm_exec(struct ast_channel *chan, void *data)
{
struct localuser *u;
char *queuename, *interface;
if (!data) {
ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface])\n");
return -1;
}
queuename = ast_strdupa((char *)data);
if (!queuename) {
ast_log(LOG_ERROR, "Out of memory\n");
return -1;
}
interface = strchr(queuename, '|');
if (!interface) {
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface])\n");
return -1;
}
LOCAL_USER_ADD(u);
*interface = '\0';
interface++;
if (set_member_paused(queuename, interface, 1)) {
ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", interface);
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
chan->priority += 100;
LOCAL_USER_REMOVE(u);
return 0;
}
return -1;
}
LOCAL_USER_REMOVE(u);
return 0;
}
static int upqm_exec(struct ast_channel *chan, void *data)
{
struct localuser *u;
char *queuename, *interface;
if (!data) {
ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface])\n");
return -1;
}
queuename = ast_strdupa((char *)data);
if (!queuename) {
ast_log(LOG_ERROR, "Out of memory\n");
return -1;
}
interface = strchr(queuename, '|');
if (!interface) {
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface])\n");
return -1;
}
LOCAL_USER_ADD(u);
*interface = '\0';
interface++;
if (set_member_paused(queuename, interface, 0)) {
ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", interface);
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
chan->priority += 100;
LOCAL_USER_REMOVE(u);
return 0;
}
return -1;
}
LOCAL_USER_REMOVE(u);
return 0;
}
static int rqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *info, *queuename;
char *interface = NULL;
ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface])\n");
info = ast_strdupa((char *)data);
if (!info) {
ast_log(LOG_ERROR, "Out of memory\n");
return -1;
}
LOCAL_USER_ADD(u);
queuename = info;
if (queuename) {
interface = strchr(queuename, '|');
if (interface) {
*interface = '\0';
interface++;
}
Kevin P. Fleming
committed
ast_copy_string(tmpchan, chan->name, sizeof(tmpchan));
interface = strrchr(tmpchan, '-');
if (interface)
*interface = '\0';
interface = tmpchan;
}
switch (remove_from_queue(queuename, interface)) {
case RES_OKAY:
ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename);
res = 0;
break;
case RES_EXISTS:
ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
chan->priority += 100;
res = 0;
break;
case RES_NOSUCHQUEUE:
ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
res = 0;
break;
case RES_OUTOFMEMORY:
ast_log(LOG_ERROR, "Out of memory\n");
break;
}
LOCAL_USER_REMOVE(u);
return res;
}
static int aqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
char *info;
char *penaltys=NULL;
int penalty = 0;
ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\n");
info = ast_strdupa((char *)data);
if (!info) {
ast_log(LOG_ERROR, "Out of memory\n");
return -1;
}
LOCAL_USER_ADD(u);
queuename = info;
if (queuename) {
interface = strchr(queuename, '|');
if (interface) {
*interface = '\0';
interface++;
}
if (interface) {
penaltys = strchr(interface, '|');
if (penaltys) {
*penaltys = '\0';
if (!interface || ast_strlen_zero(interface)) {
Kevin P. Fleming
committed
ast_copy_string(tmpchan, chan->name, sizeof(tmpchan));
interface = strrchr(tmpchan, '-');
if (interface)
*interface = '\0';
interface = tmpchan;
}
if (penaltys && !ast_strlen_zero(penaltys)) {
if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
penalty = 0;
}
}
switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
case RES_OKAY:
ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
res = 0;
break;
case RES_EXISTS:
ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
chan->priority += 100;
res = 0;
break;
case RES_NOSUCHQUEUE:
ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
res = 0;
break;
case RES_OUTOFMEMORY:
ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", interface, queuename);
break;
}
LOCAL_USER_REMOVE(u);
return res;
}
static int queue_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
char info[512];
char *info_ptr = info;
char *url = NULL;
char *announceoverride = NULL;
char *user_priority;
int prio;
Mark Spencer
committed
enum queue_result reason = QUEUE_UNKNOWN;
/* whether to exit Queue application after the timeout hits */
int go_on = 0;
/* Our queue entry */
struct queue_ent qe;
if (!data || ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
/* Setup our queue entry */
memset(&qe, 0, sizeof(qe));
qe.start = time(NULL);
/* Parse our arguments XXX Check for failure XXX */
Kevin P. Fleming
committed
ast_copy_string(info, (char *) data, sizeof(info));
queuename = strsep(&info_ptr, "|");
options = strsep(&info_ptr, "|");
url = strsep(&info_ptr, "|");
announceoverride = strsep(&info_ptr, "|");
queuetimeoutstr = info_ptr;
/* set the expire time based on the supplied timeout; */
if (queuetimeoutstr)
qe.expire = qe.start + atoi(queuetimeoutstr);
else
qe.expire = 0;
/* Get the priority from the variable ${QUEUE_PRIO} */
user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
if (user_priority) {
if (sscanf(user_priority, "%d", &prio) == 1) {
if (option_debug)
ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
Mark Spencer
committed
chan->name, prio);
} else {
ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
Mark Spencer
committed
user_priority, chan->name);
prio = 0;
}
} else {
ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
prio = 0;
Mark Spencer
committed
if (options && (strchr(options, 'r')))
ringing = 1;
ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
Mark Spencer
committed
queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
qe.prio = (int)prio;
qe.last_pos_said = 0;
qe.last_pos = 0;
Mark Spencer
committed
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 : "");
if (ringing) {
ast_indicate(chan, AST_CONTROL_RINGING);
} else {
ast_moh_start(chan, qe.moh);
}
/* This is the wait loop for callers 2 through maxlen */
Mark Spencer
committed
res = wait_our_turn(&qe, ringing, &reason);
/* If they hungup, return immediately */
if (res < 0) {
/* Record this abandoned call */
record_abandoned(&qe);
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename);
if (valid_exit(&qe, res)) {
Kevin P. Fleming
committed
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
Mark Spencer
committed
int makeannouncement = 0;
/* This is the wait loop for the head caller*/
/* To exit, they may get their call answered; */
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
Mark Spencer
committed
enum queue_member_status stat;
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
Mark Spencer
committed
reason = QUEUE_TIMEOUT;
Mark Spencer
committed
if (makeannouncement) {
/* Make a position announcement, if enabled */
if (qe.parent->announcefrequency && !ringing)
Kevin P. Fleming
committed
res = say_position(&qe);
if (res && valid_exit(&qe, res)) {
Kevin P. Fleming
committed
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
Kevin P. Fleming
committed
break;
}
Mark Spencer
committed
makeannouncement = 1;
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, options, announceoverride, url, &go_on);
if (res) {
if (res < 0) {
if (!qe.handled)
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)
Kevin P. Fleming
committed
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
Mark Spencer
committed
stat = get_member_status(qe.parent);
Mark Spencer
committed
/* leave the queue if no agents, if enabled */
Mark Spencer
committed
if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
record_abandoned(&qe);
Mark Spencer
committed
reason = QUEUE_LEAVEEMPTY;
res = 0;
break;
}
/* leave the queue if no reachable agents, if enabled */
if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
record_abandoned(&qe);
Mark Spencer
committed
reason = QUEUE_LEAVEUNAVAIL;
Mark Spencer
committed
res = 0;
break;
}
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
Mark Spencer
committed
reason = QUEUE_TIMEOUT;
res = 0;
break;
}
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
record_abandoned(&qe);
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename);
if (res && valid_exit(&qe, res)) {
Kevin P. Fleming
committed
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
/* exit after 'timeout' cycle if 'n' option enabled */
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
res = -1;
}
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
record_abandoned(&qe);
Mark Spencer
committed
reason = QUEUE_TIMEOUT;
/* Since this is a priority queue and
* it is not sure that we are still at the head
* of the queue, go and check for our turn again.
*/
if (!is_our_turn(&qe)) {
if (option_debug)
ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
qe.chan->name);
goto check_turns;
}
if (res >= 0 && res != AST_PBX_KEEPALIVE) {
if (ringing) {
ast_indicate(chan, -1);
} else {
ast_moh_stop(chan);
}
Mark Spencer
committed
if (reason != QUEUE_UNKNOWN)
set_queue_result(chan, reason);
} else {
ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
Mark Spencer
committed
set_queue_result(chan, reason);
res = 0;
}
LOCAL_USER_REMOVE(u);
return res;
}
Kevin P. Fleming
committed
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
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;
struct ast_config *cfg;
char *cat, *tmp;
struct ast_variable *var;
struct member *prev, *cur;
int new;
char *general_val = NULL;
char interface[80];
int penalty;
ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
memset(interface, 0, sizeof(interface));
ast_mutex_lock(&qlock);
/* Mark all queues as dead for the moment */
q = queues;
while(q) {
Mark Spencer
committed
q->dead = 1;
q = q->next;
}
/* Chug through config file */
cat = ast_category_browse(cfg, NULL);
while(cat) {
if (!strcasecmp(cat, "general")) {
/* Initialize global settings */
queue_persistent_members = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
queue_persistent_members = ast_true(general_val);
} else { /* Define queue */