Newer
Older
return 1;
}
/*! \brief find the entry with the best metric, or NULL */
static struct callattempt *find_best(struct callattempt *outgoing)
{
struct callattempt *best = NULL, *cur;
Mark Spencer
committed
for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->stillgoing && /* Not already done */
!cur->chan && /* Isn't already going */
(!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
}
return best;
}
/*!
Mark Michelson
committed
* \brief Place a call to a queue member.
*
* Once metrics have been calculated for each member, this function is used
* to place a call to the appropriate member (or members). The low-level
* channel-handling and error detection is handled in ring_entry
*
Mark Michelson
committed
* \retval 1 if a member was called successfully
* \retval 0 otherwise
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
{
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);
ret |= ring_entry(qe, cur, busies);
}
}
} else {
/* Ring just the best channel */
ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
ret = ring_entry(qe, best, busies);
}
/* If we have timed out, break out */
if (qe->expire && (time(NULL) >= qe->expire)) {
ast_debug(1, "Queue timed out while ringing members.\n");
ret = 0;
break;
}
}
return ret;
}
Mark Michelson
committed
/*! \brief Search for best metric and add to Round Robbin queue */
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
/*! \brief Search for best metric and add to Linear queue */
Mark Michelson
committed
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
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;
}
/*! \brief Playback announcement to queued members if period has elapsed */
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 */
if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) {
Kevin P. Fleming
committed
return 0;
/* Stop the music on hold so we can play our own file */
Steve Murphy
committed
ast_indicate(qe->chan,-1);
Steve Murphy
committed
ast_moh_stop(qe->chan);
ast_verb(3, "Playing periodic announcement\n");
if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
} else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
qe->last_periodic_announce_sound = 0;
}
/* play the announcement */
res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
/* Resume Music on Hold if the caller is going to stay in the queue */
Steve Murphy
committed
if (!res) {
ast_indicate(qe->chan, AST_CONTROL_RINGING);
Steve Murphy
committed
}
/* update last_periodic_announce_time */
Matthew Nicholson
committed
time(&qe->last_periodic_announce_time);
Matthew Nicholson
committed
qe->last_periodic_announce_time = now;
/* Update the current periodic announcement to the next announcement */
if (!qe->parent->randomperiodicannounce) {
qe->last_periodic_announce_sound++;
}
Jason Parker
committed
Kevin P. Fleming
committed
return res;
Mark Michelson
committed
/*! \brief Record that a caller gave up on waiting in queue */
static void record_abandoned(struct queue_ent *qe)
{
Jason Parker
committed
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
Mark Michelson
committed
set_queue_variables(qe->parent, qe->chan);
Jason Parker
committed
blob = ast_json_pack("{s: s, s: i, s: i}",
"Queue", qe->parent->name,
"Position", qe->pos,
"OriginalPosition", qe->opos,
"HoldTime", (int)(time(NULL) - qe->start));
ast_channel_publish_blob(qe->chan, queue_caller_abandon_type(), blob);
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. */
Jason Parker
committed
static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
{
Jason Parker
committed
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
/* Stop ringing, and resume MOH if specified */
if (qe->ring_when_ringing) {
ast_indicate(qe->chan, -1);
ast_moh_start(qe->chan, qe->moh, NULL);
}
Jason Parker
committed
blob = ast_json_pack("{s: s, s: s, s: s, s: i}",
"Queue", qe->parent->name,
"Interface", interface,
"MemberName", membername,
"RingTime", rnatime);
queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_ringnoanswer_type(), blob);
ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && autopause) {
if (qe->parent->autopausedelay > 0) {
struct member *mem;
ao2_lock(qe->parent);
if ((mem = interface_exists(qe->parent, interface))) {
time_t idletime = time(&idletime)-mem->lastcall;
if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
ao2_unlock(qe->parent);
Mark Michelson
committed
if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
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);
}
} else {
Mark Michelson
committed
/* If queue autopause is mode all, just don't send any queue to stop.
* the function will stop in all queues */
if (!set_member_paused("", interface, "Auto-Pause", 1)) {
ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
interface, qe->parent->name);
} else {
ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
}
}
}
Mark Spencer
committed
#define AST_MAX_WATCHERS 256
/*!
* \brief Wait for a member to answer the call
*
* \param[in] qe the queue_ent corresponding to the caller in the queue
* \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
* \param[in] to the amount of time (in milliseconds) to wait for a response
* \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
* \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
* \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
* \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
*
* \todo eventually all call forward logic should be intergerated into and replaced by ast_call_forward()
static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int ringing)
{
const char *queue = qe->parent->name;
struct callattempt *o, *start = NULL, *prev = NULL;
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
Mark Michelson
committed
struct ast_party_connected_line connected_caller;
char *inchan_name;
struct timeval start_time_tv = ast_tvnow();
Mark Michelson
committed
ast_party_connected_line_init(&connected_caller);
Mark Michelson
committed
ast_channel_lock(qe->chan);
inchan_name = ast_strdupa(ast_channel_name(qe->chan));
Mark Michelson
committed
ast_channel_unlock(qe->chan);
starttime = (long) time(NULL);
Mark Michelson
committed
#ifdef HAVE_EPOLL
for (epollo = outgoing; epollo; epollo = epollo->q_next) {
ast_poll_channel_add(in, epollo->chan);
}
Mark Michelson
committed
#endif
Matthew Jordan
committed
while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
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 (pos < AST_MAX_WATCHERS) {
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");
/* BUGBUG: We shouldn't have to set anything here, as each
* individual dial attempt should have set that CDR to busy
*/
Mark Michelson
committed
ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
/* BUGBUG: We shouldn't have to set anything here, as each
* individual dial attempt should have set that CDR to busy
*/
/* Poll for events from both the incoming channel as well as any outgoing channels */
/* Service all of the outgoing channels */
for (o = start; o; o = o->call_next) {
/* We go with a fixed buffer here instead of using ast_strdupa. Using
Mark Michelson
committed
* ast_strdupa in a loop like this one can cause a stack overflow
*/
char ochan_name[AST_CHANNEL_NAME];
if (o->chan) {
ast_channel_lock(o->chan);
ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
ast_channel_unlock(o->chan);
}
if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
Mark Michelson
committed
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
if (!o->block_connected_update) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
} else if (!o->dial_callerid_absent) {
Mark Michelson
committed
ast_channel_lock(o->chan);
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
Mark Michelson
committed
ast_channel_unlock(o->chan);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
Richard Mudgett
committed
if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
ast_channel_update_connected_line(in, &connected_caller, NULL);
}
Mark Michelson
committed
ast_party_connected_line_free(&connected_caller);
}
}
if (o->aoc_s_rate_list) {
size_t encoded_size;
struct ast_aoc_encoded *encoded;
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
ast_aoc_destroy_encoded(encoded);
}
}
peer = o;
ast_copy_string(on, o->member->interface, sizeof(on));
ast_copy_string(membername, o->member->membername, sizeof(membername));
/* Before processing channel, go ahead and check for forwarding */
if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
numnochan++;
do_hang(o);
winner = NULL;
continue;
} else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
Mark Michelson
committed
struct ast_channel *original = o->chan;
char *stuff;
char *tech;
ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
if ((stuff = strchr(tmpchan, '/'))) {
*stuff++ = '\0';
tech = tmpchan;
} else {
snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), ast_channel_context(o->chan));
stuff = tmpchan;
tech = "Local";
}
if (!strcasecmp(tech, "Local")) {
/*
* Drop the connected line update block for local channels since
* this is going to run dialplan and the user can change his
* mind about what connected line information he wants to send.
*/
o->block_connected_update = 0;
}
Mark Michelson
committed
ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
/* Setup parameters */
o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &status);
if (!o->chan) {
ast_log(LOG_NOTICE,
"Forwarding failed to create channel to dial '%s/%s'\n",
tech, stuff);
o->stillgoing = 0;
numnochan++;
} else {
ast_channel_lock_both(o->chan, original);
ast_party_redirecting_copy(ast_channel_redirecting(o->chan),
ast_channel_redirecting(original));
ast_channel_unlock(o->chan);
ast_channel_unlock(original);
ast_channel_lock_both(o->chan, in);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, o->chan);
if (o->pending_connected_update) {
/*
* Re-seed the callattempt's connected line information with
* previously acquired connected line info from the queued
* channel. The previously acquired connected line info could
* have been set through the CONNECTED_LINE dialplan function.
*/
o->pending_connected_update = 0;
ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
}
ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in));
if (!ast_channel_redirecting(o->chan)->from.number.valid
|| ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) {
Richard Mudgett
committed
/*
* The call was not previously redirected so it is
* now redirected from this number.
*/
ast_party_number_free(&ast_channel_redirecting(o->chan)->from.number);
ast_party_number_init(&ast_channel_redirecting(o->chan)->from.number);
ast_channel_redirecting(o->chan)->from.number.valid = 1;
ast_channel_redirecting(o->chan)->from.number.str =
ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
Richard Mudgett
committed
}
Mark Michelson
committed
ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
Mark Michelson
committed
o->dial_callerid_absent = !ast_channel_caller(o->chan)->id.number.valid
|| ast_strlen_zero(ast_channel_caller(o->chan)->id.number.str);
ast_connected_line_copy_from_caller(ast_channel_connected(o->chan),
ast_channel_caller(in));
Mark Michelson
committed
ast_channel_unlock(in);
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
&& !o->block_connected_update) {
struct ast_party_redirecting redirecting;
Mark Michelson
committed
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
/*
* Redirecting updates to the caller make sense only on single
* call at a time strategies.
*
* We must unlock o->chan before calling
* ast_channel_redirecting_macro, because we put o->chan into
* autoservice there. That is pretty much a guaranteed
* deadlock. This is why the handling of o->chan's lock may
* seem a bit unusual here.
*/
ast_party_redirecting_init(&redirecting);
ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(o->chan));
ast_channel_unlock(o->chan);
if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0) &&
ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
ast_channel_update_redirecting(in, &redirecting, NULL);
}
ast_party_redirecting_free(&redirecting);
} else {
ast_channel_unlock(o->chan);
}
Mark Michelson
committed
if (ast_call(o->chan, stuff, 0)) {
ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
tech, stuff);
do_hang(o);
numnochan++;
}
}
ast_channel_lock_both(qe->chan, o->chan);
ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
ast_channel_unlock(o->chan);
ast_channel_unlock(qe->chan);
ast_channel_lock_both(qe->chan, original);
ast_channel_publish_dial_forward(qe->chan, original, NULL, "CANCEL",
ast_channel_call_forward(original));
ast_channel_unlock(original);
ast_channel_unlock(qe->chan);
/* 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) {
switch (f->subclass.integer) {
/* This is our guy if someone answered. */
if (!peer) {
Mark Michelson
committed
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
if (!o->block_connected_update) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
} else if (!o->dial_callerid_absent) {
Mark Michelson
committed
ast_channel_lock(o->chan);
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
Mark Michelson
committed
ast_channel_unlock(o->chan);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
Richard Mudgett
committed
if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
ast_channel_update_connected_line(in, &connected_caller, NULL);
}
Mark Michelson
committed
ast_party_connected_line_free(&connected_caller);
}
}
if (o->aoc_s_rate_list) {
size_t encoded_size;
struct ast_aoc_encoded *encoded;
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
ast_aoc_destroy_encoded(encoded);
}
}
peer = o;
Mark Michelson
committed
ast_verb(3, "%s is busy\n", ochan_name);
ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
do_hang(o);
Russell Bryant
committed
endtime = (long) time(NULL);
endtime -= starttime;
Jason Parker
committed
rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
/* Have enough time for a queue member to answer? */
if (ast_remaining_ms(start_time_tv, orig) > 500) {
ring_one(qe, outgoing, &numbusies);
starttime = (long) time(NULL);
}
numbusies++;
break;
case AST_CONTROL_CONGESTION:
Mark Michelson
committed
ast_verb(3, "%s is circuit-busy\n", ochan_name);
ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
Russell Bryant
committed
endtime = (long) time(NULL);
endtime -= starttime;
Jason Parker
committed
rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
do_hang(o);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
if (ast_remaining_ms(start_time_tv, orig) > 500) {
ring_one(qe, outgoing, &numbusies);
starttime = (long) time(NULL);
}
numbusies++;
break;
case AST_CONTROL_RINGING:
Mark Michelson
committed
ast_verb(3, "%s is ringing\n", ochan_name);
/* Start ring indication when the channel is ringing, if specified */
if (qe->ring_when_ringing) {
ast_moh_stop(qe->chan);
ast_indicate(qe->chan, AST_CONTROL_RINGING);
}
break;
case AST_CONTROL_OFFHOOK:
/* Ignore going off hook */
break;
Mark Michelson
committed
case AST_CONTROL_CONNECTED_LINE:
if (o->block_connected_update) {
Mark Michelson
committed
ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
break;
}
if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
Mark Michelson
committed
struct ast_party_connected_line connected;
Mark Michelson
committed
ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
ast_party_connected_line_set_init(&connected, &o->connected);
ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
ast_party_connected_line_set(&o->connected, &connected, NULL);
Mark Michelson
committed
ast_party_connected_line_free(&connected);
o->pending_connected_update = 1;
break;
}
/*
* Prevent using the CallerID from the outgoing channel since we
* got a connected line update from it.
*/
o->dial_callerid_absent = 1;
if (ast_channel_connected_line_sub(o->chan, in, f, 1) &&
ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
Mark Michelson
committed
}
break;
case AST_CONTROL_AOC:
{
struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
ast_aoc_destroy_decoded(o->aoc_s_rate_list);
o->aoc_s_rate_list = decoded;
} else {
ast_aoc_destroy_decoded(decoded);
}
}
break;
Mark Michelson
committed
case AST_CONTROL_REDIRECTING:
if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
/*
* Redirecting updates to the caller make sense only on single
* call at a time strategies.
*/
break;
}
if (o->block_connected_update) {
ast_verb(3, "Redirecting update to %s prevented\n",
inchan_name);
break;
}
ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
ochan_name, inchan_name);
if (ast_channel_redirecting_sub(o->chan, in, f, 1) &&
ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
Mark Michelson
committed
}
break;
case AST_CONTROL_PVT_CAUSE_CODE:
ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
break;
ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
Mark Michelson
committed
break;
} else { /* ast_read() returned NULL */
endtime = (long) time(NULL) - starttime;
ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
Jason Parker
committed
rna(endtime * 1000, qe, o->chan, on, membername, 1);
do_hang(o);
Russell Bryant
committed
if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
if (ast_remaining_ms(start_time_tv, orig) > 500) {
ring_one(qe, outgoing, &numbusies);
starttime = (long) time(NULL);
}
/* If we received an event from the caller, deal with it. */
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
publish_dial_end_event(in, outgoing, NULL, "CANCEL");
Mark Michelson
committed
if (f) {
Michiel van Baak
committed
if (f->data.uint32) {
ast_channel_hangupcause_set(in, f->data.uint32);
Michiel van Baak
committed
}
ast_frfree(f);
Mark Michelson
committed
}
if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
publish_dial_end_event(in, outgoing, NULL, "CANCEL");
ast_frfree(f);
if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
publish_dial_end_event(in, outgoing, NULL, "CANCEL");
*digit = f->subclass.integer;
ast_frfree(f);
return NULL;
}
Richard Mudgett
committed
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
/* Send the frame from the in channel to all outgoing channels. */
for (o = start; o; o = o->call_next) {
if (!o->stillgoing || !o->chan) {
/* This outgoing channel has died so don't send the frame to it. */
continue;
}
switch (f->frametype) {
case AST_FRAME_CONTROL:
switch (f->subclass.integer) {
case AST_CONTROL_CONNECTED_LINE:
if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
case AST_CONTROL_REDIRECTING:
if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
default:
/* We are not going to do anything with this frame. */
goto skip_frame;
}
break;
default:
/* We are not going to do anything with this frame. */
goto skip_frame;
}
}
skip_frame:;
ast_frfree(f);
/* Make a position announcement, if enabled */
if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) {
say_position(qe, ringing);
}
/* Make a periodic announcement, if enabled */
if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) {
say_periodic_announcement(qe, ringing);
}
if (!*to) {
for (o = start; o; o = o->call_next) {
Jason Parker
committed
rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
Matthew Jordan
committed
publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
Mark Michelson
committed
#ifdef HAVE_EPOLL
Mark Michelson
committed
for (epollo = outgoing; epollo; epollo = epollo->q_next) {
ast_poll_channel_del(in, epollo->chan);
}
Mark Michelson
committed
#endif
Mark Michelson
committed
/*!
Mark Michelson
committed
* \brief Check if we should start attempting to call queue members.
* A simple process, really. Count the number of members who are available
* to take our call and then see if we are in a position in the queue at
* which a member could accept our call.
*
* \param[in] qe The caller who wants to know if it is his turn
* \retval 0 It is not our turn
* \retval 1 It is our turn
static int is_our_turn(struct queue_ent *qe)
{
struct queue_ent *ch;
int res;
int avl;
int idx = 0;
/* This needs a lock. How many members are available to be served? */
ao2_lock(qe->parent);
avl = num_available_members(qe->parent);
ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
while ((idx < avl) && (ch) && (ch != qe)) {
ch = ch->next;
}
ao2_unlock(qe->parent);
/* If the queue entry is within avl [the number of available members] calls from the top ...
* Autofill and position check added to support autofill=no (as only calls
* from the front of the queue are valid when autofill is disabled)
*/
if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
return res;
}
Mark Michelson
committed
/*!
* \brief update rules for queues
*
* Calculate min/max penalties making sure if relative they stay within bounds.
* Update queues penalty and set dialplan vars, goto next list entry.
*/
Mark Michelson
committed
static void update_qe_rule(struct queue_ent *qe)
{
int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
char max_penalty_str[20], min_penalty_str[20];
Mark Michelson
committed
/* a relative change to the penalty could put it below 0 */
Mark Michelson
committed
max_penalty = 0;
Mark Michelson
committed
min_penalty = 0;
Mark Michelson
committed
min_penalty = max_penalty;
snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
Mark Michelson
committed
pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
qe->max_penalty = max_penalty;
qe->min_penalty = min_penalty;
ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
Mark Michelson
committed
qe->pr = AST_LIST_NEXT(qe->pr, list);
}
/*! \brief The waiting areas for callers who are not actively calling members
*
* This function is one large loop. This function will return if a caller
* either exits the queue or it becomes that caller's turn to attempt calling
* queue members. Inside the loop, we service the caller with periodic announcements,
* holdtime announcements, etc. as configured in queues.conf
*
* \retval 0 if the caller's turn has arrived
* \retval -1 if the caller should exit the queue.
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
/* If we have timed out, break out */
if (qe->expire && (time(NULL) >= qe->expire)) {
Mark Spencer
committed
*reason = QUEUE_TIMEOUT;
Mark Spencer
committed
}
if (qe->parent->leavewhenempty) {
int status = 0;
if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
*reason = QUEUE_LEAVEEMPTY;
ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "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 &&
Kevin P. Fleming
committed
break;
/* If we have timed out, break out */
if (qe->expire && (time(NULL) >= qe->expire)) {
*reason = QUEUE_TIMEOUT;
break;
}
/* Make a periodic announcement, if enabled */
Steve Murphy
committed
if (qe->parent->periodicannouncefrequency &&
(res = say_periodic_announcement(qe,ringing)))
Mark Michelson
committed
/* see if we need to move to the next penalty level for this queue */
while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
Mark Michelson
committed
update_qe_rule(qe);
}
/* If we have timed out, break out */
if (qe->expire && (time(NULL) >= qe->expire)) {
*reason = QUEUE_TIMEOUT;
break;
}
if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
/* If we have timed out, break out */
if (qe->expire && (time(NULL) >= qe->expire)) {
*reason = QUEUE_TIMEOUT;
break;
}
#if 0 // BUGBUG
Mark Michelson
committed
/*!
* \brief update the queue status
* \retval Always 0
*/
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
{
Mark Michelson
committed
struct member *mem;
struct call_queue *qtmp;
Mark Michelson
committed
if (shared_lastcall) {
queue_iter = ao2_iterator_init(queues, 0);
Tilghman Lesher
committed
while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
Mark Michelson
committed
ao2_lock(qtmp);
if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
Mark Michelson
committed
time(&mem->lastcall);
mem->calls++;
mem->lastqueue = q;
ao2_ref(mem, -1);
}
ao2_unlock(qtmp);
Tilghman Lesher
committed
queue_t_unref(qtmp, "Done with iterator");
Mark Michelson
committed
}
ao2_iterator_destroy(&queue_iter);