Newer
Older
{
int ret = 0;
while (ret == 0) {
struct callattempt *best = find_best(outgoing);
if (!best) {
ast_debug(1, "Nobody left to try ringing in queue\n");
break;
}
Russell Bryant
committed
if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
struct callattempt *cur;
/* Ring everyone who shares this best metric (for ringall) */
for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
ring_entry(qe, cur, busies);
}
}
} else {
/* Ring just the best channel */
ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
ring_entry(qe, best, busies);
}
if (best->chan) /* break out with result = 1 */
ret = 1;
}
return ret;
}
Mark Michelson
committed
static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
struct callattempt *best = find_best(outgoing);
Mark Spencer
committed
if (best) {
/* Ring just the best channel */
ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
qe->parent->rrpos = best->metric % 1000;
} else {
/* Just increment rrpos */
Mark Spencer
committed
if (qe->parent->wrapped) {
/* No more channels, start over */
qe->parent->rrpos = 0;
} else {
/* Prioritize next entry */
qe->parent->rrpos++;
}
Mark Spencer
committed
qe->parent->wrapped = 0;
return 0;
}
Mark Michelson
committed
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
{
struct callattempt *best = find_best(outgoing);
if (best) {
/* Ring just the best channel */
ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
qe->linpos = best->metric % 1000;
} else {
/* Just increment rrpos */
if (qe->linwrapped) {
/* No more channels, start over */
qe->linpos = 0;
} else {
/* Prioritize next entry */
qe->linpos++;
}
}
qe->linwrapped = 0;
return 0;
}
Steve Murphy
committed
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
{
int res = 0;
time_t now;
/* Get the current time */
time(&now);
/* Check to see if it is time to announce */
Kevin P. Fleming
committed
if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
return 0;
/* Stop the music on hold so we can play our own file */
Steve Murphy
committed
if (ringing)
ast_indicate(qe->chan,-1);
else
ast_moh_stop(qe->chan);
ast_verb(3, "Playing periodic announcement\n");
/* Check to make sure we have a sound file. If not, reset to the first sound file */
if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
qe->last_periodic_announce_sound = 0;
}
/* play the announcement */
res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
if ((res > 0 && !valid_exit(qe, res)) || res < 0)
/* Resume Music on Hold if the caller is going to stay in the queue */
Steve Murphy
committed
if (!res) {
if (ringing)
ast_indicate(qe->chan, AST_CONTROL_RINGING);
else
ast_moh_start(qe->chan, qe->moh, NULL);
Steve Murphy
committed
}
/* update last_periodic_announce_time */
qe->last_periodic_announce_time = now;
/* Update the current periodic announcement to the next announcement */
qe->last_periodic_announce_sound++;
Kevin P. Fleming
committed
return res;
}
static void record_abandoned(struct queue_ent *qe)
{
Mark Michelson
committed
ao2_lock(qe->parent);
BJ Weschke
committed
set_queue_variables(qe);
manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Position: %d\r\n"
"OriginalPosition: %d\r\n"
"HoldTime: %d\r\n",
qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
qe->parent->callsabandoned++;
Mark Michelson
committed
ao2_unlock(qe->parent);
/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
{
ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
if (qe->parent->autopause) {
if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
} else {
ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
}
}
Mark Spencer
committed
#define AST_MAX_WATCHERS 256
static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
{
char *queue = qe->parent->name;
struct callattempt *o;
Mark Spencer
committed
int status;
int numbusies = prebusies;
int numnochan = 0;
int stillgoing = 0;
struct callattempt *peer = NULL;
struct ast_channel *in = qe->chan;
char on[80] = "";
char membername[80] = "";
long starttime = 0;
Mark Michelson
committed
long endtime = 0;
#ifdef HAVE_EPOLL
struct callattempt *epollo;
#endif
starttime = (long) time(NULL);
Mark Michelson
committed
#ifdef HAVE_EPOLL
for (epollo = outgoing; epollo; epollo = epollo->q_next) {
if(epollo->chan)
ast_poll_channel_add(in, epollo->chan);
}
Mark Michelson
committed
#endif
int numlines, retry, pos = 1;
struct ast_channel *watchers[AST_MAX_WATCHERS];
watchers[0] = in;
for (retry = 0; retry < 2; retry++) {
numlines = 0;
for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
if (o->stillgoing) { /* Keep track of important channels */
stillgoing = 1;
if (o->chan)
watchers[pos++] = o->chan;
}
numlines++;
}
if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
(qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
break;
/* On "ringall" strategy we only move to the next penalty level
when *all* ringing phones are done in the current penalty level */
ring_one(qe, outgoing, &numbusies);
/* and retry... */
if (pos == 1 /* not found */) {
if (numlines == (numbusies + numnochan)) {
ast_debug(1, "Everyone is busy at this time\n");
ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
}
*to = 0;
return NULL;
}
winner = ast_waitfor_n(watchers, pos, to);
for (o = outgoing; o; o = o->q_next) {
if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
peer = o;
ast_copy_string(on, o->member->interface, sizeof(on));
ast_copy_string(membername, o->member->membername, sizeof(membername));
if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
numnochan++;
do_hang(o);
winner = NULL;
continue;
} else if (!ast_strlen_zero(o->chan->call_forward)) {
char *stuff;
char *tech;
Kevin P. Fleming
committed
ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
if ((stuff = strchr(tmpchan, '/'))) {
*stuff++ = '\0';
tech = tmpchan;
} else {
snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
stuff = tmpchan;
tech = "Local";
}
/* Before processing channel, go ahead and check for forwarding */
ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
/* Setup parameters */
Mark Spencer
committed
o->chan = ast_request(tech, in->nativeformats, stuff, &status);
update_dial_status(qe->parent, o->member, status);
if (!o->chan) {
ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
o->stillgoing = 0;
numnochan++;
} else {
ast_channel_inherit_variables(in, o->chan);
if (o->chan->cid.cid_num)
Tilghman Lesher
committed
ast_free(o->chan->cid.cid_num);
o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
if (o->chan->cid.cid_name)
Tilghman Lesher
committed
ast_free(o->chan->cid.cid_name);
o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
ast_string_field_set(o->chan, accountcode, in->accountcode);
o->chan->cdrflags = in->cdrflags;
if (in->cid.cid_ani) {
if (o->chan->cid.cid_ani)
Tilghman Lesher
committed
ast_free(o->chan->cid.cid_ani);
o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
}
Tilghman Lesher
committed
ast_free(o->chan->cid.cid_rdnis);
o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
if (ast_call(o->chan, tmpchan, 0)) {
ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
do_hang(o);
numnochan++;
}
}
/* Hangup the original channel now, in case we needed it */
ast_hangup(winner);
continue;
}
f = ast_read(winner);
if (f) {
if (f->frametype == AST_FRAME_CONTROL) {
/* This is our guy if someone answered. */
if (!peer) {
ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
peer = o;
ast_verb(3, "%s is busy\n", o->chan->name);
do_hang(o);
Russell Bryant
committed
endtime = (long) time(NULL);
endtime -= starttime;
rna(endtime*1000, qe, on, membername);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
numbusies++;
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", o->chan->name);
Russell Bryant
committed
endtime = (long) time(NULL);
endtime -= starttime;
rna(endtime*1000, qe, on, membername);
do_hang(o);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
numbusies++;
break;
case AST_CONTROL_RINGING:
ast_verb(3, "%s is ringing\n", o->chan->name);
if (!sentringing) {
#if 0
ast_indicate(in, AST_CONTROL_RINGING);
#endif
sentringing++;
}
break;
case AST_CONTROL_OFFHOOK:
/* Ignore going off hook */
break;
default:
ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
endtime = (long) time(NULL) - starttime;
rna(endtime * 1000, qe, on, membername);
do_hang(o);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
}
}
}
if (winner == in) {
f = ast_read(in);
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
/* Got hung up */
Mark Michelson
committed
if (f) {
ast_frfree(f);
Mark Michelson
committed
}
if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
ast_frfree(f);
if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
ast_verb(3, "User pressed digit: %c\n", f->subclass);
*to = 0;
*digit = f->subclass;
ast_frfree(f);
return NULL;
}
ast_frfree(f);
Mark Michelson
committed
#ifdef HAVE_EPOLL
for(epollo = outgoing; epollo; epollo = epollo->q_next) {
if(epollo->chan)
ast_poll_channel_del(in, epollo->chan);
}
Mark Michelson
committed
#endif
static int is_our_turn(struct queue_ent *qe)
{
struct queue_ent *ch;
struct member *cur;
int avl = 0;
int idx = 0;
if (!qe->parent->autofill) {
/* Atomically read the parent head -- does not need a lock */
ch = qe->parent->head;
/* If we are now at the top of the head, break out */
if (ch == qe) {
ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
res = 1;
} else {
ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
res = 0;
}
/* This needs a lock. How many members are available to be served? */
Mark Michelson
committed
ao2_lock(qe->parent);
ch = qe->parent->head;
Russell Bryant
committed
if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
ast_debug(1, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
avl = 1;
struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&mem_iter))) {
Kevin P. Fleming
committed
switch (cur->status) {
case AST_DEVICE_NOT_INUSE:
case AST_DEVICE_UNKNOWN:
Kevin P. Fleming
committed
break;
}
}
ast_debug(1, "There are %d available members.\n", avl);
while ((idx < avl) && (ch) && (ch != qe)) {
idx++;
ch = ch->next;
}
/* If the queue entry is within avl [the number of available members] calls from the top ... */
if (ch && idx < avl) {
ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
res = 1;
} else {
ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
res = 0;
}
Mark Michelson
committed
ao2_unlock(qe->parent);
return res;
}
Mark Spencer
committed
static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
/* This is the holding pen for callers 2 through maxlen */
Mark Spencer
committed
enum queue_member_status stat;
if (is_our_turn(qe))
break;
/* If we have timed out, break out */
Mark Spencer
committed
if (qe->expire && (time(NULL) > qe->expire)) {
*reason = QUEUE_TIMEOUT;
Mark Spencer
committed
}
Kevin P. Fleming
committed
stat = get_member_status(qe->parent, qe->max_penalty);
/* leave the queue if no agents, if enabled */
Mark Spencer
committed
if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
*reason = QUEUE_LEAVEEMPTY;
Russell Bryant
committed
ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
Mark Spencer
committed
leave_queue(qe);
break;
}
/* leave the queue if no reachable agents, if enabled */
if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
*reason = QUEUE_LEAVEUNAVAIL;
Russell Bryant
committed
ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
leave_queue(qe);
break;
}
if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
Mark Spencer
committed
*reason = QUEUE_LEAVEUNAVAIL;
Russell Bryant
committed
ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
leave_queue(qe);
break;
}
/* Make a position announcement, if enabled */
Steve Murphy
committed
if (qe->parent->announcefrequency &&
(res = say_position(qe,ringing)))
Kevin P. Fleming
committed
break;
/* Make a periodic announcement, if enabled */
Steve Murphy
committed
if (qe->parent->periodicannouncefrequency &&
(res = say_periodic_announcement(qe,ringing)))
if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
if (res > 0 && !valid_exit(qe, res))
res = 0;
else
break;
}
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
{
Mark Michelson
committed
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
struct member *mem;
struct call_queue *qtmp;
struct ao2_iterator queue_iter;
if (shared_lastcall) {
queue_iter = ao2_iterator_init(queues, 0);
while ((qtmp = ao2_iterator_next(&queue_iter))) {
ao2_lock(qtmp);
if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
time(&mem->lastcall);
mem->calls++;
mem->lastqueue = q;
ao2_ref(mem, -1);
}
ao2_unlock(qtmp);
ao2_ref(qtmp, -1);
}
} else {
ao2_lock(q);
time(&member->lastcall);
member->calls++;
member->lastqueue = q;
ao2_unlock(q);
}
Mark Michelson
committed
ao2_lock(q);
if (callcompletedinsl)
q->callscompletedinsl++;
Mark Michelson
committed
ao2_unlock(q);
return 0;
}
static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
Kevin P. Fleming
committed
if (qe->max_penalty && (mem->penalty > qe->max_penalty))
Kevin P. Fleming
committed
return -1;
switch (q->strategy) {
case QUEUE_STRATEGY_RINGALL:
/* Everyone equal, except for penalty */
tmp->metric = mem->penalty * 1000000;
Mark Michelson
committed
case QUEUE_STRATEGY_LINEAR:
if (pos < qe->linpos) {
tmp->metric = 1000 + pos;
} else {
if (pos > qe->linpos)
/* Indicate there is another priority */
qe->linwrapped = 1;
tmp->metric = pos;
}
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_RRMEMORY:
if (pos < q->rrpos) {
tmp->metric = 1000 + pos;
} else {
Mark Spencer
committed
if (pos > q->rrpos)
Mark Spencer
committed
q->wrapped = 1;
tmp->metric = pos;
}
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_RANDOM:
Tilghman Lesher
committed
tmp->metric = ast_random() % 1000;
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_FEWESTCALLS:
tmp->metric = mem->calls;
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_LEASTRECENT:
if (!mem->lastcall)
tmp->metric = 0;
else
tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
tmp->metric += mem->penalty * 1000000;
break;
default:
ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
break;
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
enum agent_complete_reason {
CALLER,
AGENT,
TRANSFER
};
static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
const struct ast_channel *peer, const struct member *member, time_t callstart,
char *vars, size_t vars_len, enum agent_complete_reason rsn)
{
const char *reason;
if (!qe->parent->eventwhencalled)
return;
switch (rsn) {
case CALLER:
reason = "caller";
break;
case AGENT:
reason = "agent";
break;
case TRANSFER:
reason = "transfer";
break;
}
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"MemberName: %s\r\n"
"HoldTime: %ld\r\n"
"TalkTime: %ld\r\n"
"Reason: %s\r\n"
"%s",
queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
(long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
}
static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
struct callattempt *outgoing = NULL; /* the list of calls we are building */
Jason Parker
committed
int to, orig;
char oldexten[AST_MAX_EXTENSION]="";
char oldcontext[AST_MAX_CONTEXT]="";
char interfacevar[256]="";
struct ast_channel *which;
struct callattempt *lpeer;
struct member *member;
struct ast_app *app;
int numbusies = 0;
int x=0;
char digit = 0;
Russell Bryant
committed
time_t now = time(NULL);
Mark Spencer
committed
struct ast_bridge_config bridge_config;
char nondataquality = 1;
char *agiexec = NULL;
char *macroexec = NULL;
Steve Murphy
committed
char *gosubexec = NULL;
int ret = 0;
const char *monitorfilename;
const char *monitor_exec;
const char *monitor_options;
char tmpid[256], tmpid2[256];
char meid[1024], meid2[1024];
char mixmonargs[1512];
struct ast_app *mixmonapp = NULL;
char *p;
char vars[2048];
int forwardsallowed = 1;
Mark Spencer
committed
memset(&bridge_config, 0, sizeof(bridge_config));
Mark Spencer
committed
for (; options && *options; options++)
switch (*options) {
case 't':
ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
break;
case 'T':
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
break;
case 'w':
ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
break;
case 'W':
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
break;
Mark Spencer
committed
case 'd':
nondataquality = 0;
break;
case 'h':
ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
break;
case 'H':
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
break;
case 'k':
ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
break;
case 'K':
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
break;
Mark Spencer
committed
case 'n':
Mark Michelson
committed
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
*tries = qe->parent->membercount;
*noption = 1;
Mark Spencer
committed
break;
case 'i':
forwardsallowed = 0;
break;
Mark Spencer
committed
}
/* Hold the lock while we setup the outgoing calls */
Mark Michelson
committed
ao2_lock(queues);
ao2_lock(qe->parent);
ast_debug(1, "%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));
Mark Spencer
committed
memi = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&memi))) {
struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
if (!tmp) {
Mark Michelson
committed
ao2_unlock(qe->parent);
Mark Michelson
committed
ao2_unlock(queues);
Mark Spencer
committed
tmp->oldstatus = cur->status;
tmp->lastcall = cur->lastcall;
Mark Michelson
committed
tmp->lastqueue = cur->lastqueue;
ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
Kevin P. Fleming
committed
if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
/* Put them in the list of outgoing thingies... We're ready now.
Kevin P. Fleming
committed
XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */
tmp->q_next = outgoing;
Kevin P. Fleming
committed
outgoing = tmp;
/* If this line is up, don't try anybody else */
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break;
} else {
ao2_ref(cur, -1);
free(tmp);
Kevin P. Fleming
committed
}
if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
to = (qe->expire - now) * 1000;
else
to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
Jason Parker
committed
orig = to;
ring_one(qe, outgoing, &numbusies);
Mark Michelson
committed
ao2_unlock(qe->parent);
Mark Michelson
committed
ao2_unlock(queues);
lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
Mark Michelson
committed
ao2_lock(qe->parent);
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
Mark Michelson
committed
store_next_rr(qe, outgoing);
}
if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
store_next_lin(qe, outgoing);
Mark Michelson
committed
ao2_unlock(qe->parent);
peer = lpeer ? lpeer->chan : NULL;
if (to) {
/* Must gotten hung up */
} else {
/* User exited by pressing a digit */
Kevin P. Fleming
committed
res = digit;
}
if (res == -1)
ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
} else { /* peer is valid */
/* 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
if (!strcmp(qe->chan->tech->type, "Zap"))
Mark Spencer
committed
ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
if (!strcmp(peer->tech->type, "Zap"))
Mark Spencer
committed
ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
/* Update parameters for the queue */
time(&now);
recalc_holdtime(qe, (now - qe->start));
Mark Michelson
committed
ao2_lock(qe->parent);
callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
Mark Michelson
committed
ao2_unlock(qe->parent);
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) {
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);
ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
play_file(peer, qe->parent->sound_minutes);
}
}
}
res2 |= ast_autoservice_stop(qe->chan);
Russell Bryant
committed
if (ast_check_hangup(peer)) {
ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentDump",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
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);
Russell Bryant
committed
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
record_abandoned(qe);
ast_hangup(peer);
Steve Murphy
committed
if (ringing)
ast_indicate(qe->chan,-1);
else
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, member->membername, "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);
/* Play announcement to the caller telling it's his turn if defined */
if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
if (play_file(qe->chan, qe->parent->sound_callerannounce))
ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
}
Mark Michelson
committed
ao2_lock(qe->parent);
/* if setinterfacevar is defined, make member variables available to the channel */
/* use pbx_builtin_setvar to set a load of variables with one call */
if (qe->parent->setinterfacevar) {
snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d|MEMBERREALTIME=%d",
member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
pbx_builtin_setvar(qe->chan, interfacevar);
}
/* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
/* use pbx_builtin_setvar to set a load of variables with one call */
if (qe->parent->setqueueentryvar) {
snprintf(interfacevar,sizeof(interfacevar), "QEHOLDTIME=%ld|QEORIGINALPOS=%d",
(long) time(NULL) - qe->start, qe->opos);
pbx_builtin_setvar(qe->chan, interfacevar);
}
/* try to set queue variables if configured to do so*/
set_queue_variables(qe);
Mark Michelson
committed
ao2_unlock(qe->parent);
/* Begin Monitoring */
if (qe->parent->monfmt && *qe->parent->monfmt) {
if (!qe->parent->montype) {
ast_debug(1, "Starting Monitor as requested.\n");
monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
which = qe->chan;
else
which = peer;
if (monitorfilename)
Jason Parker
committed
ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
Jason Parker
committed
ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
else {
/* Last ditch effort -- no CDR, make up something */
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
Jason Parker
committed
ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
}
} else {
ast_debug(1, "Starting MixMonitor as requested.\n");
monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
if (!monitorfilename) {
if (qe->chan->cdr)
Tilghman Lesher
committed
ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
} else {
Tilghman Lesher
committed
const char *m = monitorfilename;
for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
switch (*m) {
case '^':
if (*(m + 1) == '{')
*p = '$';
break;
case ',':
*p++ = '\\';
/* Fall through */
default:
*p = *m;
}
Tilghman Lesher
committed
if (*m == '\0')
break;
}
Tilghman Lesher
committed
if (p == tmpid2 + sizeof(tmpid2))
tmpid2[sizeof(tmpid2) - 1] = '\0';
pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
}
monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
if (monitor_exec) {
Tilghman Lesher
committed
const char *m = monitor_exec;
for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
switch (*m) {
case '^':
if (*(m + 1) == '{')
*p = '$';
break;
case ',':
*p++ = '\\';
/* Fall through */
default:
*p = *m;