Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/lock.h>
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/module.h>
#include <asterisk/translate.h>
#include <asterisk/say.h>
#include <asterisk/parking.h>
#include <asterisk/musiconhold.h>
#include <asterisk/cli.h>
#include <asterisk/config.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <pthread.h>
#define QUEUE_STRATEGY_RINGALL 0
#define QUEUE_STRATEGY_ROUNDROBIN 1
#define QUEUE_STRATEGY_LEASTRECENT 2
#define QUEUE_STRATEGY_FEWESTCALLS 3
#define QUEUE_STRATEGY_RANDOM 4
static struct strategy {
int strategy;
char *name;
} strategies[] = {
{ QUEUE_STRATEGY_RINGALL, "ringall" },
{ QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
{ QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
{ QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
{ QUEUE_STRATEGY_RANDOM, "random" },
};
#define DEFAULT_RETRY 5
#define DEFAULT_TIMEOUT 15
#define RECHECK 1 /* Recheck every second to see we we're at the top yet */
static char *tdesc = "True Call Queueing";
static char *app = "Queue";
static char *synopsis = "Queue a call for a call queue";
static char *descrip =
" Queue(queuename[|options[|URL][|announceoverride]]):\n"
"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
" This application returns -1 if the originating channel hangs up, or if the\n"
"call is bridged and either of the parties in the bridge terminate the call.\n"
"Returns 0 if the queue is full, nonexistant, or has no members.\n"
"The option string may contain zero or more of the following characters:\n"
" 't' -- allow the called user transfer the calling user\n"
" 'T' -- to allow the calling user to transfer the call.\n"
" 'd' -- data-quality (modem) call (minimum delay).\n"
" 'H' -- allow caller to hang up by hitting *.\n"
" In addition to transferring the call, a call may be parked and then picked\n"
"up by another user.\n"
" The optionnal URL will be sent to the called party if the channel supports\n"
"it.\n";
// [PHM 06/26/03]
static char *app_aqm = "AddQueueMember" ;
static char *app_aqm_synopsis = "Dynamically adds queue members" ;
static char *app_aqm_descrip =
" AddQueueMember(queuename[|interface]):\n"
"Dynamically adds interface to an existing queue\n"
"Returns -1 if there is an error.\n"
"Example: AddQueueMember(techsupport|SIP/3000)\n"
"";
static char *app_rqm = "RemoveQueueMember" ;
static char *app_rqm_synopsis = "Dynamically removes queue members" ;
static char *app_rqm_descrip =
" RemoveQueueMember(queuename[|interface]):\n"
"Dynamically removes interface to an existing queue\n"
"Returns -1 if there is an error.\n"
"Example: RemoveQueueMember(techsupport|SIP/3000)\n"
"";
/* We define a customer "local user" structure because we
use it not only for keeping track of what is in use but
also for keeping track of who we're dialing. */
struct localuser {
struct ast_channel *chan;
int allowredirect_in;
int allowredirect_out;
int ringbackonly;
int musiconhold;
int dataquality;
int allowdisconnect;
struct member *member;
struct localuser *next;
};
LOCAL_USER_DECL;
struct queue_ent {
struct ast_call_queue *parent; /* What queue is our parent */
char moh[80]; /* Name of musiconhold to be used */
char announce[80]; /* Announcement to play */
char context[80]; /* Context when user exits queue */
int pos; /* Where we are in the queue */
time_t start; /* When we started holding */
struct ast_channel *chan; /* Our channel */
struct queue_ent *next; /* The next queue entry */
};
struct member {
char tech[80]; /* Technology */
char loc[256]; /* Location */
int penalty; /* Are we a last resort? */
int calls;
time_t lastcall; /* When last successful call was hungup */
struct member *next; /* Next member */
};
struct ast_call_queue {
ast_mutex_t lock;
char name[80]; /* Name of the queue */
char moh[80]; /* Name of musiconhold to be used */
char announce[80]; /* Announcement to play */
char context[80]; /* Announcement to play */
int announcetimeout; /* How often to announce their position */
int count; /* How many entries are in the queue */
int maxlen; /* Max number of entries in queue */
int dead; /* Whether this queue is dead or not */
int retry; /* Retry calling everyone after this amount of time */
int timeout; /* How long to wait for an answer */
/* Queue strategy things */
int rrpos; /* Round Robin - position */
int wrapped; /* Round Robin - wrapped around? */
struct member *members; /* Member channels to be tried */
struct queue_ent *head; /* Start of the actual queue */
struct ast_call_queue *next; /* Next call queue */
};
static struct ast_call_queue *queues = NULL;
static ast_mutex_t qlock = AST_MUTEX_INITIALIZER;
static char *int2strat(int strategy)
{
int x;
for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
if (strategy == strategies[x].strategy)
return strategies[x].name;
}
return "<unknown>";
}
static int strat2int(char *strategy)
{
int x;
for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
if (!strcasecmp(strategy, strategies[x].name))
return strategies[x].strategy;
}
return -1;
}
static int join_queue(char *queuename, struct queue_ent *qe)
{
struct ast_call_queue *q;
struct queue_ent *cur, *prev = NULL;
int res = -1;
int pos = 0;
ast_mutex_lock(&qlock);
q = queues;
while(q) {
if (!strcasecmp(q->name, queuename)) {
/* This is our one */
ast_mutex_lock(&q->lock);
if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
/* There's space for us, put us at the end */
prev = NULL;
cur = q->head;
while(cur) {
cur->pos = ++pos;
prev = cur;
cur = cur->next;
}
if (prev)
prev->next = qe;
else
q->head = qe;
/* Fix additional pointers and
information */
qe->next = NULL;
qe->parent = q;
qe->pos = ++pos;
strncpy(qe->moh, q->moh, sizeof(qe->moh));
strncpy(qe->announce, q->announce, sizeof(qe->announce));
strncpy(qe->context, q->context, sizeof(qe->context));
q->count++;
res = 0;
"Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), q->name, qe->pos, q->count );
#if 0
ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
#endif
ast_mutex_unlock(&q->lock);
ast_mutex_unlock(&qlock);
return res;
}
static void free_members(struct ast_call_queue *q)
{
struct member *curm, *next;
curm = q->members;
while(curm) {
next = curm->next;
free(curm);
curm = next;
}
q->members = NULL;
}
static void destroy_queue(struct ast_call_queue *q)
{
struct ast_call_queue *cur, *prev = NULL;
ast_mutex_lock(&qlock);
cur = queues;
while(cur) {
if (cur == q) {
if (prev)
prev->next = cur->next;
else
queues = cur->next;
} else {
prev = cur;
}
cur = cur->next;
}
ast_mutex_unlock(&qlock);
free_members(q);
free(q);
}
static void leave_queue(struct queue_ent *qe)
{
struct ast_call_queue *q;
struct queue_ent *cur, *prev = NULL;
int pos = 0;
q = qe->parent;
if (!q)
return;
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",
#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 {
cur->pos = ++pos;
prev = cur;
}
cur = cur->next;
}
ast_mutex_unlock(&q->lock);
if (q->dead && !q->count) {
/* It's dead and nobody is in it, so kill it */
destroy_queue(q);
}
}
static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
{
/* Hang up a tree of stuff */
struct localuser *oo;
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);
}
}
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
{
int res;
/* Request the peer */
tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
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'\n", cur->tech);
#endif
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
return 0;
}
tmp->chan->appl = "AppQueue";
tmp->chan->data = "(Outgoing Line)";
tmp->chan->whentohangup = 0;
if (tmp->chan->callerid)
free(tmp->chan->callerid);
if (tmp->chan->ani)
free(tmp->chan->ani);
if (qe->chan->callerid)
tmp->chan->callerid = strdup(qe->chan->callerid);
else
tmp->chan->callerid = NULL;
if (qe->chan->ani)
tmp->chan->ani = strdup(qe->chan->ani);
else
tmp->chan->ani = NULL;
/* 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, tmp->numsubst, 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->numsubst);
ast_hangup(tmp->chan);
tmp->chan = NULL;
tmp->stillgoing = 0;
return 0;
} else
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
return 0;
}
static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
{
struct localuser *cur;
struct localuser *best;
int bestmetric=0;
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)) {
ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
ring_entry(qe, cur);
}
cur = cur->next;
}
} else {
/* Ring just the best channel */
ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
ring_entry(qe, best);
}
}
} while (best && !best->chan);
if (!best) {
ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
return 0;
}
return 1;
}
static int valid_exit(struct queue_ent *qe, char digit)
char tmp[2];
if (!strlen(qe->context))
return 0;
tmp[0] = digit;
tmp[1] = '\0';
if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
qe->chan->priority = 0;
return 1;
}
return 0;
}
#define MAX 256
static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *digit)
{
char *queue = qe->parent->name;
struct localuser *o;
int found;
int numlines;
int sentringing = 0;
int numbusies = 0;
int orig = *to;
struct ast_frame *f;
struct localuser *peer = NULL;
struct ast_channel *watchers[MAX];
int pos;
struct ast_channel *winner;
struct ast_channel *in = qe->chan;
while(*to && !peer) {
o = outgoing;
found = -1;
pos = 1;
numlines = 0;
watchers[0] = in;
while(o) {
/* Keep track of important channels */
watchers[pos++] = o->chan;
found = 1;
}
o = o->next;
numlines++;
}
if (found < 0) {
if (numlines == numbusies) {
ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
} else {
ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
}
*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;
*allowredir_in = o->allowredirect_in;
*allowredir_out = o->allowredirect_out;
f = ast_read(winner);
if (f) {
if (f->frametype == AST_FRAME_CONTROL) {
switch(f->subclass) {
case AST_CONTROL_ANSWER:
/* 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;
*allowredir_in = o->allowredirect_in;
*allowredir_out = o->allowredirect_out;
*allowdisconnect = o->allowdisconnect;
}
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;
if (qe->parent->strategy)
ring_one(qe, outgoing);
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;
if (qe->parent->strategy)
ring_one(qe, outgoing);
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;
if (qe->parent->strategy)
ring_one(qe, outgoing);
}
}
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;
return NULL;
}
if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
*to=0;
return NULL;
}
if (f && (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", f->subclass);
*to=0;
*digit=f->subclass;
return NULL;
}
}
if (!*to && (option_verbose > 2))
ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
}
return peer;
}
static int wait_our_turn(struct queue_ent *qe)
{
struct queue_ent *ch;
int res = 0;
for (;;) {
/* Atomically read the parent head */
ast_mutex_lock(&qe->parent->lock);
ast_mutex_unlock(&qe->parent->lock);
/* If we are now at the top of the head, break out */
if (qe->parent->head == qe)
break;
/* Wait a second before checking again */
res = ast_waitfordigit(qe->chan, RECHECK * 1000);
if (res)
break;
}
return res;
}
static int update_queue(struct ast_call_queue *q, struct localuser *user)
{
struct member *cur;
/* 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 (user->member == 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) {
if (!q->wrapped) {
/* No more channels, start over */
q->rrpos = 0;
} else {
/* Prioritize next entry */
q->rrpos++;
q->wrapped = 0;
}
if (pos < q->rrpos) {
tmp->metric = 1000 + pos;
} else {
if (pos > q->rrpos) {
/* Indicate there is another priority */
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;
static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url)
{
struct member *cur;
struct localuser *outgoing=NULL, *tmp = NULL;
int to;
int allowredir_in=0;
int allowredir_out=0;
int allowdisconnect=0;
char restofit[AST_MAX_EXTENSION];
char *newnum;
struct ast_channel *peer;
struct localuser *lpeer;
int x=0;
char digit = 0;
/* Hold the lock while we setup the outgoing calls */
ast_mutex_lock(&qe->parent->lock);
if (strlen(qe->announce))
announce = qe->announce;
if (announceoverride && strlen(announceoverride))
announce = announceoverride;
while(cur) {
/* Get a technology/[device:]number pair */
tmp = malloc(sizeof(struct localuser));
if (!tmp) {
ast_log(LOG_WARNING, "Out of memory\n");
goto out;
}
memset(tmp, 0, sizeof(struct localuser));
tmp->allowredirect_in = 1;
if (strchr(options, 'T'))
tmp->allowredirect_out = 1;
if (strchr(options, 'r'))
tmp->ringbackonly = 1;
if (strchr(options, 'm'))
tmp->musiconhold = 1;
if (strchr(options, 'd'))
tmp->dataquality = 1;
if (strchr(options, 'H'))
tmp->allowdisconnect = 1;
}
if (url) {
ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
} else
ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
tmp->member = cur; /* Never directly dereference! Could change on reload */
strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
/* If we're dialing by extension, look at the extension to know what to dial */
if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
calc_metric(qe->parent, cur, x++, qe, tmp);
/* Put them in the list of outgoing thingies... We're ready now.
XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */
tmp->next = outgoing;
outgoing = tmp;
/* If this line is up, don't try anybody else */
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break;
cur = cur->next;
}
if (qe->parent->timeout)
to = qe->parent->timeout * 1000;
else
to = -1;
ring_one(qe, outgoing);
ast_mutex_unlock(&qe->parent->lock);
lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
if (lpeer)
peer = lpeer->chan;
else
peer = NULL;
if (to) {
} else {
if (digit && valid_exit(qe, digit))
res=digit;
else
/* Nobody answered, next please? */
res=0;
}
goto out;
}
if (peer) {
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with -1 so that it is hung up properly after the
conversation. */
if (!strcmp(qe->chan->type,"Zap")) {
if (tmp->dataquality) zapx = 0;
ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
}
if (!strcmp(peer->type,"Zap")) {
if (tmp->dataquality) zapx = 0;
ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
}
/* Update parameters for the queue */
update_queue(qe->parent, lpeer);
hanguptree(outgoing, peer);
/* Stop music on hold */
ast_moh_stop(qe->chan);
outgoing = NULL;
res2 = ast_autoservice_start(qe->chan);
if (!res2)
res2 = ast_streamfile(peer, announce, peer->language);
/* XXX Need a function to wait on *both* streams XXX */
res2 |= ast_autoservice_stop(qe->chan);
if (res2) {
/* 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_hangup(peer);
return -1;
}
}
/* 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_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
ast_hangup(peer);
return -1;
}
/* Drop out of the queue at this point, to prepare for next caller */
leave_queue(qe);
/* JDG: sendurl */
if( url && strlen(url) && ast_channel_supports_html(peer) ) {
ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
ast_channel_sendurl( peer, url );
} /* /JDG */
bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
if(bridge != AST_PBX_NO_HANGUP_PEER)
ast_hangup(peer);
if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
else res = bridge; /* bridge error, stay in the queue */
}
out:
hanguptree(outgoing, NULL);
return res;
}
static int wait_a_bit(struct queue_ent *qe)
{
int retrywait;
/* Hold the lock while we setup the outgoing calls */
ast_mutex_lock(&qe->parent->lock);
ast_mutex_unlock(&qe->parent->lock);
return ast_waitfordigit(qe->chan, retrywait);
}
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
// [PHM 06/26/03]
static struct member * interface_exists( struct ast_call_queue * q, char * interface )
{
struct member * ret = NULL ;
struct member *mem;
char buf[500] ;
if( q != NULL )
{
mem = q->members ;
while( mem != NULL ) {
sprintf( buf, "%s/%s", mem->tech, mem->loc);
if( strcmp( buf, interface ) == 0 ) {
ret = mem ;
break ;
}
else
mem = mem->next ;
}
}
return( ret ) ;
}
static struct member * create_queue_node( char * interface )
{
struct member * cur ;
char * tmp ;
/* Add a new member */
cur = malloc(sizeof(struct member));
if (cur) {
memset(cur, 0, sizeof(struct member));
strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
if ((tmp = strchr(cur->tech, '/')))
*tmp = '\0';
if ((tmp = strchr(interface, '/'))) {
tmp++;
strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
} else
ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
}
return( cur ) ;
}
static int rqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
struct member * node ;
struct member * look ;
char info[512];
char *interface=NULL;
struct ast_call_queue *q;
int found=0 ;
if (!data) {
ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
return -1;
}
LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
/* Parse our arguments XXX Check for failure XXX */
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
queuename = info;
if (queuename) {
interface = strchr(queuename, '|');
if (interface) {
*interface = '\0';
interface++;
}
else
interface = chan->name ;
}
if( ( q = queues) != NULL )
{
while( q && ( res != 0 ) && (!found) )
{
ast_mutex_lock(&q->lock);
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
if( strcmp( q->name, queuename) == 0 )
{
// found queue, try to remove interface
found=1 ;
if( ( node = interface_exists( q, interface ) ) != NULL )
{
if( ( look = q->members ) == node )
{
// 1st
q->members = node->next;
}
else
{
while( look != NULL )
if( look->next == node )
{
look->next = node->next ;
break ;
}
else
look = look->next ;
}
free( node ) ;
ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
interface, queuename);
res = 0 ;
}
else
ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
"Not there\n", interface, queuename);
}
ast_mutex_unlock(&q->lock);