Newer
Older
Kevin P. Fleming
committed
static int valid_exit(struct queue_ent *qe, char digit)
{
Kevin P. Fleming
committed
int digitlen = strlen(qe->digits);
Kevin P. Fleming
committed
Kevin P. Fleming
committed
/* Prevent possible buffer overflow */
if (digitlen < sizeof(qe->digits) - 2) {
qe->digits[digitlen] = digit;
qe->digits[digitlen + 1] = '\0';
} else {
qe->digits[0] = '\0';
return 0;
}
/* If there's no context to goto, short-circuit */
Kevin P. Fleming
committed
if (ast_strlen_zero(qe->context))
return 0;
Kevin P. Fleming
committed
/* If the extension is bad, then reset the digits to blank */
if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
qe->digits[0] = '\0';
return 0;
}
/* We have an exact match */
if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
/* Return 1 on a successful goto */
Kevin P. Fleming
committed
return 1;
}
return 0;
}
static int say_position(struct queue_ent *qe)
{
int res = 0, avgholdmins, avgholdsecs;
time_t now;
/* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
time(&now);
if ( (now - qe->last_pos) < 15 )
Kevin P. Fleming
committed
return 0;
/* If either our position has changed, or we are over the freq timer, say position */
if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
Kevin P. Fleming
committed
return 0;
ast_moh_stop(qe->chan);
/* Say we're next, if we are */
if (qe->pos == 1) {
Kevin P. Fleming
committed
res = play_file(qe->chan, qe->parent->sound_next);
if (res && valid_exit(qe, res))
goto playout;
else
goto posout;
Kevin P. Fleming
committed
res = play_file(qe->chan, qe->parent->sound_thereare);
if (res && valid_exit(qe, res))
Kevin P. Fleming
committed
goto playout;
res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
if (res && valid_exit(qe, res))
goto playout;
res = play_file(qe->chan, qe->parent->sound_calls);
if (res && valid_exit(qe, res))
goto playout;
}
/* Round hold time to nearest minute */
avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
/* If they have specified a rounding then round the seconds as well */
if(qe->parent->roundingseconds) {
avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
avgholdsecs*= qe->parent->roundingseconds;
} else {
avgholdsecs=0;
}
ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
/* If the hold time is >1 min, if it's enabled, and if it's not
supposed to be only once and we have already said it, say it */
Mark Spencer
committed
if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
(!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
Kevin P. Fleming
committed
res = play_file(qe->chan, qe->parent->sound_holdtime);
if (res && valid_exit(qe, res))
goto playout;
if (avgholdmins>0) {
Mark Spencer
committed
if (avgholdmins < 2) {
Kevin P. Fleming
committed
res = play_file(qe->chan, qe->parent->sound_lessthan);
if (res && valid_exit(qe, res))
goto playout;
res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
if (res && valid_exit(qe, res))
goto playout;
} else {
res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
if (res && valid_exit(qe, res))
goto playout;
}
res = play_file(qe->chan, qe->parent->sound_minutes);
if (res && valid_exit(qe, res))
goto playout;
Kevin P. Fleming
committed
if (avgholdsecs>0) {
res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
if (res && valid_exit(qe, res))
goto playout;
res = play_file(qe->chan, qe->parent->sound_seconds);
if (res && valid_exit(qe, res))
goto playout;
Kevin P. Fleming
committed
posout:
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
qe->chan->name, qe->parent->name, qe->pos);
res = play_file(qe->chan, qe->parent->sound_thanks);
playout:
/* Set our last_pos indicators */
qe->last_pos = now;
qe->last_pos_said = qe->pos;
ast_moh_start(qe->chan, qe->moh);
Kevin P. Fleming
committed
return res;
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
}
static void recalc_holdtime(struct queue_ent *qe)
{
int oldvalue, newvalue;
/* Calculate holdtime using a recursive boxcar filter */
/* Thanks to SRT for this contribution */
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
newvalue = time(NULL) - qe->start;
ast_mutex_lock(&qe->parent->lock);
if (newvalue <= qe->parent->servicelevel)
qe->parent->callscompletedinsl++;
oldvalue = qe->parent->holdtime;
qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
ast_mutex_unlock(&qe->parent->lock);
}
static void leave_queue(struct queue_ent *qe)
{
struct ast_call_queue *q;
struct queue_ent *cur, *prev = NULL;
int pos = 0;
Mark Spencer
committed
ast_mutex_lock(&q->lock);
prev = NULL;
cur = q->head;
while(cur) {
if (cur == qe) {
q->count--;
/* Take us out of the queue */
manager_event(EVENT_FLAG_CALL, "Leave",
"Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
qe->chan->name, q->name, q->count);
#if 0
ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
#endif
/* Take us out of the queue */
if (prev)
prev->next = cur->next;
else
q->head = cur->next;
} else {
/* Renumber the people after us in the queue based on a new count */
cur->pos = ++pos;
prev = cur;
}
cur = cur->next;
}
ast_mutex_unlock(&q->lock);
Mark Spencer
committed
if (q->dead && !q->count) {
/* It's dead and nobody is in it, so kill it */
destroy_queue(q);
}
}
Mark Spencer
committed
/* Hang up a list of outgoing calls */
static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
Mark Spencer
committed
while(outgoing) {
/* Hangup any existing lines we have open */
if (outgoing->chan && (outgoing->chan != exception))
ast_hangup(outgoing->chan);
oo = outgoing;
outgoing=outgoing->next;
free(oo);
}
}
Mark Spencer
committed
static int update_status(struct ast_call_queue *q, struct member *member, int status)
{
struct member *cur;
Mark Spencer
committed
Mark Spencer
committed
/* Since a reload could have taken place, we have to traverse the list to
be sure it's still valid */
ast_mutex_lock(&q->lock);
cur = q->members;
while(cur) {
if (member == cur) {
cur->status = status;
if (!q->maskmemberstatus) {
manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
"Queue: %s\r\n"
"Location: %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, cur->interface, cur->dynamic ? "dynamic" : "static",
cur->penalty, cur->calls, cur->lastcall, cur->status, cur->paused);
}
Mark Spencer
committed
break;
}
cur = cur->next;
}
ast_mutex_unlock(&q->lock);
return 0;
}
static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
{
if (status == AST_CAUSE_BUSY)
status = AST_DEVICE_BUSY;
else if (status == AST_CAUSE_UNREGISTERED)
status = AST_DEVICE_UNAVAILABLE;
else if (status == AST_CAUSE_NOSUCHDRIVER)
status = AST_DEVICE_INVALID;
else
status = AST_DEVICE_UNKNOWN;
return update_status(q, member, status);
}
/* traverse all defined queues which have calls waiting and contain this member
return 0 if no other queue has precedence (higher weight) or 1 if found */
Mark Spencer
committed
static int compare_weight(struct ast_call_queue *rq, struct member *member)
{
struct ast_call_queue *q;
struct member *mem;
/* &qlock and &rq->lock already set by try_calling()
* to solve deadlock */
if (q == rq) /* don't check myself, could deadlock */
ast_mutex_lock(&q->lock);
if (q->count && q->members) {
for (mem = q->members; mem; mem = mem->next) {
if (mem == member) {
ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
if (q->weight > rq->weight) {
ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
}
}
}
}
ast_mutex_unlock(&q->lock);
if (found)
break;
}
ast_mutex_unlock(&qlock);
return found;
}
static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
{
int res;
Mark Spencer
committed
int status;
char tech[256];
char *location;
if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
if (option_debug)
ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
(*busies)++;
if (tmp->member->paused) {
if (option_debug)
ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
return 0;
}
if (use_weight && compare_weight(qe->parent,tmp->member)) {
ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
(*busies)++;
return 0;
}
Kevin P. Fleming
committed
ast_copy_string(tech, tmp->interface, sizeof(tech));
if ((location = strchr(tech, '/')))
*location++ = '\0';
else
location = "";
/* Request the peer */
tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
if (!tmp->chan) { /* If we can't, just go on to the next call */
#if 0
ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
#endif
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
update_dial_status(qe->parent, tmp->member, status);
(*busies)++;
return 0;
Mark Spencer
committed
} else if (status != tmp->oldstatus)
update_dial_status(qe->parent, tmp->member, status);
Mark Spencer
committed
tmp->chan->appl = "AppQueue";
tmp->chan->data = "(Outgoing Line)";
tmp->chan->whentohangup = 0;
if (tmp->chan->cid.cid_num)
free(tmp->chan->cid.cid_num);
tmp->chan->cid.cid_num = NULL;
if (tmp->chan->cid.cid_name)
free(tmp->chan->cid.cid_name);
tmp->chan->cid.cid_name = NULL;
if (tmp->chan->cid.cid_ani)
free(tmp->chan->cid.cid_ani);
tmp->chan->cid.cid_ani = NULL;
if (qe->chan->cid.cid_num)
tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
if (qe->chan->cid.cid_name)
tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
if (qe->chan->cid.cid_ani)
tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables(qe->chan, tmp->chan);
/* Presense of ADSI CPE on outgoing channel follows ours */
tmp->chan->adsicpe = qe->chan->adsicpe;
/* Place the call, but don't wait on the answer */
res = ast_call(tmp->chan, location, 0);
if (res) {
/* Again, keep going even if there's an error */
if (option_debug)
ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
else if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
ast_hangup(tmp->chan);
tmp->chan = NULL;
tmp->stillgoing = 0;
(*busies)++;
return 0;
} else {
Mark Spencer
committed
if (qe->parent->eventwhencalled) {
manager_event(EVENT_FLAG_AGENT, "AgentCalled",
"AgentCalled: %s\r\n"
"ChannelCalling: %s\r\n"
"CallerID: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n"
"Priority: %d\r\n",
tmp->interface, qe->chan->name,
tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
qe->chan->context, qe->chan->exten, qe->chan->priority);
}
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
}
return 1;
}
static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
{
struct localuser *cur;
struct localuser *best;
int bestmetric=0;
Mark Spencer
committed
do {
best = NULL;
cur = outgoing;
while(cur) {
if (cur->stillgoing && /* Not already done */
!cur->chan && /* Isn't already going */
(!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
bestmetric = cur->metric;
best = cur;
}
cur = cur->next;
}
if (best) {
if (!qe->parent->strategy) {
/* Ring everyone who shares this best metric (for ringall) */
cur = outgoing;
while(cur) {
if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
if (option_debug)
ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
ring_entry(qe, cur, busies);
}
cur = cur->next;
}
} else {
/* Ring just the best channel */
if (option_debug)
ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
ring_entry(qe, best, busies);
}
}
} while (best && !best->chan);
if (!best) {
if (option_debug)
ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
return 0;
}
return 1;
}
static int store_next(struct queue_ent *qe, struct localuser *outgoing)
{
struct localuser *cur;
struct localuser *best;
int bestmetric=0;
Mark Spencer
committed
best = NULL;
cur = outgoing;
while(cur) {
if (cur->stillgoing && /* Not already done */
!cur->chan && /* Isn't already going */
(!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
bestmetric = cur->metric;
best = cur;
}
cur = cur->next;
}
if (best) {
/* Ring just the best channel */
if (option_debug)
ast_log(LOG_DEBUG, "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;
}
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
{
int res;
ast_stopstream(chan);
res = ast_streamfile(chan, filename, chan->language);
if (!res) {
/* Wait for a keypress */
res = ast_waitstream(chan, AST_DIGIT_ANY);
if (res <= 0 || !valid_exit(qe, res))
res = 0;
/* Stop playback */
ast_stopstream(chan);
} else {
res = 0;
}
/*if (res) {
ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
res = 0;
}*/
return res;
}
static int say_periodic_announcement(struct queue_ent *qe)
{
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 */
ast_moh_stop(qe->chan);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
/* play the announcement */
res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
/* Resume Music on Hold */
ast_moh_start(qe->chan, qe->moh);
/* update last_periodic_announce_time */
qe->last_periodic_announce_time = now;
Kevin P. Fleming
committed
return res;
}
static void record_abandoned(struct queue_ent *qe)
{
ast_mutex_lock(&qe->parent->lock);
qe->parent->callsabandoned++;
ast_mutex_unlock(&qe->parent->lock);
}
Mark Spencer
committed
#define AST_MAX_WATCHERS 256
Mark Spencer
committed
#define BUILD_WATCHERS do { \
o = outgoing; \
found = -1; \
pos = 1; \
numlines = 0; \
watchers[0] = in; \
while(o) { \
/* Keep track of important channels */ \
if (o->stillgoing) { \
stillgoing = 1; \
if (o->chan) { \
watchers[pos++] = o->chan; \
found = 1; \
} \
} \
o = o->next; \
numlines++; \
} \
} while(0)
Mark Spencer
committed
static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
{
char *queue = qe->parent->name;
struct localuser *o;
int found;
int numlines;
Mark Spencer
committed
int status;
int numbusies = prebusies;
int numnochan = 0;
int stillgoing = 0;
struct localuser *peer = NULL;
Mark Spencer
committed
struct ast_channel *watchers[AST_MAX_WATCHERS];
struct ast_channel *in = qe->chan;
Mark Spencer
committed
BUILD_WATCHERS;
if ((found < 0) && stillgoing && !qe->parent->strategy) {
/* 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);
Mark Spencer
committed
BUILD_WATCHERS;
if (numlines == (numbusies + numnochan)) {
ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
} else {
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);
o = outgoing;
while(o) {
if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
if (!peer) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
peer = o;
if (!ast_strlen_zero(o->chan->call_forward)) {
char tmpchan[256]="";
char *stuff;
char *tech;
Kevin P. Fleming
committed
ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
if ((stuff = strchr(tmpchan, '/'))) {
*stuff = '\0';
stuff++;
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 */
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_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);
if (status != o->oldstatus)
update_dial_status(qe->parent, o->member, status);
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
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 {
if (o->chan->cid.cid_num)
free(o->chan->cid.cid_num);
o->chan->cid.cid_num = NULL;
if (o->chan->cid.cid_name)
free(o->chan->cid.cid_name);
o->chan->cid.cid_name = NULL;
if (in->cid.cid_num) {
o->chan->cid.cid_num = strdup(in->cid.cid_num);
if (!o->chan->cid.cid_num)
ast_log(LOG_WARNING, "Out of memory\n");
}
if (in->cid.cid_name) {
o->chan->cid.cid_name = strdup(in->cid.cid_name);
if (!o->chan->cid.cid_name)
ast_log(LOG_WARNING, "Out of memory\n");
}
Kevin P. Fleming
committed
ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
o->chan->cdrflags = in->cdrflags;
if (in->cid.cid_ani) {
if (o->chan->cid.cid_ani)
free(o->chan->cid.cid_ani);
o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
if (o->chan->cid.cid_ani)
strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
else
ast_log(LOG_WARNING, "Out of memory\n");
}
if (o->chan->cid.cid_rdnis)
free(o->chan->cid.cid_rdnis);
if (!ast_strlen_zero(in->macroexten))
o->chan->cid.cid_rdnis = strdup(in->macroexten);
else
o->chan->cid.cid_rdnis = strdup(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);
o->stillgoing = 0;
ast_hangup(o->chan);
o->chan = NULL;
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) {
switch(f->subclass) {
/* This is our guy if someone answered. */
if (!peer) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
peer = o;
}
break;
case AST_CONTROL_BUSY:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
o->stillgoing = 0;
if (in->cdr)
ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
numbusies++;
break;
case AST_CONTROL_CONGESTION:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
o->stillgoing = 0;
if (in->cdr)
ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
numbusies++;
break;
case AST_CONTROL_RINGING:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_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_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
}
}
ast_frfree(f);
} else {
o->stillgoing = 0;
ast_hangup(o->chan);
o->chan = NULL;
Mark Spencer
committed
if (qe->parent->timeoutrestart)
ring_one(qe, outgoing, &numbusies);
}
}
o = o->next;
}
if (winner == in) {
f = ast_read(in);
#if 0
if (f && (f->frametype != AST_FRAME_VOICE))
printf("Frame type: %d, %d\n", f->frametype, f->subclass);
else if (!f || (f->frametype != AST_FRAME_VOICE))
printf("Hangup received on %s\n", in->name);
#endif
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
/* Got hung up */
*to=-1;
if (f)
ast_frfree(f);
if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
ast_frfree(f);
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
*to=0;
*digit=f->subclass;
ast_frfree(f);
return NULL;
}
ast_frfree(f);
}
if (!*to && (option_verbose > 2))
ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
}
return peer;
}
static int is_our_turn(struct queue_ent *qe)
{
struct queue_ent *ch;
int res;
/* 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) {
if (option_debug)
ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
res = 1;
} else {
if (option_debug)
ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
res = 0;
}
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
}
stat = get_member_status(qe->parent);
/* leave the queue if no agents, if enabled */
Mark Spencer
committed
if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
*reason = QUEUE_LEAVEEMPTY;
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)) {
Mark Spencer
committed
*reason = QUEUE_LEAVEUNAVAIL;
leave_queue(qe);
break;
}
/* Make a position announcement, if enabled */
if (qe->parent->announcefrequency && !ringing)
Kevin P. Fleming
committed
res = say_position(qe);
if (res)
break;
/* Make a periodic announcement, if enabled */
if (qe->parent->periodicannouncefrequency && !ringing)
res = say_periodic_announcement(qe);
if (!res) res = ast_waitfordigit(qe->chan, RECHECK * 1000);
if (res)
break;
}
return res;
}
static int update_queue(struct ast_call_queue *q, struct member *member)
{
struct member *cur;
Mark Spencer
committed
/* Since a reload could have taken place, we have to traverse the list to
be sure it's still valid */
ast_mutex_lock(&q->lock);
cur = q->members;
while(cur) {
time(&cur->lastcall);
cur->calls++;
break;
}
cur = cur->next;
}
ast_mutex_unlock(&q->lock);
return 0;
}
static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
{
switch (q->strategy) {
case QUEUE_STRATEGY_RINGALL:
/* Everyone equal, except for penalty */
tmp->metric = mem->penalty * 1000000;
case QUEUE_STRATEGY_ROUNDROBIN:
if (!pos) {
Mark Spencer
committed
if (!q->wrapped) {
/* No more channels, start over */
q->rrpos = 0;
} else {
/* Prioritize next entry */
q->rrpos++;
Mark Spencer
committed
q->wrapped = 0;
}
/* Fall through */
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:
tmp->metric = rand() % 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;
Mark Spencer
committed
static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
{
struct member *cur;
struct localuser *outgoing=NULL, *tmp = NULL;
int to;
char restofit[AST_MAX_EXTENSION];
char oldexten[AST_MAX_EXTENSION]="";
char oldcontext[AST_MAX_CONTEXT]="";
struct ast_channel *which;
struct localuser *lpeer;
struct member *member;
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;
memset(&bridge_config, 0, sizeof(bridge_config));
Mark Spencer
committed
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
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 '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 'n':
if ((now - qe->start >= qe->parent->timeout))
*go_on = 1;
break;