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 * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
12 * These features added by David C. Troy <dave@toad.net>:
13 * - Per-queue holdtime calculation
14 * - Estimated holdtime announcement
15 * - Position announcement
16 * - Abandoned/completed call counters
17 * - Failout timer passed as optional app parameter
18 * - Optional monitoring of calls, started when call is answered
20 * Patch Version 1.07 2003-12-24 01
22 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
23 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
25 * Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
26 * by Matthew Enger <m.enger@xi.com.au>
28 * This program is free software, distributed under the terms of
29 * the GNU General Public License
32 #include <asterisk/lock.h>
33 #include <asterisk/file.h>
34 #include <asterisk/logger.h>
35 #include <asterisk/channel.h>
36 #include <asterisk/pbx.h>
37 #include <asterisk/options.h>
38 #include <asterisk/module.h>
39 #include <asterisk/translate.h>
40 #include <asterisk/say.h>
41 #include <asterisk/features.h>
42 #include <asterisk/musiconhold.h>
43 #include <asterisk/cli.h>
44 #include <asterisk/manager.h>
45 #include <asterisk/config.h>
46 #include <asterisk/monitor.h>
47 #include <asterisk/utils.h>
55 #include <sys/signal.h>
56 #include <netinet/in.h>
58 #include "../astconf.h"
60 #define QUEUE_STRATEGY_RINGALL 0
61 #define QUEUE_STRATEGY_ROUNDROBIN 1
62 #define QUEUE_STRATEGY_LEASTRECENT 2
63 #define QUEUE_STRATEGY_FEWESTCALLS 3
64 #define QUEUE_STRATEGY_RANDOM 4
65 #define QUEUE_STRATEGY_RRMEMORY 5
67 static struct strategy {
71 { QUEUE_STRATEGY_RINGALL, "ringall" },
72 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
73 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
74 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
75 { QUEUE_STRATEGY_RANDOM, "random" },
76 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
79 #define DEFAULT_RETRY 5
80 #define DEFAULT_TIMEOUT 15
81 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
83 #define RES_OKAY 0 /* Action completed */
84 #define RES_EXISTS (-1) /* Entry already exists */
85 #define RES_OUTOFMEMORY (-2) /* Out of memory */
86 #define RES_NOSUCHQUEUE (-3) /* No such queue */
88 static char *tdesc = "True Call Queueing";
90 static char *app = "Queue";
92 static char *synopsis = "Queue a call for a call queue";
94 static char *descrip =
95 " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
96 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
97 " This application returns -1 if the originating channel hangs up, or if the\n"
98 "call is bridged and either of the parties in the bridge terminate the call.\n"
99 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
100 "The option string may contain zero or more of the following characters:\n"
101 " 't' -- allow the called user transfer the calling user\n"
102 " 'T' -- to allow the calling user to transfer the call.\n"
103 " 'd' -- data-quality (modem) call (minimum delay).\n"
104 " 'h' -- allow callee to hang up by hitting *.\n"
105 " 'H' -- allow caller to hang up by hitting *.\n"
106 " 'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
107 " 'r' -- ring instead of playing MOH\n"
108 " In addition to transferring the call, a call may be parked and then picked\n"
109 "up by another user.\n"
110 " The optional URL will be sent to the called party if the channel supports\n"
112 " The timeout will cause the queue to fail out after a specified number of\n"
113 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
116 static char *app_aqm = "AddQueueMember" ;
117 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
118 static char *app_aqm_descrip =
119 " AddQueueMember(queuename[|interface[|penalty]]):\n"
120 "Dynamically adds interface to an existing queue.\n"
121 "If the interface is already in the queue and there exists an n+101 priority\n"
122 "then it will then jump to this priority. Otherwise it will return an error\n"
123 "Returns -1 if there is an error.\n"
124 "Example: AddQueueMember(techsupport|SIP/3000)\n"
127 static char *app_rqm = "RemoveQueueMember" ;
128 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
129 static char *app_rqm_descrip =
130 " RemoveQueueMember(queuename[|interface]):\n"
131 "Dynamically removes interface to an existing queue\n"
132 "If the interface is NOT in the queue and there exists an n+101 priority\n"
133 "then it will then jump to this priority. Otherwise it will return an error\n"
134 "Returns -1 if there is an error.\n"
135 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
138 /* We define a customer "local user" structure because we
139 use it not only for keeping track of what is in use but
140 also for keeping track of who we're dialing. */
143 struct ast_channel *chan;
148 int allowredirect_in;
149 int allowredirect_out;
153 int allowdisconnect_in;
154 int allowdisconnect_out;
156 struct member *member;
157 struct localuser *next;
163 struct ast_call_queue *parent; /* What queue is our parent */
164 char moh[80]; /* Name of musiconhold to be used */
165 char announce[80]; /* Announcement to play for member when call is answered */
166 char context[80]; /* Context when user exits queue */
167 int pos; /* Where we are in the queue */
168 int prio; /* Our priority */
169 int last_pos_said; /* Last position we told the user */
170 time_t last_pos; /* Last time we told the user their position */
171 int opos; /* Where we started in the queue */
172 int handled; /* Whether our call was handled */
173 time_t start; /* When we started holding */
174 int queuetimeout; /* How many seconds before timing out of queue */
175 struct ast_channel *chan; /* Our channel */
176 struct queue_ent *next; /* The next queue entry */
180 char tech[80]; /* Technology */
181 char loc[256]; /* Location */
182 int penalty; /* Are we a last resort? */
183 int calls; /* Number of calls serviced by this member */
184 int dynamic; /* Are we dynamically added? */
185 time_t lastcall; /* When last successful call was hungup */
186 struct member *next; /* Next member */
189 struct ast_call_queue {
191 char name[80]; /* Name of the queue */
192 char moh[80]; /* Name of musiconhold to be used */
193 char announce[80]; /* Announcement to play when call is answered */
194 char context[80]; /* Context for this queue */
195 int strategy; /* Queueing strategy */
196 int announcefrequency; /* How often to announce their position */
197 int roundingseconds; /* How many seconds do we round to? */
198 int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
199 int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */
200 int callscompleted; /* Number of queue calls completed */
201 int callsabandoned; /* Number of queue calls abandoned */
202 int servicelevel; /* seconds setting for servicelevel*/
203 int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/
204 char monfmt[8]; /* Format to use when recording calls */
205 int monjoin; /* Should we join the two files when we are done with the call */
206 char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
207 char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */
208 char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
209 char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
210 char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */
211 char sound_lessthan[80]; /* Sound file: "less-than" (def. queue-lessthan) */
212 char sound_seconds[80]; /* Sound file: "seconds." (def. queue-seconds) */
213 char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
215 int count; /* How many entries are in the queue */
216 int maxlen; /* Max number of entries in queue */
217 int wrapuptime; /* Wrapup Time */
219 int dead; /* Whether this queue is dead or not */
220 int retry; /* Retry calling everyone after this amount of time */
221 int timeout; /* How long to wait for an answer */
223 /* Queue strategy things */
225 int rrpos; /* Round Robin - position */
226 int wrapped; /* Round Robin - wrapped around? */
227 int joinempty; /* Do we care if the queue has no members? */
228 int eventwhencalled; /* Generate an event when the agent is called (before pickup) */
229 int leavewhenempty; /* If all agents leave the queue, remove callers from the queue */
231 struct member *members; /* Member channels to be tried */
232 struct queue_ent *head; /* Start of the actual queue */
233 struct ast_call_queue *next; /* Next call queue */
236 static struct ast_call_queue *queues = NULL;
237 AST_MUTEX_DEFINE_STATIC(qlock);
239 static char *int2strat(int strategy)
242 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
243 if (strategy == strategies[x].strategy)
244 return strategies[x].name;
249 static int strat2int(char *strategy)
252 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
253 if (!strcasecmp(strategy, strategies[x].name))
254 return strategies[x].strategy;
259 /* Insert the 'new' entry after the 'prev' entry of queue 'q' */
260 static inline void insert_entry(struct ast_call_queue *q,
261 struct queue_ent *prev, struct queue_ent *new, int *pos)
263 struct queue_ent *cur;
280 static int join_queue(char *queuename, struct queue_ent *qe)
282 struct ast_call_queue *q;
283 struct queue_ent *cur, *prev = NULL;
288 ast_mutex_lock(&qlock);
289 for (q = queues; q; q = q->next) {
290 if (!strcasecmp(q->name, queuename)) {
291 /* This is our one */
292 ast_mutex_lock(&q->lock);
293 if ((q->members || q->joinempty) && (!q->maxlen || (q->count < q->maxlen))) {
294 /* There's space for us, put us at the right position inside
296 * Take into account the priority of the calling user */
301 /* We have higher priority than the current user, enter
302 * before him, after all the other users with priority
303 * higher or equal to our priority. */
304 if ((!inserted) && (qe->prio > cur->prio)) {
305 insert_entry(q, prev, qe, &pos);
312 /* No luck, join at the end of the queue */
314 insert_entry(q, prev, qe, &pos);
315 strncpy(qe->moh, q->moh, sizeof(qe->moh) - 1);
316 strncpy(qe->announce, q->announce, sizeof(qe->announce) - 1);
317 strncpy(qe->context, q->context, sizeof(qe->context) - 1);
320 manager_event(EVENT_FLAG_CALL, "Join",
321 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
322 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count );
324 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
327 ast_mutex_unlock(&q->lock);
331 ast_mutex_unlock(&qlock);
335 static void free_members(struct ast_call_queue *q, int all)
337 /* Free non-dynamic members */
338 struct member *curm, *next, *prev;
343 if (all || !curm->dynamic) {
355 static void destroy_queue(struct ast_call_queue *q)
357 struct ast_call_queue *cur, *prev = NULL;
358 ast_mutex_lock(&qlock);
359 for (cur = queues; cur; cur = cur->next) {
362 prev->next = cur->next;
369 ast_mutex_unlock(&qlock);
371 ast_mutex_destroy(&q->lock);
375 static int play_file(struct ast_channel *chan, char *filename)
379 ast_stopstream(chan);
380 res = ast_streamfile(chan, filename, chan->language);
383 res = ast_waitstream(chan, "");
388 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
391 ast_stopstream(chan);
396 static int say_position(struct queue_ent *qe)
398 int res = 0, avgholdmins, avgholdsecs;
401 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
403 if ( (now - qe->last_pos) < 15 )
406 /* If either our position has changed, or we are over the freq timer, say position */
407 if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
410 ast_moh_stop(qe->chan);
411 /* Say we're next, if we are */
413 res += play_file(qe->chan, qe->parent->sound_next);
416 res += play_file(qe->chan, qe->parent->sound_thereare);
417 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
418 res += play_file(qe->chan, qe->parent->sound_calls);
420 /* Round hold time to nearest minute */
421 avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
423 /* If they have specified a rounding then round the seconds as well */
424 if(qe->parent->roundingseconds) {
425 avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
426 avgholdsecs*= qe->parent->roundingseconds;
431 if (option_verbose > 2)
432 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
434 /* If the hold time is >1 min, if it's enabled, and if it's not
435 supposed to be only once and we have already said it, say it */
436 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
437 res += play_file(qe->chan, qe->parent->sound_holdtime);
439 if (avgholdmins < 2) {
440 res += play_file(qe->chan, qe->parent->sound_lessthan);
441 res += ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
443 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
444 res += play_file(qe->chan, qe->parent->sound_minutes);
447 res += ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
448 res += play_file(qe->chan, qe->parent->sound_seconds);
454 /* Set our last_pos indicators */
456 qe->last_pos_said = qe->pos;
458 if (option_verbose > 2)
459 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
460 res += play_file(qe->chan, qe->parent->sound_thanks);
461 ast_moh_start(qe->chan, qe->moh);
466 static void record_abandoned(struct queue_ent *qe)
468 ast_mutex_lock(&qe->parent->lock);
469 qe->parent->callsabandoned++;
470 ast_mutex_unlock(&qe->parent->lock);
473 static void recalc_holdtime(struct queue_ent *qe)
475 int oldvalue, newvalue;
477 /* Calculate holdtime using a recursive boxcar filter */
478 /* Thanks to SRT for this contribution */
479 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
481 newvalue = time(NULL) - qe->start;
483 ast_mutex_lock(&qe->parent->lock);
484 if (newvalue <= qe->parent->servicelevel)
485 qe->parent->callscompletedinsl++;
486 oldvalue = qe->parent->holdtime;
487 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
488 ast_mutex_unlock(&qe->parent->lock);
492 static void leave_queue(struct queue_ent *qe)
494 struct ast_call_queue *q;
495 struct queue_ent *cur, *prev = NULL;
500 ast_mutex_lock(&q->lock);
508 /* Take us out of the queue */
509 manager_event(EVENT_FLAG_CALL, "Leave",
510 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
511 qe->chan->name, q->name, q->count);
513 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
515 /* Take us out of the queue */
517 prev->next = cur->next;
521 /* Renumber the people after us in the queue based on a new count */
527 ast_mutex_unlock(&q->lock);
528 if (q->dead && !q->count) {
529 /* It's dead and nobody is in it, so kill it */
534 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
536 /* Hang up a tree of stuff */
537 struct localuser *oo;
539 /* Hangup any existing lines we have open */
540 if (outgoing->chan && (outgoing->chan != exception))
541 ast_hangup(outgoing->chan);
543 outgoing=outgoing->next;
548 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
551 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
552 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s/%s\n", tmp->tech, tmp->numsubst);
554 ast_cdr_busy(qe->chan->cdr);
558 /* Request the peer */
559 tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
560 if (!tmp->chan) { /* If we can't, just go on to the next call */
562 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
565 ast_cdr_busy(qe->chan->cdr);
569 tmp->chan->appl = "AppQueue";
570 tmp->chan->data = "(Outgoing Line)";
571 tmp->chan->whentohangup = 0;
572 if (tmp->chan->callerid)
573 free(tmp->chan->callerid);
575 free(tmp->chan->ani);
576 if (qe->chan->callerid)
577 tmp->chan->callerid = strdup(qe->chan->callerid);
579 tmp->chan->callerid = NULL;
581 tmp->chan->ani = strdup(qe->chan->ani);
583 tmp->chan->ani = NULL;
584 /* Presense of ADSI CPE on outgoing channel follows ours */
585 tmp->chan->adsicpe = qe->chan->adsicpe;
586 /* Place the call, but don't wait on the answer */
587 res = ast_call(tmp->chan, tmp->numsubst, 0);
589 /* Again, keep going even if there's an error */
591 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
592 else if (option_verbose > 2)
593 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
594 ast_hangup(tmp->chan);
599 if (qe->parent->eventwhencalled) {
600 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
601 "AgentCalled: %s/%s\r\n"
602 "ChannelCalling: %s\r\n"
607 tmp->tech, tmp->numsubst, qe->chan->name,
608 tmp->chan->callerid ? tmp->chan->callerid : "unknown <>",
609 qe->chan->context, qe->chan->exten, qe->chan->priority);
611 if (option_verbose > 2)
612 ast_verbose(VERBOSE_PREFIX_3 "Called %s/%s\n", tmp->tech, tmp->numsubst);
617 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
619 struct localuser *cur;
620 struct localuser *best;
626 if (cur->stillgoing && /* Not already done */
627 !cur->chan && /* Isn't already going */
628 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
629 bestmetric = cur->metric;
635 if (!qe->parent->strategy) {
636 /* Ring everyone who shares this best metric (for ringall) */
639 if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
640 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
646 /* Ring just the best channel */
648 ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n",
649 best->tech, best->numsubst, best->metric);
650 ring_entry(qe, best);
653 } while (best && !best->chan);
656 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
662 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
664 struct localuser *cur;
665 struct localuser *best;
670 if (cur->stillgoing && /* Not already done */
671 !cur->chan && /* Isn't already going */
672 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
673 bestmetric = cur->metric;
679 /* Ring just the best channel */
680 ast_log(LOG_DEBUG, "Next is '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
681 qe->parent->rrpos = best->metric % 1000;
683 /* Just increment rrpos */
684 if (!qe->parent->wrapped) {
685 /* No more channels, start over */
686 qe->parent->rrpos = 0;
688 /* Prioritize next entry */
692 qe->parent->wrapped = 0;
696 static int valid_exit(struct queue_ent *qe, char digit)
699 if (ast_strlen_zero(qe->context))
703 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
704 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
705 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
706 qe->chan->priority = 0;
712 #define AST_MAX_WATCHERS 256
714 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect_in, int *allowdisconnect_out, char *digit)
716 char *queue = qe->parent->name;
724 struct localuser *peer = NULL;
725 struct ast_channel *watchers[AST_MAX_WATCHERS];
727 struct ast_channel *winner;
728 struct ast_channel *in = qe->chan;
730 while(*to && !peer) {
737 /* Keep track of important channels */
738 if (o->stillgoing && o->chan) {
739 watchers[pos++] = o->chan;
746 if (numlines == numbusies) {
747 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
749 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
754 winner = ast_waitfor_n(watchers, pos, to);
757 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
759 if (option_verbose > 2)
760 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
762 *allowredir_in = o->allowredirect_in;
763 *allowredir_out = o->allowredirect_out;
764 *allowdisconnect_in = o->allowdisconnect_in;
765 *allowdisconnect_out = o->allowdisconnect_out;
767 } else if (o->chan && (o->chan == winner)) {
768 f = ast_read(winner);
770 if (f->frametype == AST_FRAME_CONTROL) {
771 switch(f->subclass) {
772 case AST_CONTROL_ANSWER:
773 /* This is our guy if someone answered. */
775 if (option_verbose > 2)
776 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
778 *allowredir_in = o->allowredirect_in;
779 *allowredir_out = o->allowredirect_out;
780 *allowdisconnect_in = o->allowdisconnect_out;
781 *allowdisconnect_out = o->allowdisconnect_out;
784 case AST_CONTROL_BUSY:
785 if (option_verbose > 2)
786 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
789 ast_cdr_busy(in->cdr);
792 if (qe->parent->strategy)
793 ring_one(qe, outgoing);
796 case AST_CONTROL_CONGESTION:
797 if (option_verbose > 2)
798 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
801 ast_cdr_busy(in->cdr);
804 if (qe->parent->strategy)
805 ring_one(qe, outgoing);
808 case AST_CONTROL_RINGING:
809 if (option_verbose > 2)
810 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
813 ast_indicate(in, AST_CONTROL_RINGING);
818 case AST_CONTROL_OFFHOOK:
819 /* Ignore going off hook */
822 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
830 if (qe->parent->strategy)
831 ring_one(qe, outgoing);
839 if (f && (f->frametype != AST_FRAME_VOICE))
840 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
841 else if (!f || (f->frametype != AST_FRAME_VOICE))
842 printf("Hangup received on %s\n", in->name);
844 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
849 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect_out && (f->subclass == '*')) {
850 if (option_verbose > 3)
851 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
855 if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
856 if (option_verbose > 3)
857 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
863 if (!*to && (option_verbose > 2))
864 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
871 static int is_our_turn(struct queue_ent *qe)
873 struct queue_ent *ch;
876 /* Atomically read the parent head -- does not need a lock */
877 ch = qe->parent->head;
878 /* If we are now at the top of the head, break out */
881 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
885 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
891 static int wait_our_turn(struct queue_ent *qe, int ringing)
893 struct queue_ent *ch;
897 /* This is the holding pen for callers 2 through maxlen */
899 /* Atomically read the parent head -- does not need a lock */
900 ch = qe->parent->head;
902 /* If we are now at the top of the head, break out */
905 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
909 /* If we have timed out, break out */
910 if ( qe->queuetimeout ) {
912 if ( (now - qe->start) >= qe->queuetimeout )
916 /* leave the queue if no agents, if enabled */
917 if (!(qe->parent->members) && qe->parent->leavewhenempty) {
922 /* Make a position announcement, if enabled */
923 if (qe->parent->announcefrequency && !ringing)
926 /* Wait a second before checking again */
927 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
934 static int update_queue(struct ast_call_queue *q, struct member *member)
937 /* Since a reload could have taken place, we have to traverse the list to
938 be sure it's still valid */
939 ast_mutex_lock(&q->lock);
943 time(&cur->lastcall);
950 ast_mutex_unlock(&q->lock);
954 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
956 switch (q->strategy) {
957 case QUEUE_STRATEGY_RINGALL:
958 /* Everyone equal, except for penalty */
959 tmp->metric = mem->penalty * 1000000;
961 case QUEUE_STRATEGY_ROUNDROBIN:
964 /* No more channels, start over */
967 /* Prioritize next entry */
973 case QUEUE_STRATEGY_RRMEMORY:
974 if (pos < q->rrpos) {
975 tmp->metric = 1000 + pos;
977 if (pos > q->rrpos) {
978 /* Indicate there is another priority */
983 tmp->metric += mem->penalty * 1000000;
985 case QUEUE_STRATEGY_RANDOM:
986 tmp->metric = rand() % 1000;
987 tmp->metric += mem->penalty * 1000000;
989 case QUEUE_STRATEGY_FEWESTCALLS:
990 tmp->metric = mem->calls;
991 tmp->metric += mem->penalty * 1000000;
993 case QUEUE_STRATEGY_LEASTRECENT:
997 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
998 tmp->metric += mem->penalty * 1000000;
1001 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
1007 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
1010 struct localuser *outgoing=NULL, *tmp = NULL;
1012 int allowredir_in=0;
1013 int allowredir_out=0;
1014 int allowdisconnect_in=0;
1015 int allowdisconnect_out=0;
1016 char restofit[AST_MAX_EXTENSION];
1017 char oldexten[AST_MAX_EXTENSION]="";
1018 char oldcontext[AST_MAX_EXTENSION]="";
1019 char queuename[256]="";
1021 char *monitorfilename;
1022 struct ast_channel *peer;
1023 struct localuser *lpeer;
1024 struct member *member;
1025 int res = 0, bridge = 0;
1028 char *announce = NULL;
1032 struct ast_bridge_config config;
1033 /* Hold the lock while we setup the outgoing calls */
1034 ast_mutex_lock(&qe->parent->lock);
1036 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
1038 strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
1040 cur = qe->parent->members;
1041 if (!ast_strlen_zero(qe->announce))
1042 announce = qe->announce;
1043 if (announceoverride && !ast_strlen_zero(announceoverride))
1044 announce = announceoverride;
1046 /* Get a technology/[device:]number pair */
1047 tmp = malloc(sizeof(struct localuser));
1049 ast_mutex_unlock(&qe->parent->lock);
1050 ast_log(LOG_WARNING, "Out of memory\n");
1053 memset(tmp, 0, sizeof(struct localuser));
1054 tmp->stillgoing = -1;
1056 if (strchr(options, 't'))
1057 tmp->allowredirect_in = 1;
1058 if (strchr(options, 'T'))
1059 tmp->allowredirect_out = 1;
1060 if (strchr(options, 'r'))
1061 tmp->ringbackonly = 1;
1062 if (strchr(options, 'm'))
1063 tmp->musiconhold = 1;
1064 if (strchr(options, 'd'))
1065 tmp->dataquality = 1;
1066 if (strchr(options, 'h'))
1067 tmp->allowdisconnect_in = 1;
1068 if (strchr(options, 'H'))
1069 tmp->allowdisconnect_out = 1;
1070 if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout))
1075 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
1077 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
1080 tmp->member = cur; /* Never directly dereference! Could change on reload */
1081 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
1082 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
1083 tmp->lastcall = cur->lastcall;
1084 /* If we're dialing by extension, look at the extension to know what to dial */
1085 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
1086 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
1087 snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
1089 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
1091 /* Special case: If we ring everyone, go ahead and ring them, otherwise
1092 just calculate their metric for the appropriate strategy */
1093 calc_metric(qe->parent, cur, x++, qe, tmp);
1094 /* Put them in the list of outgoing thingies... We're ready now.
1095 XXX If we're forcibly removed, these outgoing calls won't get
1097 tmp->next = outgoing;
1099 /* If this line is up, don't try anybody else */
1100 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
1105 if (qe->parent->timeout)
1106 to = qe->parent->timeout * 1000;
1109 ring_one(qe, outgoing);
1110 ast_mutex_unlock(&qe->parent->lock);
1111 lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &digit);
1112 ast_mutex_lock(&qe->parent->lock);
1113 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
1114 store_next(qe, outgoing);
1116 ast_mutex_unlock(&qe->parent->lock);
1123 /* Musta gotten hung up */
1124 record_abandoned(qe);
1127 if (digit && valid_exit(qe, digit))
1130 /* Nobody answered, next please? */
1134 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
1138 /* Ah ha! Someone answered within the desired timeframe. Of course after this
1139 we will always return with -1 so that it is hung up properly after the
1142 if (!strcmp(qe->chan->type,"Zap")) {
1143 if (tmp->dataquality) zapx = 0;
1144 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1146 if (!strcmp(peer->type,"Zap")) {
1147 if (tmp->dataquality) zapx = 0;
1148 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1150 /* Update parameters for the queue */
1151 recalc_holdtime(qe);
1152 member = lpeer->member;
1153 hanguptree(outgoing, peer);
1157 res2 = ast_autoservice_start(qe->chan);
1159 res2 = ast_streamfile(peer, announce, peer->language);
1161 res2 = ast_waitstream(peer, "");
1163 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
1167 res2 |= ast_autoservice_stop(qe->chan);
1169 /* Agent must have hung up */
1170 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
1171 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
1176 /* Stop music on hold */
1177 ast_moh_stop(qe->chan);
1178 /* If appropriate, log that we have a destination channel */
1180 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
1181 /* Make sure channels are compatible */
1182 res = ast_channel_make_compatible(qe->chan, peer);
1184 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1185 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1189 /* Begin Monitoring */
1190 if (qe->parent->monfmt && *qe->parent->monfmt) {
1191 monitorfilename = pbx_builtin_getvar_helper( qe->chan, "MONITOR_FILENAME");
1192 if(monitorfilename) {
1193 ast_monitor_start( peer, qe->parent->monfmt, monitorfilename, 1 );
1195 ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1197 if(qe->parent->monjoin) {
1198 ast_monitor_setjoinfiles( peer, 1);
1201 /* Drop out of the queue at this point, to prepare for next caller */
1203 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
1205 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1206 ast_channel_sendurl( peer, url );
1208 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1209 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1210 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1213 memset(&config,0,sizeof(struct ast_bridge_config));
1214 config.allowredirect_in = allowredir_in;
1215 config.allowredirect_out = allowredir_out;
1216 config.allowdisconnect_in = allowdisconnect_in;
1217 config.allowdisconnect_out = allowdisconnect_out;
1218 bridge = ast_bridge_call(qe->chan,peer,&config);
1220 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1221 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1222 } else if (qe->chan->_softhangup) {
1223 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1225 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1228 if(bridge != AST_PBX_NO_HANGUP_PEER)
1230 update_queue(qe->parent, member);
1231 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1232 else res = bridge; /* bridge error, stay in the queue */
1235 hanguptree(outgoing, NULL);
1239 static int wait_a_bit(struct queue_ent *qe)
1241 /* Don't need to hold the lock while we setup the outgoing calls */
1242 int retrywait = qe->parent->retry * 1000;
1243 return ast_waitfordigit(qe->chan, retrywait);
1248 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
1250 struct member * ret = NULL ;
1258 while( mem != NULL ) {
1259 snprintf( buf, sizeof(buf), "%s/%s", mem->tech, mem->loc);
1261 if( strcmp( buf, interface ) == 0 ) {
1274 static struct member * create_queue_node( char * interface, int penalty )
1276 struct member * cur ;
1279 /* Add a new member */
1281 cur = malloc(sizeof(struct member));
1284 memset(cur, 0, sizeof(struct member));
1285 cur->penalty = penalty;
1286 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
1287 if ((tmp = strchr(cur->tech, '/')))
1289 if ((tmp = strchr(interface, '/'))) {
1291 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1293 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1299 static int remove_from_queue(char *queuename, char *interface)
1301 struct ast_call_queue *q;
1302 struct member *last_member, *look;
1303 int res = RES_NOSUCHQUEUE;
1305 ast_mutex_lock(&qlock);
1306 for (q = queues ; q ; q = q->next) {
1307 ast_mutex_lock(&q->lock);
1308 if (!strcmp(q->name, queuename)) {
1309 if ((last_member = interface_exists(q, interface))) {
1310 if ((look = q->members) == last_member) {
1311 q->members = last_member->next;
1313 while (look != NULL) {
1314 if (look->next == last_member) {
1315 look->next = last_member->next;
1327 ast_mutex_unlock(&q->lock);
1330 ast_mutex_unlock(&q->lock);
1332 ast_mutex_unlock(&qlock);
1336 static int add_to_queue(char *queuename, char *interface, int penalty)
1338 struct ast_call_queue *q;
1339 struct member *new_member;
1340 int res = RES_NOSUCHQUEUE;
1342 ast_mutex_lock(&qlock);
1343 for (q = queues ; q ; q = q->next) {
1344 ast_mutex_lock(&q->lock);
1345 if (!strcmp(q->name, queuename)) {
1346 if (interface_exists(q, interface) == NULL) {
1347 new_member = create_queue_node(interface, penalty);
1349 if (new_member != NULL) {
1350 new_member->dynamic = 1;
1351 new_member->next = q->members;
1352 q->members = new_member;
1355 res = RES_OUTOFMEMORY;
1360 ast_mutex_unlock(&q->lock);
1363 ast_mutex_unlock(&q->lock);
1365 ast_mutex_unlock(&qlock);
1369 static int rqm_exec(struct ast_channel *chan, void *data)
1372 struct localuser *u;
1373 char *info, *queuename;
1374 char tmpchan[256]="";
1375 char *interface = NULL;
1378 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface])\n");
1382 info = ast_strdupa((char *)data);
1384 ast_log(LOG_ERROR, "Out of memory\n");
1392 interface = strchr(queuename, '|');
1398 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1399 interface = strrchr(tmpchan, '-');
1402 interface = tmpchan;
1406 switch (remove_from_queue(queuename, interface)) {
1408 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename);
1412 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
1413 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) {
1414 chan->priority += 100;
1418 case RES_NOSUCHQUEUE:
1419 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1422 case RES_OUTOFMEMORY:
1423 ast_log(LOG_ERROR, "Out of memory\n");
1427 LOCAL_USER_REMOVE(u);
1431 static int aqm_exec(struct ast_channel *chan, void *data)
1434 struct localuser *u;
1437 char tmpchan[512]="";
1438 char *interface=NULL;
1439 char *penaltys=NULL;
1443 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\n");
1447 info = ast_strdupa((char *)data);
1449 ast_log(LOG_ERROR, "Out of memory\n");
1456 interface = strchr(queuename, '|');
1462 penaltys = strchr(interface, '|');
1468 if (!interface || ast_strlen_zero(interface)) {
1469 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1470 interface = strrchr(tmpchan, '-');
1473 interface = tmpchan;
1475 if (penaltys && strlen(penaltys)) {
1476 if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
1477 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
1483 switch (add_to_queue(queuename, interface, penalty)) {
1485 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1489 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
1490 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) {
1491 chan->priority += 100;
1495 case RES_NOSUCHQUEUE:
1496 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1499 case RES_OUTOFMEMORY:
1500 ast_log(LOG_ERROR, "Out of memory\n");
1504 LOCAL_USER_REMOVE(u);
1508 static int queue_exec(struct ast_channel *chan, void *data)
1512 struct localuser *u;
1515 char *options = NULL;
1517 char *announceoverride = NULL;
1518 char *user_priority;
1520 char *queuetimeoutstr = NULL;
1522 /* whether to exit Queue application after the timeout hits */
1525 /* Our queue entry */
1526 struct queue_ent qe;
1529 ast_log(LOG_WARNING, "Queue requires an argument (queuename[|[timeout][|URL]])\n");
1535 /* Setup our queue entry */
1536 memset(&qe, 0, sizeof(qe));
1538 /* Parse our arguments XXX Check for failure XXX */
1539 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1542 options = strchr(queuename, '|');
1546 url = strchr(options, '|');
1550 announceoverride = strchr(url, '|');
1551 if (announceoverride) {
1552 *announceoverride = '\0';
1554 queuetimeoutstr = strchr(announceoverride, '|');
1555 if (queuetimeoutstr) {
1556 *queuetimeoutstr = '\0';
1558 qe.queuetimeout = atoi(queuetimeoutstr);
1560 qe.queuetimeout = 0;
1567 /* Get the priority from the variable ${QUEUE_PRIO} */
1568 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
1569 if (user_priority) {
1570 if (sscanf(user_priority, "%d", &prio) == 1) {
1572 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
1575 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
1576 user_priority, chan->name);
1581 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
1586 if (strchr(options, 'r')) {
1591 // if (option_debug)
1592 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n",
1593 queuename, options, url, announceoverride, qe.queuetimeout, (int)prio);
1596 qe.start = time(NULL);
1597 qe.prio = (int)prio;
1598 qe.last_pos_said = 0;
1600 if (!join_queue(queuename, &qe)) {
1601 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
1602 /* Start music on hold */
1605 ast_indicate(chan, AST_CONTROL_RINGING);
1607 ast_moh_start(chan, qe.moh);
1610 /* This is the wait loop for callers 2 through maxlen */
1612 res = wait_our_turn(&qe, ringing);
1613 /* If they hungup, return immediately */
1615 /* Record this abandoned call */
1616 record_abandoned(&qe);
1617 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1618 if (option_verbose > 2) {
1619 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1626 if (valid_exit(&qe, res)) {
1627 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1633 /* This is the wait loop for the head caller*/
1634 /* To exit, they may get their call answered; */
1635 /* they may dial a digit from the queue context; */
1636 /* or, they may timeout. */
1638 /* Leave if we have exceeded our queuetimeout */
1639 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1644 /* leave the queue if no agents, if enabled */
1645 if (!((qe.parent)->members) && (qe.parent)->leavewhenempty) {
1650 /* Make a position announcement, if enabled */
1651 if (qe.parent->announcefrequency && !ringing)
1654 /* Try calling all queue members for 'timeout' seconds */
1655 res = try_calling(&qe, options, announceoverride, url, &go_on);
1659 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1661 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1665 /* Leave if we have exceeded our queuetimeout */
1666 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1671 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
1672 res = wait_a_bit(&qe);
1674 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1675 if (option_verbose > 2) {
1676 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1681 if (res && valid_exit(&qe, res)) {
1682 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1685 /* exit after 'timeout' cycle if 'n' option enabled */
1687 if (option_verbose > 2) {
1688 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
1691 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1695 /* Since this is a priority queue and
1696 * it is not sure that we are still at the head
1697 * of the queue, go and check for our turn again.
1699 if (!is_our_turn(&qe)) {
1700 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
1706 /* Don't allow return code > 0 */
1707 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1710 ast_indicate(chan, -1);
1714 ast_stopstream(chan);
1718 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1721 LOCAL_USER_REMOVE(u);
1725 static void reload_queues(void)
1727 struct ast_call_queue *q, *ql, *qn;
1728 struct ast_config *cfg;
1730 struct ast_variable *var;
1731 struct member *prev, *cur;
1733 cfg = ast_load("queues.conf");
1735 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1738 ast_mutex_lock(&qlock);
1739 /* Mark all queues as dead for the moment */
1745 /* Chug through config file */
1746 cat = ast_category_browse(cfg, NULL);
1748 if (strcasecmp(cat, "general")) {
1749 /* Look for an existing one */
1752 if (!strcmp(q->name, cat))
1758 q = malloc(sizeof(struct ast_call_queue));
1761 memset(q, 0, sizeof(struct ast_call_queue));
1762 ast_mutex_init(&q->lock);
1763 strncpy(q->name, cat, sizeof(q->name) - 1);
1770 ast_mutex_lock(&q->lock);
1771 /* Re-initialize the queue */
1776 q->announcefrequency = 0;
1777 q->announceholdtime = 0;
1778 q->roundingseconds = 0; /* Default - don't announce seconds */
1780 q->callscompleted = 0;
1781 q->callsabandoned = 0;
1782 q->callscompletedinsl = 0;
1783 q->servicelevel = 0;
1787 q->announce[0] = '\0';
1788 q->context[0] = '\0';
1789 q->monfmt[0] = '\0';
1790 strncpy(q->sound_next, "queue-youarenext", sizeof(q->sound_next) - 1);
1791 strncpy(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare) - 1);
1792 strncpy(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls) - 1);
1793 strncpy(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime) - 1);
1794 strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1);
1795 strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1);
1796 strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1);
1797 strncpy(q->sound_lessthan, "queue-lessthan", sizeof(q->sound_lessthan) - 1);
1800 /* find the end of any dynamic members */
1804 var = ast_variable_browse(cfg, cat);
1806 if (!strcasecmp(var->name, "member")) {
1807 /* Add a new member */
1808 cur = malloc(sizeof(struct member));
1810 memset(cur, 0, sizeof(struct member));
1811 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1812 if ((tmp = strchr(cur->tech, ','))) {
1815 cur->penalty = atoi(tmp);
1816 if (cur->penalty < 0)
1819 if ((tmp = strchr(cur->tech, '/')))
1821 if ((tmp = strchr(var->value, '/'))) {
1823 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1824 if ((tmp = strchr(cur->loc, ',')))
1827 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1834 } else if (!strcasecmp(var->name, "music")) {
1835 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1836 } else if (!strcasecmp(var->name, "announce")) {
1837 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1838 } else if (!strcasecmp(var->name, "context")) {
1839 strncpy(q->context, var->value, sizeof(q->context) - 1);
1840 } else if (!strcasecmp(var->name, "timeout")) {
1841 q->timeout = atoi(var->value);
1842 } else if (!strcasecmp(var->name, "monitor-join")) {
1843 q->monjoin = ast_true(var->value);
1844 } else if (!strcasecmp(var->name, "monitor-format")) {
1845 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
1846 } else if (!strcasecmp(var->name, "queue-youarenext")) {
1847 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
1848 } else if (!strcasecmp(var->name, "queue-thereare")) {
1849 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
1850 } else if (!strcasecmp(var->name, "queue-callswaiting")) {
1851 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
1852 } else if (!strcasecmp(var->name, "queue-holdtime")) {
1853 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
1854 } else if (!strcasecmp(var->name, "queue-minutes")) {
1855 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
1856 } else if (!strcasecmp(var->name, "queue-seconds")) {
1857 strncpy(q->sound_seconds, var->value, sizeof(q->sound_seconds) - 1);
1858 } else if (!strcasecmp(var->name, "queue-lessthan")) {
1859 strncpy(q->sound_lessthan, var->value, sizeof(q->sound_lessthan) - 1);
1860 } else if (!strcasecmp(var->name, "queue-thankyou")) {
1861 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
1862 } else if (!strcasecmp(var->name, "announce-frequency")) {
1863 q->announcefrequency = atoi(var->value);
1864 } else if (!strcasecmp(var->name, "announce-round-seconds")) {
1865 q->roundingseconds = atoi(var->value);
1866 if(q->roundingseconds>60 || q->roundingseconds<0) {
1867 ast_log(LOG_WARNING, "'%s' isn't a valid value for queue-rounding-seconds using 0 instead at line %d of queue.conf\n", var->value, var->lineno);
1868 q->roundingseconds=0;
1870 } else if (!strcasecmp(var->name, "announce-holdtime")) {
1871 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
1872 } else if (!strcasecmp(var->name, "retry")) {
1873 q->retry = atoi(var->value);
1874 } else if (!strcasecmp(var->name, "wrapuptime")) {
1875 q->wrapuptime = atoi(var->value);
1876 } else if (!strcasecmp(var->name, "maxlen")) {
1877 q->maxlen = atoi(var->value);
1878 } else if (!strcasecmp(var->name, "servicelevel")) {
1879 q->servicelevel= atoi(var->value);
1880 } else if (!strcasecmp(var->name, "strategy")) {
1881 q->strategy = strat2int(var->value);
1882 if (q->strategy < 0) {
1883 ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1886 } else if (!strcasecmp(var->name, "joinempty")) {
1887 q->joinempty = ast_true(var->value);
1888 } else if (!strcasecmp(var->name, "leavewhenempty")) {
1889 q->leavewhenempty = ast_true(var->value);
1890 } else if (!strcasecmp(var->name, "eventwhencalled")) {
1891 q->eventwhencalled = ast_true(var->value);
1893 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1898 q->retry = DEFAULT_RETRY;
1900 q->timeout = DEFAULT_TIMEOUT;
1904 ast_mutex_unlock(&q->lock);
1911 cat = ast_category_browse(cfg, cat);
1926 ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
1931 ast_mutex_unlock(&qlock);
1934 static int __queues_show(int fd, int argc, char **argv, int queue_show)
1936 struct ast_call_queue *q;
1937 struct queue_ent *qe;
1942 char calls[80] = "";
1946 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
1947 return RESULT_SHOWUSAGE;
1948 ast_mutex_lock(&qlock);
1951 ast_mutex_unlock(&qlock);
1953 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1955 ast_cli(fd, "No queues.\n");
1956 return RESULT_SUCCESS;
1959 ast_mutex_lock(&q->lock);
1961 if (strcasecmp(q->name, argv[2]) != 0) {
1962 ast_mutex_unlock(&q->lock);
1965 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1972 snprintf(max, sizeof(max), "%d", q->maxlen);
1974 strncpy(max, "unlimited", sizeof(max) - 1);
1976 if(q->callscompleted > 0)
1977 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1978 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
1979 q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
1981 ast_cli(fd, " Members: \n");
1982 for (mem = q->members; mem; mem = mem->next) {
1984 snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
1988 strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1);
1990 snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
1991 mem->calls, (long)(time(NULL) - mem->lastcall));
1993 strncpy(calls, " has taken no calls yet", sizeof(calls) - 1);
1994 ast_cli(fd, " %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
1997 ast_cli(fd, " No Members\n");
2000 ast_cli(fd, " Callers: \n");
2001 for (qe = q->head; qe; qe = qe->next)
2002 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name,
2003 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio);
2005 ast_cli(fd, " No Callers\n");
2007 ast_mutex_unlock(&q->lock);
2012 ast_mutex_unlock(&qlock);
2013 return RESULT_SUCCESS;
2016 static int queues_show(int fd, int argc, char **argv)
2018 return __queues_show(fd, argc, argv, 0);
2021 static int queue_show(int fd, int argc, char **argv)
2023 return __queues_show(fd, argc, argv, 1);
2026 static char *complete_queue(char *line, char *word, int pos, int state)
2028 struct ast_call_queue *q;
2031 ast_mutex_lock(&qlock);
2032 for (q = queues; q; q = q->next) {
2033 if (!strncasecmp(word, q->name, strlen(word))) {
2034 if (++which > state)
2038 ast_mutex_unlock(&qlock);
2039 return q ? strdup(q->name) : NULL;
2042 /* JDG: callback to display queues status in manager */
2043 static int manager_queues_show( struct mansession *s, struct message *m )
2045 char *a[] = { "show", "queues" };
2046 return queues_show( s->fd, 2, a );
2050 /* Dump queue status */
2051 static int manager_queues_status( struct mansession *s, struct message *m )
2055 char *id = astman_get_header(m,"ActionID");
2056 char idText[256] = "";
2057 struct ast_call_queue *q;
2058 struct queue_ent *qe;
2061 astman_send_ack(s, m, "Queue status will follow");
2063 ast_mutex_lock(&qlock);
2064 if (!ast_strlen_zero(id)) {
2065 snprintf(idText,256,"ActionID: %s\r\n",id);
2067 for (q = queues; q; q = q->next) {
2068 ast_mutex_lock(&q->lock);
2070 /* List queue properties */
2071 if(q->callscompleted > 0)
2072 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
2073 ast_mutex_lock(&s->lock);
2074 ast_cli(s->fd, "Event: QueueParams\r\n"
2081 "ServiceLevel: %d\r\n"
2082 "ServicelevelPerf: %2.1f\r\n"
2085 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
2086 q->callsabandoned, q->servicelevel, sl, idText);
2088 /* List Queue Members */
2089 for (mem = q->members; mem; mem = mem->next)
2090 ast_cli(s->fd, "Event: QueueMember\r\n"
2092 "Location: %s/%s\r\n"
2093 "Membership: %s\r\n"
2095 "CallsTaken: %d\r\n"
2099 q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
2100 mem->penalty, mem->calls, mem->lastcall, idText);
2102 /* List Queue Entries */
2105 for (qe = q->head; qe; qe = qe->next)
2106 ast_cli(s->fd, "Event: QueueEntry\r\n"
2114 q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
2115 ast_mutex_unlock(&s->lock);
2116 ast_mutex_unlock(&q->lock);
2118 ast_mutex_unlock(&qlock);
2119 return RESULT_SUCCESS;
2122 static int manager_add_queue_member(struct mansession *s, struct message *m)
2124 char *queuename, *interface, *penalty_s;
2127 queuename = astman_get_header(m, "Queue");
2128 interface = astman_get_header(m, "Interface");
2129 penalty_s = astman_get_header(m, "Penalty");
2131 if (ast_strlen_zero(queuename)) {
2132 astman_send_error(s, m, "'Queue' not specified.");
2136 if (ast_strlen_zero(interface)) {
2137 astman_send_error(s, m, "'Interface' not specified.");
2141 if (ast_strlen_zero(penalty_s))
2143 else if (sscanf(penalty_s, "%d", &penalty) != 1) {
2147 switch (add_to_queue(queuename, interface, penalty)) {
2149 astman_send_ack(s, m, "Added interface to queue");
2152 astman_send_error(s, m, "Unable to add interface: Already there");
2154 case RES_NOSUCHQUEUE:
2155 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
2157 case RES_OUTOFMEMORY:
2158 astman_send_error(s, m, "Out of memory");
2164 static int manager_remove_queue_member(struct mansession *s, struct message *m)
2166 char *queuename, *interface;
2168 queuename = astman_get_header(m, "Queue");
2169 interface = astman_get_header(m, "Interface");
2171 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
2172 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
2176 switch (remove_from_queue(queuename, interface)) {
2178 astman_send_ack(s, m, "Removed interface from queue");
2181 astman_send_error(s, m, "Unable to remove interface: Not there");
2183 case RES_NOSUCHQUEUE:
2184 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
2186 case RES_OUTOFMEMORY:
2187 astman_send_error(s, m, "Out of memory");
2193 static int handle_add_queue_member(int fd, int argc, char *argv[])
2195 char *queuename, *interface;
2198 if ((argc != 6) && (argc != 8)) {
2199 return RESULT_SHOWUSAGE;
2200 } else if (strcmp(argv[4], "to")) {
2201 return RESULT_SHOWUSAGE;
2202 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
2203 return RESULT_SHOWUSAGE;
2206 queuename = argv[5];
2207 interface = argv[3];
2209 if (sscanf(argv[7], "%d", &penalty) == 1) {
2211 ast_cli(fd, "Penalty must be >= 0\n");
2215 ast_cli(fd, "Penalty must be an integer >= 0\n");
2222 switch (add_to_queue(queuename, interface, penalty)) {
2224 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
2225 return RESULT_SUCCESS;
2227 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
2228 return RESULT_FAILURE;
2229 case RES_NOSUCHQUEUE:
2230 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
2231 return RESULT_FAILURE;
2232 case RES_OUTOFMEMORY:
2233 ast_cli(fd, "Out of memory\n");
2234 return RESULT_FAILURE;
2236 return RESULT_FAILURE;
2240 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
2242 /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
2245 /* Don't attempt to complete name of member (infinite possibilities) */
2249 return strdup("to");
2254 /* No need to duplicate code */
2255 return complete_queue(line, word, pos, state);
2258 return strdup("penalty");
2263 if (state < 100) { /* 0-99 */
2264 char *num = malloc(3);
2266 sprintf(num, "%d", state);
2277 static int handle_remove_queue_member(int fd, int argc, char *argv[])
2279 char *queuename, *interface;
2282 return RESULT_SHOWUSAGE;
2283 } else if (strcmp(argv[4], "from")) {
2284 return RESULT_SHOWUSAGE;
2287 queuename = argv[5];
2288 interface = argv[3];
2290 switch (remove_from_queue(queuename, interface)) {
2292 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
2293 return RESULT_SUCCESS;
2295 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
2296 return RESULT_FAILURE;
2297 case RES_NOSUCHQUEUE:
2298 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
2299 return RESULT_FAILURE;
2300 case RES_OUTOFMEMORY:
2301 ast_cli(fd, "Out of memory\n");
2302 return RESULT_FAILURE;
2304 return RESULT_FAILURE;
2308 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
2311 struct ast_call_queue *q;
2314 /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
2315 if ((pos > 5) || (pos < 3)) {
2320 return strdup("from");
2327 /* No need to duplicate code */
2328 return complete_queue(line, word, pos, state);
2331 if (queues != NULL) {
2332 for (q = queues ; q ; q = q->next) {
2333 ast_mutex_lock(&q->lock);
2334 for (m = q->members ; m ; m = m->next) {
2335 if (++which > state) {
2336 char *tmp = malloc(strlen(m->tech) + strlen(m->loc) + 2);
2338 sprintf(tmp, "%s/%s", m->tech, m->loc);
2340 ast_log(LOG_ERROR, "Out of memory\n");
2342 ast_mutex_unlock(&q->lock);
2346 ast_mutex_unlock(&q->lock);
2352 static char show_queues_usage[] =
2353 "Usage: show queues\n"
2354 " Provides summary information on call queues.\n";
2356 static struct ast_cli_entry cli_show_queues = {
2357 { "show", "queues", NULL }, queues_show,
2358 "Show status of queues", show_queues_usage, NULL };
2360 static char show_queue_usage[] =
2361 "Usage: show queue\n"
2362 " Provides summary information on a specified queue.\n";
2364 static struct ast_cli_entry cli_show_queue = {
2365 { "show", "queue", NULL }, queue_show,
2366 "Show status of a specified queue", show_queue_usage, complete_queue };
2368 static char aqm_cmd_usage[] =
2369 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
2371 static struct ast_cli_entry cli_add_queue_member = {
2372 { "add", "queue", "member", NULL }, handle_add_queue_member,
2373 "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
2375 static char rqm_cmd_usage[] =
2376 "Usage: remove queue member <channel> from <queue>\n";
2378 static struct ast_cli_entry cli_remove_queue_member = {
2379 { "remove", "queue", "member", NULL }, handle_remove_queue_member,
2380 "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
2382 int unload_module(void)
2384 STANDARD_HANGUP_LOCALUSERS;
2385 ast_cli_unregister(&cli_show_queue);
2386 ast_cli_unregister(&cli_show_queues);
2387 ast_cli_unregister(&cli_add_queue_member);
2388 ast_cli_unregister(&cli_remove_queue_member);
2389 ast_manager_unregister("Queues");
2390 ast_manager_unregister("QueueStatus");
2391 ast_manager_unregister("QueueAdd");
2392 ast_manager_unregister("QueueRemove");
2393 ast_unregister_application(app_aqm);
2394 ast_unregister_application(app_rqm);
2395 return ast_unregister_application(app);
2398 int load_module(void)
2401 res = ast_register_application(app, queue_exec, synopsis, descrip);
2403 ast_cli_register(&cli_show_queue);
2404 ast_cli_register(&cli_show_queues);
2405 ast_cli_register(&cli_add_queue_member);
2406 ast_cli_register(&cli_remove_queue_member);
2407 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
2408 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
2409 ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
2410 ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
2411 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
2412 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
2425 char *description(void)
2433 STANDARD_USECOUNT(res);
2439 return ASTERISK_GPL_KEY;