2 * Asterisk -- A telephony toolkit for Linux.
4 * True call queues with optional send URL on answer
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/parking.h>
24 #include <asterisk/musiconhold.h>
25 #include <asterisk/cli.h>
26 #include <asterisk/manager.h> /* JDG */
27 #include <asterisk/config.h>
35 #include <sys/signal.h>
36 #include <netinet/in.h>
38 #include "../astconf.h"
42 #define QUEUE_STRATEGY_RINGALL 0
43 #define QUEUE_STRATEGY_ROUNDROBIN 1
44 #define QUEUE_STRATEGY_LEASTRECENT 2
45 #define QUEUE_STRATEGY_FEWESTCALLS 3
46 #define QUEUE_STRATEGY_RANDOM 4
48 static struct strategy {
52 { QUEUE_STRATEGY_RINGALL, "ringall" },
53 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
54 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
55 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
56 { QUEUE_STRATEGY_RANDOM, "random" },
59 #define DEFAULT_RETRY 5
60 #define DEFAULT_TIMEOUT 15
61 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
63 static char *tdesc = "True Call Queueing";
65 static char *app = "Queue";
67 static char *synopsis = "Queue a call for a call queue";
69 static char *descrip =
70 " Queue(queuename[|options[|URL][|announceoverride]]):\n"
71 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
72 " This application returns -1 if the originating channel hangs up, or if the\n"
73 "call is bridged and either of the parties in the bridge terminate the call.\n"
74 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
75 "The option string may contain zero or more of the following characters:\n"
76 " 't' -- allow the called user transfer the calling user\n"
77 " 'T' -- to allow the calling user to transfer the call.\n"
78 " 'd' -- data-quality (modem) call (minimum delay).\n"
79 " 'H' -- allow caller to hang up by hitting *.\n"
80 " 'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
81 " In addition to transferring the call, a call may be parked and then picked\n"
82 "up by another user.\n"
83 " The optional URL will be sent to the called party if the channel supports\n"
87 static char *app_aqm = "AddQueueMember" ;
88 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
89 static char *app_aqm_descrip =
90 " AddQueueMember(queuename[|interface]):\n"
91 "Dynamically adds interface to an existing queue\n"
92 "Returns -1 if there is an error.\n"
93 "Example: AddQueueMember(techsupport|SIP/3000)\n"
96 static char *app_rqm = "RemoveQueueMember" ;
97 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
98 static char *app_rqm_descrip =
99 " RemoveQueueMember(queuename[|interface]):\n"
100 "Dynamically removes interface to an existing queue\n"
101 "Returns -1 if there is an error.\n"
102 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
105 /* We define a customer "local user" structure because we
106 use it not only for keeping track of what is in use but
107 also for keeping track of who we're dialing. */
110 struct ast_channel *chan;
115 int allowredirect_in;
116 int allowredirect_out;
121 struct member *member;
122 struct localuser *next;
128 struct ast_call_queue *parent; /* What queue is our parent */
129 char moh[80]; /* Name of musiconhold to be used */
130 char announce[80]; /* Announcement to play */
131 char context[80]; /* Context when user exits queue */
132 int pos; /* Where we are in the queue */
133 int opos; /* Where we started in the queue */
134 int handled; /* Whether our call was handled */
135 time_t start; /* When we started holding */
136 struct ast_channel *chan; /* Our channel */
137 struct queue_ent *next; /* The next queue entry */
141 char tech[80]; /* Technology */
142 char loc[256]; /* Location */
143 int penalty; /* Are we a last resort? */
145 int dynamic; /* Are we dynamically added? */
146 time_t lastcall; /* When last successful call was hungup */
147 struct member *next; /* Next member */
150 struct ast_call_queue {
152 char name[80]; /* Name of the queue */
153 char moh[80]; /* Name of musiconhold to be used */
154 char announce[80]; /* Announcement to play */
155 char context[80]; /* Announcement to play */
156 int strategy; /* Queueing strategy */
157 int announcetimeout; /* How often to announce their position */
158 int count; /* How many entries are in the queue */
159 int maxlen; /* Max number of entries in queue */
161 int dead; /* Whether this queue is dead or not */
162 int retry; /* Retry calling everyone after this amount of time */
163 int timeout; /* How long to wait for an answer */
165 /* Queue strategy things */
167 int rrpos; /* Round Robin - position */
168 int wrapped; /* Round Robin - wrapped around? */
170 struct member *members; /* Member channels to be tried */
171 struct queue_ent *head; /* Start of the actual queue */
172 struct ast_call_queue *next; /* Next call queue */
175 static struct ast_call_queue *queues = NULL;
176 static ast_mutex_t qlock = AST_MUTEX_INITIALIZER;
178 static char *int2strat(int strategy)
181 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
182 if (strategy == strategies[x].strategy)
183 return strategies[x].name;
188 static int strat2int(char *strategy)
191 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
192 if (!strcasecmp(strategy, strategies[x].name))
193 return strategies[x].strategy;
198 static int join_queue(char *queuename, struct queue_ent *qe)
200 struct ast_call_queue *q;
201 struct queue_ent *cur, *prev = NULL;
204 ast_mutex_lock(&qlock);
207 if (!strcasecmp(q->name, queuename)) {
208 /* This is our one */
209 ast_mutex_lock(&q->lock);
210 if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
211 /* There's space for us, put us at the end */
223 /* Fix additional pointers and
229 strncpy(qe->moh, q->moh, sizeof(qe->moh));
230 strncpy(qe->announce, q->announce, sizeof(qe->announce));
231 strncpy(qe->context, q->context, sizeof(qe->context));
234 manager_event(EVENT_FLAG_CALL, "Join",
235 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
236 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count );
238 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
241 ast_mutex_unlock(&q->lock);
246 ast_mutex_unlock(&qlock);
250 static void free_members(struct ast_call_queue *q, int all)
252 /* Free non-dynamic members */
253 struct member *curm, *next, *prev;
258 if (all || !curm->dynamic) {
270 static void destroy_queue(struct ast_call_queue *q)
272 struct ast_call_queue *cur, *prev = NULL;
273 ast_mutex_lock(&qlock);
278 prev->next = cur->next;
286 ast_mutex_unlock(&qlock);
291 static void leave_queue(struct queue_ent *qe)
293 struct ast_call_queue *q;
294 struct queue_ent *cur, *prev = NULL;
299 ast_mutex_lock(&q->lock);
307 /* Take us out of the queue */
308 manager_event(EVENT_FLAG_CALL, "Leave",
309 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
310 qe->chan->name, q->name, q->count);
312 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
314 /* Take us out of the queue */
316 prev->next = cur->next;
325 ast_mutex_unlock(&q->lock);
326 if (q->dead && !q->count) {
327 /* It's dead and nobody is in it, so kill it */
332 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
334 /* Hang up a tree of stuff */
335 struct localuser *oo;
337 /* Hangup any existing lines we have open */
338 if (outgoing->chan && (outgoing->chan != exception))
339 ast_hangup(outgoing->chan);
341 outgoing=outgoing->next;
346 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
349 /* Request the peer */
350 tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
351 if (!tmp->chan) { /* If we can't, just go on to the next call */
353 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
356 ast_cdr_busy(qe->chan->cdr);
360 tmp->chan->appl = "AppQueue";
361 tmp->chan->data = "(Outgoing Line)";
362 tmp->chan->whentohangup = 0;
363 if (tmp->chan->callerid)
364 free(tmp->chan->callerid);
366 free(tmp->chan->ani);
367 if (qe->chan->callerid)
368 tmp->chan->callerid = strdup(qe->chan->callerid);
370 tmp->chan->callerid = NULL;
372 tmp->chan->ani = strdup(qe->chan->ani);
374 tmp->chan->ani = NULL;
375 /* Presense of ADSI CPE on outgoing channel follows ours */
376 tmp->chan->adsicpe = qe->chan->adsicpe;
377 /* Place the call, but don't wait on the answer */
378 res = ast_call(tmp->chan, tmp->numsubst, 0);
380 /* Again, keep going even if there's an error */
382 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
383 else if (option_verbose > 2)
384 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
385 ast_hangup(tmp->chan);
390 if (option_verbose > 2)
391 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
395 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
397 struct localuser *cur;
398 struct localuser *best;
404 if (cur->stillgoing && /* Not already done */
405 !cur->chan && /* Isn't already going */
406 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
407 bestmetric = cur->metric;
413 if (!qe->parent->strategy) {
414 /* Ring everyone who shares this best metric (for ringall) */
417 if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
418 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
424 /* Ring just the best channel */
425 ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
426 ring_entry(qe, best);
429 } while (best && !best->chan);
431 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
437 static int valid_exit(struct queue_ent *qe, char digit)
440 if (!strlen(qe->context))
444 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
445 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
446 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
447 qe->chan->priority = 0;
455 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)
457 char *queue = qe->parent->name;
465 struct localuser *peer = NULL;
466 struct ast_channel *watchers[MAX];
468 struct ast_channel *winner;
469 struct ast_channel *in = qe->chan;
471 while(*to && !peer) {
478 /* Keep track of important channels */
479 if (o->stillgoing && o->chan) {
480 watchers[pos++] = o->chan;
487 if (numlines == numbusies) {
488 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
490 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
495 winner = ast_waitfor_n(watchers, pos, to);
498 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
500 if (option_verbose > 2)
501 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
503 *allowredir_in = o->allowredirect_in;
504 *allowredir_out = o->allowredirect_out;
505 *allowdisconnect = o->allowdisconnect;
507 } else if (o->chan && (o->chan == winner)) {
508 f = ast_read(winner);
510 if (f->frametype == AST_FRAME_CONTROL) {
511 switch(f->subclass) {
512 case AST_CONTROL_ANSWER:
513 /* This is our guy if someone answered. */
515 if (option_verbose > 2)
516 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
518 *allowredir_in = o->allowredirect_in;
519 *allowredir_out = o->allowredirect_out;
520 *allowdisconnect = o->allowdisconnect;
523 case AST_CONTROL_BUSY:
524 if (option_verbose > 2)
525 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
528 ast_cdr_busy(in->cdr);
531 if (qe->parent->strategy)
532 ring_one(qe, outgoing);
535 case AST_CONTROL_CONGESTION:
536 if (option_verbose > 2)
537 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
540 ast_cdr_busy(in->cdr);
543 if (qe->parent->strategy)
544 ring_one(qe, outgoing);
547 case AST_CONTROL_RINGING:
548 if (option_verbose > 2)
549 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
552 ast_indicate(in, AST_CONTROL_RINGING);
557 case AST_CONTROL_OFFHOOK:
558 /* Ignore going off hook */
561 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
569 if (qe->parent->strategy)
570 ring_one(qe, outgoing);
578 if (f && (f->frametype != AST_FRAME_VOICE))
579 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
580 else if (!f || (f->frametype != AST_FRAME_VOICE))
581 printf("Hangup received on %s\n", in->name);
583 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
588 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
589 if (option_verbose > 3)
590 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
594 if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
595 if (option_verbose > 3)
596 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
602 if (!*to && (option_verbose > 2))
603 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
610 static int wait_our_turn(struct queue_ent *qe)
612 struct queue_ent *ch;
615 /* Atomically read the parent head -- does not need a lock */
616 ch = qe->parent->head;
617 /* If we are now at the top of the head, break out */
618 if (qe->parent->head == qe)
620 /* Wait a second before checking again */
621 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
628 static int update_queue(struct ast_call_queue *q, struct localuser *user)
631 /* Since a reload could have taken place, we have to traverse the list to
632 be sure it's still valid */
633 ast_mutex_lock(&q->lock);
636 if (user->member == cur) {
637 time(&cur->lastcall);
643 ast_mutex_unlock(&q->lock);
647 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
649 switch (q->strategy) {
650 case QUEUE_STRATEGY_RINGALL:
651 /* Everyone equal, except for penalty */
652 tmp->metric = mem->penalty * 1000000;
654 case QUEUE_STRATEGY_ROUNDROBIN:
657 /* No more channels, start over */
660 /* Prioritize next entry */
665 if (pos < q->rrpos) {
666 tmp->metric = 1000 + pos;
668 if (pos > q->rrpos) {
669 /* Indicate there is another priority */
674 tmp->metric += mem->penalty * 1000000;
676 case QUEUE_STRATEGY_RANDOM:
677 tmp->metric = rand() % 1000;
678 tmp->metric += mem->penalty * 1000000;
680 case QUEUE_STRATEGY_FEWESTCALLS:
681 tmp->metric = mem->calls;
682 tmp->metric += mem->penalty * 1000000;
684 case QUEUE_STRATEGY_LEASTRECENT:
688 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
689 tmp->metric += mem->penalty * 1000000;
692 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
698 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
701 struct localuser *outgoing=NULL, *tmp = NULL;
704 int allowredir_out=0;
705 int allowdisconnect=0;
706 char restofit[AST_MAX_EXTENSION];
707 char oldexten[AST_MAX_EXTENSION]="";
708 char oldcontext[AST_MAX_EXTENSION]="";
709 char queuename[256]="";
711 struct ast_channel *peer;
712 struct localuser *lpeer;
713 int res = 0, bridge = 0;
716 char *announce = NULL;
719 /* Hold the lock while we setup the outgoing calls */
720 ast_mutex_lock(&qe->parent->lock);
721 strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
722 cur = qe->parent->members;
723 if (strlen(qe->announce))
724 announce = qe->announce;
725 if (announceoverride && strlen(announceoverride))
726 announce = announceoverride;
728 /* Get a technology/[device:]number pair */
729 tmp = malloc(sizeof(struct localuser));
731 ast_mutex_unlock(&qe->parent->lock);
732 ast_log(LOG_WARNING, "Out of memory\n");
735 memset(tmp, 0, sizeof(struct localuser));
736 tmp->stillgoing = -1;
738 if (strchr(options, 't'))
739 tmp->allowredirect_in = 1;
740 if (strchr(options, 'T'))
741 tmp->allowredirect_out = 1;
742 if (strchr(options, 'r'))
743 tmp->ringbackonly = 1;
744 if (strchr(options, 'm'))
745 tmp->musiconhold = 1;
746 if (strchr(options, 'd'))
747 tmp->dataquality = 1;
748 if (strchr(options, 'H'))
749 tmp->allowdisconnect = 1;
750 if (strchr(options, 'n'))
754 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
756 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
758 tmp->member = cur; /* Never directly dereference! Could change on reload */
759 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
760 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
761 /* If we're dialing by extension, look at the extension to know what to dial */
762 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
763 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
764 snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
766 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
768 /* Special case: If we ring everyone, go ahead and ring them, otherwise
769 just calculate their metric for the appropriate strategy */
770 calc_metric(qe->parent, cur, x++, qe, tmp);
771 /* Put them in the list of outgoing thingies... We're ready now.
772 XXX If we're forcibly removed, these outgoing calls won't get
774 tmp->next = outgoing;
776 /* If this line is up, don't try anybody else */
777 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
782 if (qe->parent->timeout)
783 to = qe->parent->timeout * 1000;
786 ring_one(qe, outgoing);
787 ast_mutex_unlock(&qe->parent->lock);
788 lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
795 /* Musta gotten hung up */
798 if (digit && valid_exit(qe, digit))
801 /* Nobody answered, next please? */
807 /* Ah ha! Someone answered within the desired timeframe. Of course after this
808 we will always return with -1 so that it is hung up properly after the
811 if (!strcmp(qe->chan->type,"Zap")) {
812 if (tmp->dataquality) zapx = 0;
813 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
815 if (!strcmp(peer->type,"Zap")) {
816 if (tmp->dataquality) zapx = 0;
817 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
819 /* Update parameters for the queue */
820 update_queue(qe->parent, lpeer);
821 hanguptree(outgoing, peer);
822 /* Stop music on hold */
823 ast_moh_stop(qe->chan);
827 res2 = ast_autoservice_start(qe->chan);
829 res2 = ast_streamfile(peer, announce, peer->language);
831 res2 = ast_waitstream(peer, "");
832 res2 |= ast_autoservice_stop(qe->chan);
834 /* Agent must have hung up */
835 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
836 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
841 /* If appropriate, log that we have a destination channel */
843 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
844 /* Make sure channels are compatible */
845 res = ast_channel_make_compatible(qe->chan, peer);
847 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
848 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
852 /* Drop out of the queue at this point, to prepare for next caller */
855 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
856 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
857 ast_channel_sendurl( peer, url );
859 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
860 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
861 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
863 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
864 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
865 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
866 } else if (qe->chan->_softhangup) {
867 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
869 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
872 if(bridge != AST_PBX_NO_HANGUP_PEER)
875 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
876 else res = bridge; /* bridge error, stay in the queue */
879 hanguptree(outgoing, NULL);
883 static int wait_a_bit(struct queue_ent *qe)
885 /* Don't need to hold the lock while we setup the outgoing calls */
886 int retrywait = qe->parent->retry * 1000;
887 return ast_waitfordigit(qe->chan, retrywait);
892 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
894 struct member * ret = NULL ;
902 while( mem != NULL ) {
903 sprintf( buf, "%s/%s", mem->tech, mem->loc);
905 if( strcmp( buf, interface ) == 0 ) {
918 static struct member * create_queue_node( char * interface )
920 struct member * cur ;
923 /* Add a new member */
925 cur = malloc(sizeof(struct member));
928 memset(cur, 0, sizeof(struct member));
929 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
930 if ((tmp = strchr(cur->tech, '/')))
932 if ((tmp = strchr(interface, '/'))) {
934 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
936 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
943 static int rqm_exec(struct ast_channel *chan, void *data)
948 struct member * node ;
949 struct member * look ;
951 char tmpchan[256]="";
952 char *interface=NULL;
953 struct ast_call_queue *q;
957 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
961 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
963 /* Parse our arguments XXX Check for failure XXX */
964 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
967 interface = strchr(queuename, '|');
973 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
974 interface = strrchr(tmpchan, '-');
981 if( ( q = queues) != NULL )
983 while( q && ( res != 0 ) && (!found) )
985 ast_mutex_lock(&q->lock);
986 if( strcmp( q->name, queuename) == 0 )
988 // found queue, try to remove interface
991 if( ( node = interface_exists( q, interface ) ) != NULL )
993 if( ( look = q->members ) == node )
996 q->members = node->next;
1000 while( look != NULL )
1001 if( look->next == node )
1003 look->next = node->next ;
1012 ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
1013 interface, queuename);
1017 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
1018 "Not there\n", interface, queuename);
1021 ast_mutex_unlock(&q->lock);
1027 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1029 LOCAL_USER_REMOVE(u);
1035 static int aqm_exec(struct ast_channel *chan, void *data)
1038 struct localuser *u;
1041 char tmpchan[512]="";
1042 char *interface=NULL;
1043 struct ast_call_queue *q;
1044 struct member *save;
1048 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
1052 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1054 /* Parse our arguments XXX Check for failure XXX */
1055 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1058 interface = strchr(queuename, '|');
1064 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1065 interface = strrchr(tmpchan, '-');
1068 interface = tmpchan;
1072 if( ( q = queues) != NULL )
1074 while( q && ( res != 0 ) && (!found) )
1076 ast_mutex_lock(&q->lock);
1077 if( strcmp( q->name, queuename) == 0 )
1079 // found queue, try to enable interface
1082 if( interface_exists( q, interface ) == NULL )
1085 q->members = create_queue_node( interface ) ;
1087 if( q->members != NULL ) {
1088 q->members->dynamic = 1;
1089 q->members->next = save ;
1093 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1097 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
1098 "Already there\n", interface, queuename);
1101 ast_mutex_unlock(&q->lock);
1107 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1109 LOCAL_USER_REMOVE(u);
1114 static int queue_exec(struct ast_channel *chan, void *data)
1117 struct localuser *u;
1120 char *options = NULL;
1122 char *announceoverride = NULL;
1123 /* whether to exit Queue application after the timeout hits */
1128 /* Our queue entry */
1129 struct queue_ent qe;
1132 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
1138 /* Parse our arguments XXX Check for failure XXX */
1139 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1142 options = strchr(queuename, '|');
1146 url = strchr(options, '|');
1150 announceoverride = strchr(url, '|');
1151 if (announceoverride) {
1152 *announceoverride = '\0';
1159 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s\n",
1160 queuename, options, url, announceoverride);
1161 /* Setup our queue entry */
1162 memset(&qe, 0, sizeof(qe));
1164 qe.start = time(NULL);
1165 if (!join_queue(queuename, &qe)) {
1166 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
1167 /* Start music on hold */
1168 ast_moh_start(chan, qe.moh);
1170 res = wait_our_turn(&qe);
1171 /* If they hungup, return immediately */
1173 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1174 if (option_verbose > 2) {
1175 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1182 if (valid_exit(&qe, res)) {
1183 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1189 res = try_calling(&qe, options, announceoverride, url, &go_on);
1193 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1195 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1198 res = wait_a_bit(&qe);
1200 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1201 if (option_verbose > 2) {
1202 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1207 if (res && valid_exit(&qe, res)) {
1208 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1211 /* exit after a timeout if 'n' option enabled */
1213 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1219 /* Don't allow return code > 0 */
1220 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1226 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1229 LOCAL_USER_REMOVE(u);
1233 static void reload_queues(void)
1235 struct ast_call_queue *q, *ql, *qn;
1236 struct ast_config *cfg;
1238 struct ast_variable *var;
1239 struct member *prev, *cur;
1241 cfg = ast_load("queues.conf");
1243 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1246 ast_mutex_lock(&qlock);
1247 /* Mark all queues as dead for the moment */
1253 /* Chug through config file */
1254 cat = ast_category_browse(cfg, NULL);
1256 if (strcasecmp(cat, "general")) {
1257 /* Look for an existing one */
1260 if (!strcmp(q->name, cat))
1266 q = malloc(sizeof(struct ast_call_queue));
1269 memset(q, 0, sizeof(struct ast_call_queue));
1270 ast_mutex_init(&q->lock);
1271 strncpy(q->name, cat, sizeof(q->name));
1278 ast_mutex_lock(&q->lock);
1279 /* Re-initialize the queue */
1286 strcpy(q->announce, "");
1287 strcpy(q->context, "");
1290 /* find the end of any dynamic members */
1294 var = ast_variable_browse(cfg, cat);
1296 if (!strcasecmp(var->name, "member")) {
1297 /* Add a new member */
1298 cur = malloc(sizeof(struct member));
1300 memset(cur, 0, sizeof(struct member));
1301 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1302 if ((tmp = strchr(cur->tech, ','))) {
1305 cur->penalty = atoi(tmp);
1306 if (cur->penalty < 0)
1309 if ((tmp = strchr(cur->tech, '/')))
1311 if ((tmp = strchr(var->value, '/'))) {
1313 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1314 if ((tmp = strchr(cur->loc, ',')))
1317 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1324 } else if (!strcasecmp(var->name, "music")) {
1325 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1326 } else if (!strcasecmp(var->name, "announce")) {
1327 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1328 } else if (!strcasecmp(var->name, "context")) {
1329 strncpy(q->context, var->value, sizeof(q->context) - 1);
1330 } else if (!strcasecmp(var->name, "timeout")) {
1331 q->timeout = atoi(var->value);
1332 } else if (!strcasecmp(var->name, "retry")) {
1333 q->retry = atoi(var->value);
1334 } else if (!strcasecmp(var->name, "maxlen")) {
1335 q->maxlen = atoi(var->value);
1336 } else if (!strcasecmp(var->name, "strategy")) {
1337 q->strategy = strat2int(var->value);
1338 if (q->strategy < 0) {
1339 ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1343 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1348 q->retry = DEFAULT_RETRY;
1350 q->timeout = DEFAULT_TIMEOUT;
1354 ast_mutex_unlock(&q->lock);
1361 cat = ast_category_browse(cfg, cat);
1376 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1381 ast_mutex_unlock(&qlock);
1384 static int __queues_show(int fd, int argc, char **argv, int queue_show)
1386 struct ast_call_queue *q;
1387 struct queue_ent *qe;
1394 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
1395 return RESULT_SHOWUSAGE;
1396 ast_mutex_lock(&qlock);
1399 ast_mutex_unlock(&qlock);
1401 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1403 ast_cli(fd, "No queues.\n");
1404 return RESULT_SUCCESS;
1407 ast_mutex_lock(&q->lock);
1409 if (strcasecmp(q->name, argv[2]) != 0) {
1410 ast_mutex_unlock(&q->lock);
1413 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1420 snprintf(max, sizeof(max), "%d", q->maxlen);
1422 strcpy(max, "unlimited");
1423 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy));
1425 ast_cli(fd, " Members: \n");
1426 for (mem = q->members; mem; mem = mem->next) {
1428 snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
1432 strcat(max, " (dynamic)");
1434 snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
1435 mem->calls, (long)(time(NULL) - mem->lastcall));
1437 strcpy(calls, " has taken no calls yet");
1438 ast_cli(fd, " %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
1441 ast_cli(fd, " No Members\n");
1444 ast_cli(fd, " Callers: \n");
1445 for (qe = q->head; qe; qe = qe->next)
1446 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name,
1447 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60);
1449 ast_cli(fd, " No Callers\n");
1451 ast_mutex_unlock(&q->lock);
1456 ast_mutex_unlock(&qlock);
1457 return RESULT_SUCCESS;
1460 static int queues_show(int fd, int argc, char **argv)
1462 return __queues_show(fd, argc, argv, 0);
1465 static int queue_show(int fd, int argc, char **argv)
1467 return __queues_show(fd, argc, argv, 1);
1470 static char *complete_queue(char *line, char *word, int pos, int state)
1472 struct ast_call_queue *q;
1475 ast_mutex_lock(&qlock);
1478 if (!strncasecmp(word, q->name, strlen(word))) {
1479 if (++which > state)
1484 ast_mutex_unlock(&qlock);
1485 return q ? strdup(q->name) : NULL;
1488 /* JDG: callback to display queues status in manager */
1489 static int manager_queues_show( struct mansession *s, struct message *m )
1491 char *a[] = { "show", "queues" };
1492 return queues_show( s->fd, 2, a );
1496 /* Dump queue status */
1497 static int manager_queues_status( struct mansession *s, struct message *m )
1501 char *id = astman_get_header(m,"ActionID");
1502 char idText[256] = "";
1503 struct ast_call_queue *q;
1504 struct queue_ent *qe;
1505 astman_send_ack(s, m, "Queue status will follow");
1507 ast_mutex_lock(&qlock);
1510 snprintf(idText,256,"ActionID: %s\r\n",id);
1513 ast_mutex_lock(&q->lock);
1514 ast_cli(s->fd, "Event: QueueParams\r\n"
1520 q->name, q->maxlen, q->count,idText);
1522 /* Do we care about queue members? */
1523 for (mem = q->members; mem; mem = mem->next)
1524 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1527 for (qe = q->head; qe; qe = qe->next)
1528 ast_cli(s->fd, "Event: QueueMember\r\n"
1536 q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
1537 ast_mutex_unlock(&q->lock);
1540 ast_mutex_unlock(&qlock);
1541 return RESULT_SUCCESS;
1544 static char show_queues_usage[] =
1545 "Usage: show queues\n"
1546 " Provides summary information on call queues.\n";
1548 static struct ast_cli_entry cli_show_queues = {
1549 { "show", "queues", NULL }, queues_show,
1550 "Show status of queues", show_queues_usage, NULL };
1552 static char show_queue_usage[] =
1553 "Usage: show queue\n"
1554 " Provides summary information on a specified queue.\n";
1556 static struct ast_cli_entry cli_show_queue = {
1557 { "show", "queue", NULL }, queue_show,
1558 "Show status of a specified queue", show_queue_usage, complete_queue };
1560 int unload_module(void)
1562 STANDARD_HANGUP_LOCALUSERS;
1563 ast_cli_unregister(&cli_show_queue);
1564 ast_cli_unregister(&cli_show_queues);
1565 ast_manager_unregister( "Queues" );
1566 ast_manager_unregister( "QueueStatus" );
1567 return ast_unregister_application(app);
1570 int load_module(void)
1573 res = ast_register_application(app, queue_exec, synopsis, descrip);
1575 ast_cli_register(&cli_show_queue);
1576 ast_cli_register(&cli_show_queues);
1577 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1578 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1581 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1582 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1595 char *description(void)
1603 STANDARD_USECOUNT(res);
1609 return ASTERISK_GPL_KEY;