Newer
Older
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);
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
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;
}
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
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
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;
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
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);
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
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
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)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, 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)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
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)
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, 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)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, 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;
}
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 */
/* Look for an existing one */
q = queues;
while(q) {
if (!strcmp(q->name, cat))
break;
q = q->next;
}
if (!q) {
/* Make one then */
q = alloc_queue(cat);
new = 1;
new = 0;
if (!new)
ast_mutex_lock(&q->lock);
/* Re-initialize the queue, and clear statistics */
init_queue(q);
clear_queue(q);
free_members(q, 0);
prev = q->members;
if (prev) {
/* find the end of any dynamic members */
while(prev->next)
prev = prev->next;
}
var = ast_variable_browse(cfg, cat);
while(var) {
if (!strcasecmp(var->name, "member")) {
/* Add a new member */
ast_copy_string(interface, var->value, sizeof(interface));
if ((tmp = strchr(interface, ','))) {
*tmp = '\0';
tmp++;
penalty = atoi(tmp);
if (penalty < 0) {
penalty = 0;
}
} else
penalty = 0;
cur = create_queue_member(interface, penalty, 0);
if (cur) {
if (prev)
prev->next = cur;
else
q->members = cur;
prev = cur;
}
} else {
queue_set_param(q, var->name, var->value, var->lineno, 1);
ast_mutex_unlock(&q->lock);
if (new) {
q->next = queues;
queues = q;
}
}
}
cat = ast_category_browse(cfg, cat);
}
q = queues;
ql = NULL;
while(q) {
qn = q->next;
Mark Spencer
committed
if (q->dead) {