"README.md" did not exist on "7de043e43db24c2f774b481bd608e2912cc97f9d"
Newer
Older
Mark Spencer
committed
}
/* Hold the lock while we setup the outgoing calls */
if (use_weight)
ast_mutex_lock(&qlock);
ast_mutex_lock(&qe->parent->lock);
if (option_debug)
ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
qe->chan->name);
Kevin P. Fleming
committed
ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
if (announceoverride && !ast_strlen_zero(announceoverride))
Mark Spencer
committed
Mark Spencer
committed
tmp = malloc(sizeof(*tmp));
ast_mutex_unlock(&qe->parent->lock);
if (use_weight)
ast_mutex_unlock(&qlock);
ast_log(LOG_WARNING, "Out of memory\n");
goto out;
}
Mark Spencer
committed
memset(tmp, 0, sizeof(*tmp));
if (option_debug) {
if (url)
ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
else
ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
}
tmp->member = cur; /* Never directly dereference! Could change on reload */
Mark Spencer
committed
tmp->oldstatus = cur->status;
tmp->lastcall = cur->lastcall;
ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
/* If we're dialing by extension, look at the extension to know what to dial */
if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
newnum++;
strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
calc_metric(qe->parent, cur, x++, qe, tmp);
/* Put them in the list of outgoing thingies... We're ready now.
XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */
tmp->next = outgoing;
outgoing = tmp;
/* If this line is up, don't try anybody else */
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break;
cur = cur->next;
}
if (qe->parent->timeout)
to = qe->parent->timeout * 1000;
else
to = -1;
ring_one(qe, outgoing, &numbusies);
ast_mutex_unlock(&qe->parent->lock);
if (use_weight)
ast_mutex_unlock(&qlock);
Mark Spencer
committed
lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
ast_mutex_lock(&qe->parent->lock);
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
store_next(qe, outgoing);
}
ast_mutex_unlock(&qe->parent->lock);
if (lpeer)
peer = lpeer->chan;
else
peer = NULL;
if (to) {
} else {
Kevin P. Fleming
committed
res = digit;
}
if (option_debug)
ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
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(&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 (!strcasecmp(interface, mem->interface))
/* 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);
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
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;
}
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) || !strcasecmp(q->name, queuename)) {
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
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;
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
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);
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
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_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
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_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
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);
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
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);
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
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;
qe.last_periodic_announce_time = time(NULL);
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;
/* Make a periodic announcement, if enabled */
if (qe.parent->periodicannouncefrequency && !ringing)
res = say_periodic_announcement(&qe);
if (res && valid_exit(&qe, res)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
break;
}
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, options, announceoverride, url, &go_on);
if (res) {
if (res < 0) {
Mark Spencer
committed
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);
Mark Spencer
committed
}
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)) {
Mark Spencer
committed
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)) {
Mark Spencer
committed
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)) {
Mark Spencer
committed
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 */
Mark Spencer
committed
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;
}