Consider a comment to start with ;-- but not if there are more dashes (bug #2977)
[asterisk/asterisk.git] / apps / app_queue.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * True call queues with optional send URL on answer
5  * 
6  * Copyright (C) 1999-2004, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
11  *
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
19  *
20  * Patch Version 1.07 2003-12-24 01
21  *
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>
24  *
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>
27  *
28  * This program is free software, distributed under the terms of
29  * the GNU General Public License
30  */
31
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>
48 #include <asterisk/causes.h>
49 #include <stdlib.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <sys/time.h>
56 #include <sys/signal.h>
57 #include <netinet/in.h>
58
59 #include "../astconf.h"
60
61 #define QUEUE_STRATEGY_RINGALL          0
62 #define QUEUE_STRATEGY_ROUNDROBIN       1
63 #define QUEUE_STRATEGY_LEASTRECENT      2
64 #define QUEUE_STRATEGY_FEWESTCALLS      3
65 #define QUEUE_STRATEGY_RANDOM           4
66 #define QUEUE_STRATEGY_RRMEMORY         5
67
68 static struct strategy {
69         int strategy;
70         char *name;
71 } strategies[] = {
72         { QUEUE_STRATEGY_RINGALL, "ringall" },
73         { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
74         { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
75         { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
76         { QUEUE_STRATEGY_RANDOM, "random" },
77         { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
78 };
79
80 #define DEFAULT_RETRY           5
81 #define DEFAULT_TIMEOUT         15
82 #define RECHECK                         1               /* Recheck every second to see we we're at the top yet */
83
84 #define RES_OKAY        0                       /* Action completed */
85 #define RES_EXISTS      (-1)            /* Entry already exists */
86 #define RES_OUTOFMEMORY (-2)    /* Out of memory */
87 #define RES_NOSUCHQUEUE (-3)    /* No such queue */
88
89 static char *tdesc = "True Call Queueing";
90
91 static char *app = "Queue";
92
93 static char *synopsis = "Queue a call for a call queue";
94
95 static char *descrip =
96 "  Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
97 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
98 "  This application returns -1 if the originating channel hangs up, or if the\n"
99 "call is bridged and  either of the parties in the bridge terminate the call.\n"
100 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
101 "The option string may contain zero or more of the following characters:\n"
102 "      't' -- allow the called user transfer the calling user\n"
103 "      'T' -- to allow the calling user to transfer the call.\n"
104 "      'd' -- data-quality (modem) call (minimum delay).\n"
105 "      'h' -- allow callee to hang up by hitting *.\n"
106 "      'H' -- allow caller to hang up by hitting *.\n"
107 "      'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
108 "      'r' -- ring instead of playing MOH\n"
109 "  In addition to transferring the call, a call may be parked and then picked\n"
110 "up by another user.\n"
111 "  The optional URL will be sent to the called party if the channel supports\n"
112 "it.\n"
113 "  The timeout will cause the queue to fail out after a specified number of\n"
114 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
115
116 // [PHM 06/26/03]
117 static char *app_aqm = "AddQueueMember" ;
118 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
119 static char *app_aqm_descrip =
120 "   AddQueueMember(queuename[|interface[|penalty]]):\n"
121 "Dynamically adds interface to an existing queue.\n"
122 "If the interface is already in the queue and there exists an n+101 priority\n"
123 "then it will then jump to this priority.  Otherwise it will return an error\n"
124 "Returns -1 if there is an error.\n"
125 "Example: AddQueueMember(techsupport|SIP/3000)\n"
126 "";
127
128 static char *app_rqm = "RemoveQueueMember" ;
129 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
130 static char *app_rqm_descrip =
131 "   RemoveQueueMember(queuename[|interface]):\n"
132 "Dynamically removes interface to an existing queue\n"
133 "If the interface is NOT in the queue and there exists an n+101 priority\n"
134 "then it will then jump to this priority.  Otherwise it will return an error\n"
135 "Returns -1 if there is an error.\n"
136 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
137 "";
138
139 /* We define a customer "local user" structure because we
140    use it not only for keeping track of what is in use but
141    also for keeping track of who we're dialing. */
142
143 struct localuser {
144         struct ast_channel *chan;
145         char numsubst[256];
146         char tech[40];
147         int stillgoing;
148         int metric;
149         int oldstatus;
150         int allowredirect_in;
151         int allowredirect_out;
152         int ringbackonly;
153         int musiconhold;
154         int dataquality;
155         int allowdisconnect_in;
156         int allowdisconnect_out;
157         time_t lastcall;
158         struct member *member;
159         struct localuser *next;
160 };
161
162 LOCAL_USER_DECL;
163
164 struct queue_ent {
165         struct ast_call_queue *parent;  /* What queue is our parent */
166         char moh[80];                   /* Name of musiconhold to be used */
167         char announce[80];              /* Announcement to play for member when call is answered */
168         char context[80];               /* Context when user exits queue */
169         int pos;                                        /* Where we are in the queue */
170         int prio;                                       /* Our priority */
171         int last_pos_said;              /* Last position we told the user */
172         time_t last_pos;                /* Last time we told the user their position */
173         int opos;                       /* Where we started in the queue */
174         int handled;                    /* Whether our call was handled */
175         time_t start;                   /* When we started holding */
176         int queuetimeout;               /* How many seconds before timing out of queue */
177         struct ast_channel *chan;       /* Our channel */
178         struct queue_ent *next;         /* The next queue entry */
179 };
180
181 struct member {
182         char tech[80];                          /* Technology */
183         char loc[256];                          /* Location */
184         int penalty;                            /* Are we a last resort? */
185         int calls;                                      /* Number of calls serviced by this member */
186         int dynamic;                            /* Are we dynamically added? */
187         int status;                                     /* Status of queue member */
188         time_t lastcall;                        /* When last successful call was hungup */
189         struct member *next;            /* Next member */
190 };
191
192 struct ast_call_queue {
193         ast_mutex_t     lock;   
194         char name[80];                  /* Name of the queue */
195         char moh[80];                   /* Name of musiconhold to be used */
196         char announce[80];              /* Announcement to play when call is answered */
197         char context[80];               /* Context for this queue */
198         int strategy;                   /* Queueing strategy */
199         int announcefrequency;          /* How often to announce their position */
200         int roundingseconds;            /* How many seconds do we round to? */
201         int announceholdtime;           /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
202         int holdtime;                   /* Current avg holdtime for this queue, based on recursive boxcar filter */
203         int callscompleted;             /* Number of queue calls completed */
204         int callsabandoned;             /* Number of queue calls abandoned */
205         int servicelevel;               /* seconds setting for servicelevel*/
206         int callscompletedinsl;         /* Number of queue calls answererd with servicelevel*/
207         char monfmt[8];                 /* Format to use when recording calls */
208         int monjoin;                    /* Should we join the two files when we are done with the call */
209         char sound_next[80];            /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
210         char sound_thereare[80];        /* Sound file: "There are currently" (def. queue-thereare) */
211         char sound_calls[80];           /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
212         char sound_holdtime[80];        /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
213         char sound_minutes[80];         /* Sound file: "minutes." (def. queue-minutes) */
214         char sound_lessthan[80];        /* Sound file: "less-than" (def. queue-lessthan) */
215         char sound_seconds[80];         /* Sound file: "seconds." (def. queue-seconds) */
216         char sound_thanks[80];          /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
217         char sound_reporthold[80];      /* Sound file: "Hold time" (def. queue-reporthold) */
218
219         int count;                      /* How many entries are in the queue */
220         int maxlen;                     /* Max number of entries in queue */
221         int wrapuptime;         /* Wrapup Time */
222
223         int dead;                       /* Whether this queue is dead or not */
224         int retry;                      /* Retry calling everyone after this amount of time */
225         int timeout;                    /* How long to wait for an answer */
226         
227         /* Queue strategy things */
228         
229         int rrpos;                      /* Round Robin - position */
230         int wrapped;                    /* Round Robin - wrapped around? */
231         int joinempty;                  /* Do we care if the queue has no members? */
232         int eventwhencalled;                    /* Generate an event when the agent is called (before pickup) */
233         int leavewhenempty;             /* If all agents leave the queue, remove callers from the queue */
234         int reportholdtime;             /* Should we report caller hold time to member? */
235         int memberdelay;                /* Seconds to delay connecting member to caller */
236
237         struct member *members;         /* Member channels to be tried */
238         struct queue_ent *head;         /* Start of the actual queue */
239         struct ast_call_queue *next;    /* Next call queue */
240 };
241
242 static struct ast_call_queue *queues = NULL;
243 AST_MUTEX_DEFINE_STATIC(qlock);
244
245 static char *int2strat(int strategy)
246 {
247         int x;
248         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
249                 if (strategy == strategies[x].strategy)
250                         return strategies[x].name;
251         }
252         return "<unknown>";
253 }
254
255 static int strat2int(char *strategy)
256 {
257         int x;
258         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
259                 if (!strcasecmp(strategy, strategies[x].name))
260                         return strategies[x].strategy;
261         }
262         return -1;
263 }
264
265 /* Insert the 'new' entry after the 'prev' entry of queue 'q' */
266 static inline void insert_entry(struct ast_call_queue *q, 
267                                         struct queue_ent *prev, struct queue_ent *new, int *pos)
268 {
269         struct queue_ent *cur;
270
271         if (!q || !new)
272                 return;
273         if (prev) {
274                 cur = prev->next;
275                 prev->next = new;
276         } else {
277                 cur = q->head;
278                 q->head = new;
279         }
280         new->next = cur;
281         new->parent = q;
282         new->pos = ++(*pos);
283         new->opos = *pos;
284 }
285
286 static int has_no_members(struct ast_call_queue *q)
287 {
288         struct member *member;
289         int empty = 1;
290         member = q->members;
291         while(empty && member) {
292                 switch(member->status) {
293                 case AST_DEVICE_UNAVAILABLE:
294                 case AST_DEVICE_INVALID:
295                         /* Not logged on, etc */
296                         break;
297                 default:
298                         /* Not empty */
299                         empty = 0;
300                 }
301                 member = member->next;
302         }
303         return empty;
304 }
305
306 struct statechange {
307         int state;
308         char dev[0];
309 };
310
311 static void *changethread(void *data)
312 {
313         struct ast_call_queue *q;
314         struct statechange *sc = data;
315         struct member *cur;
316         char *loc;
317         loc = strchr(sc->dev, '/');
318         if (loc) {
319                 *loc = '\0';
320                 loc++;
321         } else {
322                 ast_log(LOG_WARNING, "Can't change device with no technology!\n");
323                 free(sc);
324                 return NULL;
325         }
326         if (option_debug)
327                 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
328         ast_mutex_lock(&qlock);
329         for (q = queues; q; q = q->next) {
330                 ast_mutex_lock(&q->lock);
331                 cur = q->members;
332                 while(cur) {
333                         if (!strcasecmp(sc->dev, cur->tech) && !strcmp(loc, cur->loc)) {
334                                 if (cur->status != sc->state) {
335                                         cur->status = sc->state;
336                                         manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
337                                                 "Queue: %s\r\n"
338                                                 "Location: %s/%s\r\n"
339                                                 "Membership: %s\r\n"
340                                                 "Penalty: %d\r\n"
341                                                 "CallsTaken: %d\r\n"
342                                                 "LastCall: %ld\r\n"
343                                                 "Status: %d\r\n",
344                                         q->name, cur->tech, cur->loc, cur->dynamic ? "dynamic" : "static",
345                                         cur->penalty, cur->calls, cur->lastcall, cur->status);
346                                 }
347                         }
348                         cur = cur->next;
349                 }
350                 ast_mutex_unlock(&q->lock);
351         }
352         ast_mutex_unlock(&qlock);
353         ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
354         free(sc);
355         return NULL;
356 }
357
358 static int statechange_queue(const char *dev, int state, void *ign)
359 {
360         /* Avoid potential for deadlocks by spawning a new thread to handle
361            the event */
362         struct statechange *sc;
363         pthread_t t;
364         pthread_attr_t attr;
365         sc = malloc(sizeof(struct statechange) + strlen(dev) + 1);
366         if (sc) {
367                 sc->state = state;
368                 strcpy(sc->dev, dev);
369                 pthread_attr_init(&attr);
370                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
371                 if (ast_pthread_create(&t, &attr, changethread, sc)) {
372                         ast_log(LOG_WARNING, "Failed to create update thread!\n");
373                         free(sc);
374                 }
375         }
376         return 0;
377 }
378
379 static int join_queue(char *queuename, struct queue_ent *qe)
380 {
381         struct ast_call_queue *q;
382         struct queue_ent *cur, *prev = NULL;
383         int res = -1;
384         int pos = 0;
385         int inserted = 0;
386
387         ast_mutex_lock(&qlock);
388         for (q = queues; q; q = q->next) {
389                 if (!strcasecmp(q->name, queuename)) {
390                         /* This is our one */
391                         ast_mutex_lock(&q->lock);
392                         if ((!has_no_members(q) || q->joinempty) && (!q->maxlen || (q->count < q->maxlen))) {
393                                 /* There's space for us, put us at the right position inside
394                                  * the queue. 
395                                  * Take into account the priority of the calling user */
396                                 inserted = 0;
397                                 prev = NULL;
398                                 cur = q->head;
399                                 while(cur) {
400                                         /* We have higher priority than the current user, enter
401                                          * before him, after all the other users with priority
402                                          * higher or equal to our priority. */
403                                         if ((!inserted) && (qe->prio > cur->prio)) {
404                                                 insert_entry(q, prev, qe, &pos);
405                                                 inserted = 1;
406                                         }
407                                         cur->pos = ++pos;
408                                         prev = cur;
409                                         cur = cur->next;
410                                 }
411                                 /* No luck, join at the end of the queue */
412                                 if (!inserted)
413                                         insert_entry(q, prev, qe, &pos);
414                                 strncpy(qe->moh, q->moh, sizeof(qe->moh) - 1);
415                                 strncpy(qe->announce, q->announce, sizeof(qe->announce) - 1);
416                                 strncpy(qe->context, q->context, sizeof(qe->context) - 1);
417                                 q->count++;
418                                 res = 0;
419                                 manager_event(EVENT_FLAG_CALL, "Join", 
420                                         "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
421                                         qe->chan->name, 
422                                         qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
423                                         qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
424                                         q->name, qe->pos, q->count );
425 #if 0
426 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
427 #endif
428                         }
429                         ast_mutex_unlock(&q->lock);
430                         break;
431                 }
432         }
433         ast_mutex_unlock(&qlock);
434         return res;
435 }
436
437 static void free_members(struct ast_call_queue *q, int all)
438 {
439         /* Free non-dynamic members */
440         struct member *curm, *next, *prev;
441         curm = q->members;
442         prev = NULL;
443         while(curm) {
444                 next = curm->next;
445                 if (all || !curm->dynamic) {
446                         if (prev)
447                                 prev->next = next;
448                         else
449                                 q->members = next;
450                         free(curm);
451                 } else 
452                         prev = curm;
453                 curm = next;
454         }
455 }
456
457 static void destroy_queue(struct ast_call_queue *q)
458 {
459         struct ast_call_queue *cur, *prev = NULL;
460         ast_mutex_lock(&qlock);
461         for (cur = queues; cur; cur = cur->next) {
462                 if (cur == q) {
463                         if (prev)
464                                 prev->next = cur->next;
465                         else
466                                 queues = cur->next;
467                 } else {
468                         prev = cur;
469                 }
470         }
471         ast_mutex_unlock(&qlock);
472         free_members(q, 1);
473         ast_mutex_destroy(&q->lock);
474         free(q);
475 }
476
477 static int play_file(struct ast_channel *chan, char *filename)
478 {
479         int res;
480
481         ast_stopstream(chan);
482         res = ast_streamfile(chan, filename, chan->language);
483
484         if (!res)
485                 res = ast_waitstream(chan, "");
486         else
487                 res = 0;
488
489         if (res) {
490                 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
491                 res = 0;
492         }
493         ast_stopstream(chan);
494
495         return res;
496 }
497
498 static int say_position(struct queue_ent *qe)
499 {
500         int res = 0, avgholdmins, avgholdsecs;
501         time_t now;
502
503         /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
504         time(&now);
505         if ( (now - qe->last_pos) < 15 )
506                 return -1;
507
508         /* If either our position has changed, or we are over the freq timer, say position */
509         if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
510                 return -1;
511
512         ast_moh_stop(qe->chan);
513         /* Say we're next, if we are */
514         if (qe->pos == 1) {
515                 res += play_file(qe->chan, qe->parent->sound_next);
516                 goto posout;
517         } else {
518                 res += play_file(qe->chan, qe->parent->sound_thereare);
519                 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
520                 res += play_file(qe->chan, qe->parent->sound_calls);
521         }
522         /* Round hold time to nearest minute */
523         avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
524
525         /* If they have specified a rounding then round the seconds as well */
526         if(qe->parent->roundingseconds) {
527                 avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
528                 avgholdsecs*= qe->parent->roundingseconds;
529         } else {
530                 avgholdsecs=0;
531         }
532
533         if (option_verbose > 2)
534                 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
535
536         /* If the hold time is >1 min, if it's enabled, and if it's not
537            supposed to be only once and we have already said it, say it */
538         if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
539                 res += play_file(qe->chan, qe->parent->sound_holdtime);
540                 if(avgholdmins>0) {
541                         if (avgholdmins < 2) {
542                                 res += play_file(qe->chan, qe->parent->sound_lessthan);
543                                 res += ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
544                         } else 
545                                 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
546                         res += play_file(qe->chan, qe->parent->sound_minutes);
547                 }
548                 if(avgholdsecs>0) {
549                         res += ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
550                         res += play_file(qe->chan, qe->parent->sound_seconds);
551                 }
552
553         }
554
555         posout:
556         /* Set our last_pos indicators */
557         qe->last_pos = now;
558         qe->last_pos_said = qe->pos;
559
560         if (option_verbose > 2)
561                 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
562         res += play_file(qe->chan, qe->parent->sound_thanks);
563         ast_moh_start(qe->chan, qe->moh);
564
565         return (res>0);
566 }
567
568 static void record_abandoned(struct queue_ent *qe)
569 {
570         ast_mutex_lock(&qe->parent->lock);
571         qe->parent->callsabandoned++;
572         ast_mutex_unlock(&qe->parent->lock);
573 }
574
575 static void recalc_holdtime(struct queue_ent *qe)
576 {
577         int oldvalue, newvalue;
578
579         /* Calculate holdtime using a recursive boxcar filter */
580         /* Thanks to SRT for this contribution */
581         /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
582
583         newvalue = time(NULL) - qe->start;
584
585         ast_mutex_lock(&qe->parent->lock);
586         if (newvalue <= qe->parent->servicelevel)
587                 qe->parent->callscompletedinsl++;
588         oldvalue = qe->parent->holdtime;
589         qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
590         ast_mutex_unlock(&qe->parent->lock);
591 }
592
593
594 static void leave_queue(struct queue_ent *qe)
595 {
596         struct ast_call_queue *q;
597         struct queue_ent *cur, *prev = NULL;
598         int pos = 0;
599         q = qe->parent;
600         if (!q)
601                 return;
602         ast_mutex_lock(&q->lock);
603
604         prev = NULL;
605         cur = q->head;
606         while(cur) {
607                 if (cur == qe) {
608                         q->count--;
609
610                         /* Take us out of the queue */
611                         manager_event(EVENT_FLAG_CALL, "Leave",
612                                 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
613                                 qe->chan->name, q->name,  q->count);
614 #if 0
615 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
616 #endif
617                         /* Take us out of the queue */
618                         if (prev)
619                                 prev->next = cur->next;
620                         else
621                                 q->head = cur->next;
622                 } else {
623                         /* Renumber the people after us in the queue based on a new count */
624                         cur->pos = ++pos;
625                         prev = cur;
626                 }
627                 cur = cur->next;
628         }
629         ast_mutex_unlock(&q->lock);
630         if (q->dead && !q->count) {     
631                 /* It's dead and nobody is in it, so kill it */
632                 destroy_queue(q);
633         }
634 }
635
636 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
637 {
638         /* Hang up a tree of stuff */
639         struct localuser *oo;
640         while(outgoing) {
641                 /* Hangup any existing lines we have open */
642                 if (outgoing->chan && (outgoing->chan != exception))
643                         ast_hangup(outgoing->chan);
644                 oo = outgoing;
645                 outgoing=outgoing->next;
646                 free(oo);
647         }
648 }
649
650 static int update_status(struct ast_call_queue *q, struct member *member, int status)
651 {
652         struct member *cur;
653         /* Since a reload could have taken place, we have to traverse the list to
654                 be sure it's still valid */
655         ast_mutex_lock(&q->lock);
656         cur = q->members;
657         while(cur) {
658                 if (member == cur) {
659                         cur->status = status;
660                         manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
661                                 "Queue: %s\r\n"
662                                 "Location: %s/%s\r\n"
663                                 "Membership: %s\r\n"
664                                 "Penalty: %d\r\n"
665                                 "CallsTaken: %d\r\n"
666                                 "LastCall: %ld\r\n"
667                                 "Status: %d\r\n",
668                                         q->name, cur->tech, cur->loc, cur->dynamic ? "dynamic" : "static",
669                                         cur->penalty, cur->calls, cur->lastcall, cur->status);
670                         break;
671                 }
672                 cur = cur->next;
673         }
674         q->callscompleted++;
675         ast_mutex_unlock(&q->lock);
676         return 0;
677 }
678
679 static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
680 {
681         if (status == AST_CAUSE_BUSY)
682                 status = AST_DEVICE_BUSY;
683         else if (status == AST_CAUSE_UNREGISTERED)
684                 status = AST_DEVICE_UNAVAILABLE;
685         else if (status == AST_CAUSE_NOSUCHDRIVER)
686                 status = AST_DEVICE_INVALID;
687         else
688                 status = AST_DEVICE_UNKNOWN;
689         return update_status(q, member, status);
690 }
691
692 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
693 {
694         int res;
695         int status;
696         if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
697                 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s/%s\n", tmp->tech, tmp->numsubst);
698                 if (qe->chan->cdr)
699                         ast_cdr_busy(qe->chan->cdr);
700                 tmp->stillgoing = 0;
701                 return 0;
702         }
703         /* Request the peer */
704         tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst, &status);
705         if (!tmp->chan) {                       /* If we can't, just go on to the next call */
706 #if 0
707                 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
708 #endif                  
709                 if (qe->chan->cdr)
710                         ast_cdr_busy(qe->chan->cdr);
711                 tmp->stillgoing = 0;
712                 update_dial_status(qe->parent, tmp->member, status);
713                 return 0;
714         } else if (status != tmp->oldstatus) 
715                 update_dial_status(qe->parent, tmp->member, status);
716         
717         tmp->chan->appl = "AppQueue";
718         tmp->chan->data = "(Outgoing Line)";
719         tmp->chan->whentohangup = 0;
720         if (tmp->chan->cid.cid_num)
721                 free(tmp->chan->cid.cid_num);
722         tmp->chan->cid.cid_num = NULL;
723         if (tmp->chan->cid.cid_name)
724                 free(tmp->chan->cid.cid_name);
725         tmp->chan->cid.cid_name = NULL;
726         if (tmp->chan->cid.cid_ani)
727                 free(tmp->chan->cid.cid_ani);
728         tmp->chan->cid.cid_ani = NULL;
729         if (qe->chan->cid.cid_num)
730                 tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
731         if (qe->chan->cid.cid_name)
732                 tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
733         if (qe->chan->cid.cid_ani)
734                 tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
735         /* Presense of ADSI CPE on outgoing channel follows ours */
736         tmp->chan->adsicpe = qe->chan->adsicpe;
737         /* Place the call, but don't wait on the answer */
738         res = ast_call(tmp->chan, tmp->numsubst, 0);
739         if (res) {
740                 /* Again, keep going even if there's an error */
741                 if (option_debug)
742                         ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
743                 else if (option_verbose > 2)
744                         ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
745                 ast_hangup(tmp->chan);
746                 tmp->chan = NULL;
747                 tmp->stillgoing = 0;
748                 return 0;
749         } else {
750                 if (qe->parent->eventwhencalled) {
751                         manager_event(EVENT_FLAG_AGENT, "AgentCalled",
752                                                 "AgentCalled: %s/%s\r\n"
753                                                 "ChannelCalling: %s\r\n"
754                                                 "CallerID: %s\r\n"
755                                                 "CallerIDName: %s\r\n"
756                                                 "Context: %s\r\n"
757                                                 "Extension: %s\r\n"
758                                                 "Priority: %d\r\n",
759                                                 tmp->tech, tmp->numsubst, qe->chan->name,
760                                                 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
761                                                 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
762                                                 qe->chan->context, qe->chan->exten, qe->chan->priority);
763                 }
764                 if (option_verbose > 2)
765                         ast_verbose(VERBOSE_PREFIX_3 "Called %s/%s\n", tmp->tech, tmp->numsubst);
766         }
767         return 0;
768 }
769
770 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
771 {
772         struct localuser *cur;
773         struct localuser *best;
774         int bestmetric=0;
775         do {
776                 best = NULL;
777                 cur = outgoing;
778                 while(cur) {
779                         if (cur->stillgoing &&                                                  /* Not already done */
780                                 !cur->chan &&                                                           /* Isn't already going */
781                                 (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
782                                         bestmetric = cur->metric;
783                                         best = cur;
784                         }
785                         cur = cur->next;
786                 }
787                 if (best) {
788                         if (!qe->parent->strategy) {
789                                 /* Ring everyone who shares this best metric (for ringall) */
790                                 cur = outgoing;
791                                 while(cur) {
792                                         if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
793                                                 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
794                                                 ring_entry(qe, cur);
795                                         }
796                                         cur = cur->next;
797                                 }
798                         } else {
799                                 /* Ring just the best channel */
800                                 if (option_debug)
801                                         ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", 
802                                                                         best->tech, best->numsubst, best->metric);
803                                 ring_entry(qe, best);
804                         }
805                 }
806         } while (best && !best->chan);
807         if (!best) {
808                 if (option_debug)
809                         ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
810                 return 0;
811         }
812         return 1;
813 }
814
815 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
816 {
817         struct localuser *cur;
818         struct localuser *best;
819         int bestmetric=0;
820         best = NULL;
821         cur = outgoing;
822         while(cur) {
823                 if (cur->stillgoing &&                                                  /* Not already done */
824                         !cur->chan &&                                                           /* Isn't already going */
825                         (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
826                                 bestmetric = cur->metric;
827                                 best = cur;
828                 }
829                 cur = cur->next;
830         }
831         if (best) {
832                 /* Ring just the best channel */
833                 ast_log(LOG_DEBUG, "Next is '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
834                 qe->parent->rrpos = best->metric % 1000;
835         } else {
836                 /* Just increment rrpos */
837                 if (!qe->parent->wrapped) {
838                         /* No more channels, start over */
839                         qe->parent->rrpos = 0;
840                 } else {
841                         /* Prioritize next entry */
842                         qe->parent->rrpos++;
843                 }
844         }
845         qe->parent->wrapped = 0;
846         return 0;
847 }
848
849 static int valid_exit(struct queue_ent *qe, char digit)
850 {
851         char tmp[2];
852         if (ast_strlen_zero(qe->context))
853                 return 0;
854         tmp[0] = digit;
855         tmp[1] = '\0';
856         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->cid.cid_num)) {
857                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
858                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
859                 qe->chan->priority = 0;
860                 return 1;
861         }
862         return 0;
863 }
864
865 #define AST_MAX_WATCHERS 256
866
867 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)
868 {
869         char *queue = qe->parent->name;
870         struct localuser *o;
871         int found;
872         int numlines;
873         int status;
874         int sentringing = 0;
875         int numbusies = 0;
876         int numnochan = 0;
877         int orig = *to;
878         struct ast_frame *f;
879         struct localuser *peer = NULL;
880         struct ast_channel *watchers[AST_MAX_WATCHERS];
881         int pos;
882         struct ast_channel *winner;
883         struct ast_channel *in = qe->chan;
884         
885         while(*to && !peer) {
886                 o = outgoing;
887                 found = -1;
888                 pos = 1;
889                 numlines = 0;
890                 watchers[0] = in;
891                 while(o) {
892                         /* Keep track of important channels */
893                         if (o->stillgoing && o->chan) {
894                                 watchers[pos++] = o->chan;
895                                 found = 1;
896                         }
897                         o = o->next;
898                         numlines++;
899                 }
900                 if (found < 0) {
901                         if (numlines == (numbusies + numnochan)) {
902                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
903                         } else {
904                                 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
905                         }
906                         *to = 0;
907                         return NULL;
908                 }
909                 winner = ast_waitfor_n(watchers, pos, to);
910                 o = outgoing;
911                 while(o) {
912                         if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
913                                 if (!peer) {
914                                         if (option_verbose > 2)
915                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
916                                         peer = o;
917                                         *allowredir_in = o->allowredirect_in;
918                                         *allowredir_out = o->allowredirect_out;
919                                         *allowdisconnect_in = o->allowdisconnect_in;
920                                         *allowdisconnect_out = o->allowdisconnect_out;
921                                 }
922                         } else if (o->chan && (o->chan == winner)) {
923                                 if (!ast_strlen_zero(o->chan->call_forward)) {
924                                         char tmpchan[256]="";
925                                         char *stuff;
926                                         char *tech;
927                                         strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
928                                         if ((stuff = strchr(tmpchan, '/'))) {
929                                                 *stuff = '\0';
930                                                 stuff++;
931                                                 tech = tmpchan;
932                                         } else {
933                                                 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
934                                                 stuff = tmpchan;
935                                                 tech = "Local";
936                                         }
937                                         /* Before processing channel, go ahead and check for forwarding */
938                                         if (option_verbose > 2)
939                                                 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
940                                         /* Setup parameters */
941                                         o->chan = ast_request(tech, in->nativeformats, stuff, &status);
942                                         if (status != o->oldstatus) 
943                                                 update_dial_status(qe->parent, o->member, status);                                              
944                                         if (!o->chan) {
945                                                 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
946                                                 o->stillgoing = 0;
947                                                 numnochan++;
948                                         } else {
949                                                 if (o->chan->cid.cid_num)
950                                                         free(o->chan->cid.cid_num);
951                                                 o->chan->cid.cid_num = NULL;
952                                                 if (o->chan->cid.cid_name)
953                                                         free(o->chan->cid.cid_name);
954                                                 o->chan->cid.cid_name = NULL;
955
956                                                 if (in->cid.cid_num) {
957                                                         o->chan->cid.cid_num = strdup(in->cid.cid_num);
958                                                         if (!o->chan->cid.cid_num)
959                                                                 ast_log(LOG_WARNING, "Out of memory\n");        
960                                                 }
961                                                 if (in->cid.cid_name) {
962                                                         o->chan->cid.cid_name = strdup(in->cid.cid_name);
963                                                         if (!o->chan->cid.cid_name)
964                                                                 ast_log(LOG_WARNING, "Out of memory\n");        
965                                                 }
966                                                 strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
967                                                 o->chan->cdrflags = in->cdrflags;
968
969                                                 if (in->cid.cid_ani) {
970                                                         if (o->chan->cid.cid_ani)
971                                                                 free(o->chan->cid.cid_ani);
972                                                         o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
973                                                         if (o->chan->cid.cid_ani)
974                                                                 strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
975                                                         else
976                                                                 ast_log(LOG_WARNING, "Out of memory\n");
977                                                 }
978                                                 if (o->chan->cid.cid_rdnis) 
979                                                         free(o->chan->cid.cid_rdnis);
980                                                 if (!ast_strlen_zero(in->macroexten))
981                                                         o->chan->cid.cid_rdnis = strdup(in->macroexten);
982                                                 else
983                                                         o->chan->cid.cid_rdnis = strdup(in->exten);
984                                                 if (ast_call(o->chan, tmpchan, 0)) {
985                                                         ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
986                                                         o->stillgoing = 0;
987                                                         ast_hangup(o->chan);
988                                                         o->chan = NULL;
989                                                         numnochan++;
990                                                 }
991                                         }
992                                         /* Hangup the original channel now, in case we needed it */
993                                         ast_hangup(winner);
994                                         continue;
995                                 }
996                                 f = ast_read(winner);
997                                 if (f) {
998                                         if (f->frametype == AST_FRAME_CONTROL) {
999                                                 switch(f->subclass) {
1000                                             case AST_CONTROL_ANSWER:
1001                                                         /* This is our guy if someone answered. */
1002                                                         if (!peer) {
1003                                                                 if (option_verbose > 2)
1004                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1005                                                                 peer = o;
1006                                                                 *allowredir_in = o->allowredirect_in;
1007                                                                 *allowredir_out = o->allowredirect_out;
1008                                                                 *allowdisconnect_in = o->allowdisconnect_out;
1009                                                                 *allowdisconnect_out = o->allowdisconnect_out;
1010                                                         }
1011                                                         break;
1012                                                 case AST_CONTROL_BUSY:
1013                                                         if (option_verbose > 2)
1014                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
1015                                                         o->stillgoing = 0;
1016                                                         if (in->cdr)
1017                                                                 ast_cdr_busy(in->cdr);
1018                                                         ast_hangup(o->chan);
1019                                                         o->chan = NULL;
1020                                                         if (qe->parent->strategy)
1021                                                                 ring_one(qe, outgoing);
1022                                                         numbusies++;
1023                                                         break;
1024                                                 case AST_CONTROL_CONGESTION:
1025                                                         if (option_verbose > 2)
1026                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
1027                                                         o->stillgoing = 0;
1028                                                         if (in->cdr)
1029                                                                 ast_cdr_busy(in->cdr);
1030                                                         ast_hangup(o->chan);
1031                                                         o->chan = NULL;
1032                                                         if (qe->parent->strategy)
1033                                                                 ring_one(qe, outgoing);
1034                                                         numbusies++;
1035                                                         break;
1036                                                 case AST_CONTROL_RINGING:
1037                                                         if (option_verbose > 2)
1038                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
1039                                                         if (!sentringing) {
1040 #if 0
1041                                                                 ast_indicate(in, AST_CONTROL_RINGING);
1042 #endif                                                          
1043                                                                 sentringing++;
1044                                                         }
1045                                                         break;
1046                                                 case AST_CONTROL_OFFHOOK:
1047                                                         /* Ignore going off hook */
1048                                                         break;
1049                                                 default:
1050                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
1051                                                 }
1052                                         }
1053                                         ast_frfree(f);
1054                                 } else {
1055                                         o->stillgoing = 0;
1056                                         ast_hangup(o->chan);
1057                                         o->chan = NULL;
1058                                         if (qe->parent->strategy)
1059                                                 ring_one(qe, outgoing);
1060                                 }
1061                         }
1062                         o = o->next;
1063                 }
1064                 if (winner == in) {
1065                         f = ast_read(in);
1066 #if 0
1067                         if (f && (f->frametype != AST_FRAME_VOICE))
1068                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
1069                         else if (!f || (f->frametype != AST_FRAME_VOICE))
1070                                 printf("Hangup received on %s\n", in->name);
1071 #endif
1072                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
1073                                 /* Got hung up */
1074                                 *to=-1;
1075                                 return NULL;
1076                         }
1077                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect_out && (f->subclass == '*')) {
1078                             if (option_verbose > 3)
1079                                         ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
1080                                 *to=0;
1081                                 return NULL;
1082                         }
1083                         if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
1084                                 if (option_verbose > 3)
1085                                         ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
1086                                 *to=0;
1087                                 *digit=f->subclass;
1088                                 return NULL;
1089                         }
1090                 }
1091                 if (!*to && (option_verbose > 2))
1092                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
1093         }
1094
1095         return peer;
1096         
1097 }
1098
1099 static int is_our_turn(struct queue_ent *qe)
1100 {
1101         struct queue_ent *ch;
1102         int res;
1103
1104         /* Atomically read the parent head -- does not need a lock */
1105         ch = qe->parent->head;
1106         /* If we are now at the top of the head, break out */
1107         if (ch == qe) {
1108                 if (option_debug)
1109                         ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
1110                 res = 1;
1111         } else {
1112                 if (option_debug)
1113                         ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
1114                 res = 0;
1115         }
1116         return res;
1117 }
1118
1119 static int wait_our_turn(struct queue_ent *qe, int ringing)
1120 {
1121         struct queue_ent *ch;
1122         int res = 0;
1123         time_t now;
1124
1125         /* This is the holding pen for callers 2 through maxlen */
1126         for (;;) {
1127                 /* Atomically read the parent head -- does not need a lock */
1128                 ch = qe->parent->head;
1129
1130                 /* If we are now at the top of the head, break out */
1131                 if (ch == qe) {
1132                         if (option_debug)
1133                                 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
1134                         break;
1135                 }
1136
1137                 /* If we have timed out, break out */
1138                 if ( qe->queuetimeout ) {
1139                         time(&now);
1140                         if ( (now - qe->start) >= qe->queuetimeout )
1141                         break;
1142                 }
1143
1144                 /* leave the queue if no agents, if enabled */
1145                 if (has_no_members(qe->parent) && qe->parent->leavewhenempty) {
1146                         leave_queue(qe);
1147                         break;
1148                 }
1149
1150                 /* Make a position announcement, if enabled */
1151                 if (qe->parent->announcefrequency && !ringing)
1152                         say_position(qe);
1153
1154                 /* Wait a second before checking again */
1155                 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
1156                 if (res)
1157                         break;
1158         }
1159         return res;
1160 }
1161
1162 static int update_queue(struct ast_call_queue *q, struct member *member)
1163 {
1164         struct member *cur;
1165         /* Since a reload could have taken place, we have to traverse the list to
1166                 be sure it's still valid */
1167         ast_mutex_lock(&q->lock);
1168         cur = q->members;
1169         while(cur) {
1170                 if (member == cur) {
1171                         time(&cur->lastcall);
1172                         cur->calls++;
1173                         break;
1174                 }
1175                 cur = cur->next;
1176         }
1177         q->callscompleted++;
1178         ast_mutex_unlock(&q->lock);
1179         return 0;
1180 }
1181
1182 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
1183 {
1184         switch (q->strategy) {
1185         case QUEUE_STRATEGY_RINGALL:
1186                 /* Everyone equal, except for penalty */
1187                 tmp->metric = mem->penalty * 1000000;
1188                 break;
1189         case QUEUE_STRATEGY_ROUNDROBIN:
1190                 if (!pos) {
1191                         if (!q->wrapped) {
1192                                 /* No more channels, start over */
1193                                 q->rrpos = 0;
1194                         } else {
1195                                 /* Prioritize next entry */
1196                                 q->rrpos++;
1197                         }
1198                         q->wrapped = 0;
1199                 }
1200                 /* Fall through */
1201         case QUEUE_STRATEGY_RRMEMORY:
1202                 if (pos < q->rrpos) {
1203                         tmp->metric = 1000 + pos;
1204                 } else {
1205                         if (pos > q->rrpos) {
1206                                 /* Indicate there is another priority */
1207                                 q->wrapped = 1;
1208                         }
1209                         tmp->metric = pos;
1210                 }
1211                 tmp->metric += mem->penalty * 1000000;
1212                 break;
1213         case QUEUE_STRATEGY_RANDOM:
1214                 tmp->metric = rand() % 1000;
1215                 tmp->metric += mem->penalty * 1000000;
1216                 break;
1217         case QUEUE_STRATEGY_FEWESTCALLS:
1218                 tmp->metric = mem->calls;
1219                 tmp->metric += mem->penalty * 1000000;
1220                 break;
1221         case QUEUE_STRATEGY_LEASTRECENT:
1222                 if (!mem->lastcall)
1223                         tmp->metric = 0;
1224                 else
1225                         tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
1226                 tmp->metric += mem->penalty * 1000000;
1227                 break;
1228         default:
1229                 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
1230                 break;
1231         }
1232         return 0;
1233 }
1234
1235 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
1236 {
1237         struct member *cur;
1238         struct localuser *outgoing=NULL, *tmp = NULL;
1239         int to;
1240         int allowredir_in=0;
1241         int allowredir_out=0;
1242         int allowdisconnect_in=0;
1243         int allowdisconnect_out=0;
1244         char restofit[AST_MAX_EXTENSION];
1245         char oldexten[AST_MAX_EXTENSION]="";
1246         char oldcontext[AST_MAX_EXTENSION]="";
1247         char queuename[256]="";
1248         char *newnum;
1249         char *monitorfilename;
1250         struct ast_channel *peer;
1251         struct localuser *lpeer;
1252         struct member *member;
1253         int res = 0, bridge = 0;
1254         int zapx = 2;
1255         int x=0;
1256         char *announce = NULL;
1257         char digit = 0;
1258         time_t callstart;
1259         time_t now;
1260         struct ast_bridge_config config;
1261         /* Hold the lock while we setup the outgoing calls */
1262         ast_mutex_lock(&qe->parent->lock);
1263         if (option_debug)
1264                 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 
1265                                                         qe->chan->name);
1266         strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
1267         time(&now);
1268         cur = qe->parent->members;
1269         if (!ast_strlen_zero(qe->announce))
1270                 announce = qe->announce;
1271         if (announceoverride && !ast_strlen_zero(announceoverride))
1272                 announce = announceoverride;
1273         while(cur) {
1274                 /* Get a technology/[device:]number pair */
1275                 tmp = malloc(sizeof(struct localuser));
1276                 if (!tmp) {
1277                         ast_mutex_unlock(&qe->parent->lock);
1278                         ast_log(LOG_WARNING, "Out of memory\n");
1279                         goto out;
1280                 }
1281                 memset(tmp, 0, sizeof(struct localuser));
1282                 tmp->stillgoing = -1;
1283                 if (options) {
1284                         if (strchr(options, 't'))
1285                                 tmp->allowredirect_in = 1;
1286                         if (strchr(options, 'T'))
1287                                 tmp->allowredirect_out = 1;
1288                         if (strchr(options, 'r'))
1289                                 tmp->ringbackonly = 1;
1290                         if (strchr(options, 'm'))
1291                                 tmp->musiconhold = 1;
1292                         if (strchr(options, 'd'))
1293                                 tmp->dataquality = 1;
1294                         if (strchr(options, 'h'))
1295                                 tmp->allowdisconnect_in = 1;
1296                         if (strchr(options, 'H'))
1297                                 tmp->allowdisconnect_out = 1;
1298                         if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout))
1299                                 *go_on = 1;
1300                 }
1301                 if (option_debug) {
1302                         if (url)
1303                                 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
1304                         else 
1305                                 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
1306                 }
1307
1308                 tmp->member = cur;              /* Never directly dereference!  Could change on reload */
1309                 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
1310                 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
1311                 tmp->oldstatus = cur->status;
1312                 tmp->lastcall = cur->lastcall;
1313                 /* If we're dialing by extension, look at the extension to know what to dial */
1314                 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
1315                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
1316                         snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
1317                         if (option_debug)
1318                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
1319                 }
1320                 /* Special case: If we ring everyone, go ahead and ring them, otherwise
1321                    just calculate their metric for the appropriate strategy */
1322                 calc_metric(qe->parent, cur, x++, qe, tmp);
1323                 /* Put them in the list of outgoing thingies...  We're ready now. 
1324                    XXX If we're forcibly removed, these outgoing calls won't get
1325                    hung up XXX */
1326                 tmp->next = outgoing;
1327                 outgoing = tmp;         
1328                 /* If this line is up, don't try anybody else */
1329                 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
1330                         break;
1331
1332                 cur = cur->next;
1333         }
1334         if (qe->parent->timeout)
1335                 to = qe->parent->timeout * 1000;
1336         else
1337                 to = -1;
1338         ring_one(qe, outgoing);
1339         ast_mutex_unlock(&qe->parent->lock);
1340         lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &digit);
1341         ast_mutex_lock(&qe->parent->lock);
1342         if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
1343                 store_next(qe, outgoing);
1344         }
1345         ast_mutex_unlock(&qe->parent->lock);
1346         if (lpeer)
1347                 peer = lpeer->chan;
1348         else
1349                 peer = NULL;
1350         if (!peer) {
1351                 if (to) {
1352                         /* Musta gotten hung up */
1353                         record_abandoned(qe);
1354                         res = -1;
1355                 } else {
1356                         if (digit && valid_exit(qe, digit))
1357                                 res=digit;
1358                         else
1359                                 /* Nobody answered, next please? */
1360                                 res=0;
1361                 }
1362                 if (option_debug)
1363                         ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
1364                 goto out;
1365         }
1366         if (peer) {
1367                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
1368                    we will always return with -1 so that it is hung up properly after the 
1369                    conversation.  */
1370                 qe->handled++;
1371                 if (!strcmp(qe->chan->type,"Zap")) {
1372                         if (tmp->dataquality) zapx = 0;
1373                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1374                 }                       
1375                 if (!strcmp(peer->type,"Zap")) {
1376                         if (tmp->dataquality) zapx = 0;
1377                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1378                 }
1379                 /* Update parameters for the queue */
1380                 recalc_holdtime(qe);
1381                 member = lpeer->member;
1382                 hanguptree(outgoing, peer);
1383                 outgoing = NULL;
1384                 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
1385                         int res2;
1386                         res2 = ast_autoservice_start(qe->chan);
1387                         if (!res2) {
1388                                 if (qe->parent->memberdelay) {
1389                                         ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
1390                                         res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
1391                                 }
1392                                 if (!res2 && announce) {
1393                                         if (play_file(peer, announce))
1394                                                 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
1395                                 }
1396                                 if (!res2 && qe->parent->reportholdtime) {
1397                                         if (!play_file(peer, qe->parent->sound_reporthold)) {
1398                                                 int holdtime;
1399                                                 time_t now;
1400
1401                                                 time(&now);
1402                                                 holdtime = abs((now - qe->start) / 60);
1403                                                 if (holdtime < 2) {
1404                                                         play_file(peer, qe->parent->sound_lessthan);
1405                                                         ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
1406                                                 } else 
1407                                                         ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
1408                                                 play_file(peer, qe->parent->sound_minutes);
1409                                         }
1410                                 }
1411                         }
1412                         res2 |= ast_autoservice_stop(qe->chan);
1413                         if (res2) {
1414                                 /* Agent must have hung up */
1415                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
1416                                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
1417                                 ast_hangup(peer);
1418                                 return -1;
1419                         }
1420                 }
1421                 /* Stop music on hold */
1422                 ast_moh_stop(qe->chan);
1423                 /* If appropriate, log that we have a destination channel */
1424                 if (qe->chan->cdr)
1425                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
1426                 /* Make sure channels are compatible */
1427                 res = ast_channel_make_compatible(qe->chan, peer);
1428                 if (res < 0) {
1429                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1430                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1431                         ast_hangup(peer);
1432                         return -1;
1433                 }
1434                 /* Begin Monitoring */
1435                 if (qe->parent->monfmt && *qe->parent->monfmt) {
1436                         monitorfilename = pbx_builtin_getvar_helper( qe->chan, "MONITOR_FILENAME");
1437                         if(monitorfilename) {
1438                                 ast_monitor_start( peer, qe->parent->monfmt, monitorfilename, 1 );
1439                         } else {
1440                                 ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1441                         }
1442                         if(qe->parent->monjoin) {
1443                                 ast_monitor_setjoinfiles( peer, 1);
1444                         }
1445                 }
1446                 /* Drop out of the queue at this point, to prepare for next caller */
1447                 leave_queue(qe);                        
1448                 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
1449                         if (option_debug)
1450                                 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1451                         ast_channel_sendurl( peer, url );
1452                 }
1453                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1454                 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1455                 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1456                 time(&callstart);
1457
1458                 memset(&config,0,sizeof(struct ast_bridge_config));
1459         config.allowredirect_in = allowredir_in;
1460         config.allowredirect_out = allowredir_out;
1461         config.allowdisconnect_in = allowdisconnect_in;
1462         config.allowdisconnect_out = allowdisconnect_out;
1463         bridge = ast_bridge_call(qe->chan,peer,&config);
1464
1465                 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1466                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1467                 } else if (qe->chan->_softhangup) {
1468                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1469                 } else {
1470                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1471                 }
1472
1473                 if(bridge != AST_PBX_NO_HANGUP_PEER)
1474                         ast_hangup(peer);
1475                 update_queue(qe->parent, member);
1476                 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1477                 else res = bridge; /* bridge error, stay in the queue */
1478         }       
1479 out:
1480         hanguptree(outgoing, NULL);
1481         return res;
1482 }
1483
1484 static int wait_a_bit(struct queue_ent *qe)
1485 {
1486         /* Don't need to hold the lock while we setup the outgoing calls */
1487         int retrywait = qe->parent->retry * 1000;
1488         return ast_waitfordigit(qe->chan, retrywait);
1489 }
1490
1491 // [PHM 06/26/03]
1492
1493 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
1494 {
1495         struct member * ret = NULL ;
1496         struct member *mem;
1497         char buf[500] ;
1498
1499         if( q != NULL )
1500         {
1501                 mem = q->members ;
1502
1503                 while( mem != NULL ) {
1504                         snprintf( buf, sizeof(buf), "%s/%s", mem->tech, mem->loc);
1505
1506                         if( strcmp( buf, interface ) == 0 ) {
1507                                 ret = mem ;
1508                                 break ;
1509                         }
1510                         else
1511                                 mem = mem->next ;
1512                 }
1513         }
1514
1515         return( ret ) ;
1516 }
1517
1518
1519 static struct member * create_queue_node( char * interface, int penalty )
1520 {
1521         struct member * cur ;
1522         char * tmp ;
1523         
1524         /* Add a new member */
1525
1526         cur = malloc(sizeof(struct member));
1527
1528         if (cur) {
1529                 memset(cur, 0, sizeof(struct member));
1530                 cur->penalty = penalty;
1531                 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
1532                 if ((tmp = strchr(cur->tech, '/')))
1533                         *tmp = '\0';
1534                 if ((tmp = strchr(interface, '/'))) {
1535                         tmp++;
1536                         strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1537                 } else
1538                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1539                 cur->status = ast_device_state(interface);
1540         }
1541
1542         return( cur ) ;
1543 }
1544
1545 static int remove_from_queue(char *queuename, char *interface)
1546 {
1547         struct ast_call_queue *q;
1548         struct member *last_member, *look;
1549         int res = RES_NOSUCHQUEUE;
1550
1551         ast_mutex_lock(&qlock);
1552         for (q = queues ; q ; q = q->next) {
1553                 ast_mutex_lock(&q->lock);
1554                 if (!strcmp(q->name, queuename)) {
1555                         if ((last_member = interface_exists(q, interface))) {
1556                                 if ((look = q->members) == last_member) {
1557                                         q->members = last_member->next;
1558                                 } else {
1559                                         while (look != NULL) {
1560                                                 if (look->next == last_member) {
1561                                                         look->next = last_member->next;
1562                                                         break;
1563                                                 } else {
1564                                                          look = look->next;
1565                                                 }
1566                                         }
1567                                 }
1568                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
1569                                                 "Queue: %s\r\n"
1570                                                 "Location: %s/%s\r\n",
1571                                         q->name, last_member->tech, last_member->loc);
1572                                 free(last_member);
1573                                 res = RES_OKAY;
1574                         } else {
1575                                 res = RES_EXISTS;
1576                         }
1577                         ast_mutex_unlock(&q->lock);
1578                         break;
1579                 }
1580                 ast_mutex_unlock(&q->lock);
1581         }
1582         ast_mutex_unlock(&qlock);
1583         return res;
1584 }
1585
1586 static int add_to_queue(char *queuename, char *interface, int penalty)
1587 {
1588         struct ast_call_queue *q;
1589         struct member *new_member;
1590         int res = RES_NOSUCHQUEUE;
1591
1592         ast_mutex_lock(&qlock);
1593         for (q = queues ; q ; q = q->next) {
1594                 ast_mutex_lock(&q->lock);
1595                 if (!strcmp(q->name, queuename)) {
1596                         if (interface_exists(q, interface) == NULL) {
1597                                 new_member = create_queue_node(interface, penalty);
1598
1599                                 if (new_member != NULL) {
1600                                         new_member->dynamic = 1;
1601                                         new_member->next = q->members;
1602                                         q->members = new_member;
1603                                         manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
1604                                                 "Queue: %s\r\n"
1605                                                 "Location: %s/%s\r\n"
1606                                                 "Membership: %s\r\n"
1607                                                 "Penalty: %d\r\n"
1608                                                 "CallsTaken: %d\r\n"
1609                                                 "LastCall: %ld\r\n"
1610                                                 "Status: %d\r\n",
1611                                         q->name, new_member->tech, new_member->loc, new_member->dynamic ? "dynamic" : "static",
1612                                         new_member->penalty, new_member->calls, new_member->lastcall, new_member->status);
1613                                         res = RES_OKAY;
1614                                 } else {
1615                                         res = RES_OUTOFMEMORY;
1616                                 }
1617                         } else {
1618                                 res = RES_EXISTS;
1619                         }
1620                         ast_mutex_unlock(&q->lock);
1621                         break;
1622                 }
1623                 ast_mutex_unlock(&q->lock);
1624         }
1625         ast_mutex_unlock(&qlock);
1626         return res;
1627 }
1628
1629 static int rqm_exec(struct ast_channel *chan, void *data)
1630 {
1631         int res=-1;
1632         struct localuser *u;
1633         char *info, *queuename;
1634         char tmpchan[256]="";
1635         char *interface = NULL;
1636
1637         if (!data) {
1638                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface])\n");
1639                 return -1;
1640         }
1641
1642         info = ast_strdupa((char *)data);
1643         if (!info) {
1644                 ast_log(LOG_ERROR, "Out of memory\n");
1645                 return -1;
1646         }
1647
1648         LOCAL_USER_ADD(u);
1649
1650         queuename = info;
1651         if (queuename) {
1652                 interface = strchr(queuename, '|');
1653                 if (interface) {
1654                         *interface = '\0';
1655                         interface++;
1656                 }
1657                 else {
1658                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1659                         interface = strrchr(tmpchan, '-');
1660                         if (interface)
1661                                 *interface = '\0';
1662                         interface = tmpchan;
1663                 }
1664         }
1665
1666         switch (remove_from_queue(queuename, interface)) {
1667         case RES_OKAY:
1668                 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename);
1669                 res = 0;
1670                 break;
1671         case RES_EXISTS:
1672                 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
1673                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
1674                         chan->priority += 100;
1675                 }
1676                 res = 0;
1677                 break;
1678         case RES_NOSUCHQUEUE:
1679                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1680                 res = 0;
1681                 break;
1682         case RES_OUTOFMEMORY:
1683                 ast_log(LOG_ERROR, "Out of memory\n");
1684                 break;
1685         }
1686
1687         LOCAL_USER_REMOVE(u);
1688         return res;
1689 }
1690
1691 static int aqm_exec(struct ast_channel *chan, void *data)
1692 {
1693         int res=-1;
1694         struct localuser *u;
1695         char *queuename;
1696         char *info;
1697         char tmpchan[512]="";
1698         char *interface=NULL;
1699         char *penaltys=NULL;
1700         int penalty = 0;
1701
1702         if (!data) {
1703                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\n");
1704                 return -1;
1705         }
1706
1707         info = ast_strdupa((char *)data);
1708         if (!info) {
1709                 ast_log(LOG_ERROR, "Out of memory\n");
1710                 return -1;
1711         }
1712         LOCAL_USER_ADD(u);
1713
1714         queuename = info;
1715         if (queuename) {
1716                 interface = strchr(queuename, '|');
1717                 if (interface) {
1718                         *interface = '\0';
1719                         interface++;
1720                 }
1721                 if (interface) {
1722                         penaltys = strchr(interface, '|');
1723                         if (penaltys) {
1724                                 *penaltys = '\0';
1725                                 penaltys++;
1726                         }
1727                 }
1728                 if (!interface || ast_strlen_zero(interface)) {
1729                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1730                         interface = strrchr(tmpchan, '-');
1731                         if (interface)
1732                                 *interface = '\0';
1733                         interface = tmpchan;
1734                 }
1735                 if (penaltys && strlen(penaltys)) {
1736                         if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
1737                                 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
1738                                 penalty = 0;
1739                         }
1740                 }
1741         }
1742
1743         switch (add_to_queue(queuename, interface, penalty)) {
1744         case RES_OKAY:
1745                 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1746                 res = 0;
1747                 break;
1748         case RES_EXISTS:
1749                 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
1750                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
1751                         chan->priority += 100;
1752                 }
1753                 res = 0;
1754                 break;
1755         case RES_NOSUCHQUEUE:
1756                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1757                 res = 0;
1758                 break;
1759         case RES_OUTOFMEMORY:
1760                 ast_log(LOG_ERROR, "Out of memory\n");
1761                 break;
1762         }
1763
1764         LOCAL_USER_REMOVE(u);
1765         return res;
1766 }
1767
1768 static int queue_exec(struct ast_channel *chan, void *data)
1769 {
1770         int res=-1;
1771         int ringing=0;
1772         struct localuser *u;
1773         char *queuename;
1774         char info[512];
1775         char *options = NULL;
1776         char *url = NULL;
1777         char *announceoverride = NULL;
1778         char *user_priority;
1779         int prio;
1780         char *queuetimeoutstr = NULL;
1781
1782         /* whether to exit Queue application after the timeout hits */
1783         int go_on = 0;
1784
1785         /* Our queue entry */
1786         struct queue_ent qe;
1787         
1788         if (!data) {
1789                 ast_log(LOG_WARNING, "Queue requires an argument (queuename[|[timeout][|URL]])\n");
1790                 return -1;
1791         }
1792         
1793         LOCAL_USER_ADD(u);
1794
1795         /* Setup our queue entry */
1796         memset(&qe, 0, sizeof(qe));
1797         
1798         /* Parse our arguments XXX Check for failure XXX */
1799         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1800         queuename = info;
1801         if (queuename) {
1802                 options = strchr(queuename, '|');
1803                 if (options) {
1804                         *options = '\0';
1805                         options++;
1806                         url = strchr(options, '|');
1807                         if (url) {
1808                                 *url = '\0';
1809                                 url++;
1810                                 announceoverride = strchr(url, '|');
1811                                 if (announceoverride) {
1812                                         *announceoverride = '\0';
1813                                         announceoverride++;
1814                                         queuetimeoutstr = strchr(announceoverride, '|');
1815                                         if (queuetimeoutstr) {
1816                                                 *queuetimeoutstr = '\0';
1817                                                 queuetimeoutstr++;
1818                                                 qe.queuetimeout = atoi(queuetimeoutstr);
1819                                         } else {
1820                                                 qe.queuetimeout = 0;
1821                                         }
1822                                 }
1823                         }
1824                 }
1825         }
1826
1827         /* Get the priority from the variable ${QUEUE_PRIO} */
1828         user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
1829         if (user_priority) {
1830                 if (sscanf(user_priority, "%d", &prio) == 1) {
1831                         if (option_debug)
1832                                 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
1833                                                                 chan->name, prio);
1834                 } else {
1835                         ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
1836                                                         user_priority, chan->name);
1837                         prio = 0;
1838                 }
1839         } else {
1840                 if (option_debug)
1841                         ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
1842                 prio = 0;
1843         }
1844
1845         if (options) {
1846                 if (strchr(options, 'r')) {
1847                         ringing = 1;
1848                 }
1849         }
1850
1851 //      if (option_debug) 
1852                 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n",
1853                                 queuename, options, url, announceoverride, qe.queuetimeout, (int)prio);
1854
1855         qe.chan = chan;
1856         qe.start = time(NULL);
1857         qe.prio = (int)prio;
1858         qe.last_pos_said = 0;
1859         qe.last_pos = 0;
1860         if (!join_queue(queuename, &qe)) {
1861                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->cid.cid_num ? chan->cid.cid_num : "");
1862                 /* Start music on hold */
1863 check_turns:
1864                 if (ringing) {
1865                         ast_indicate(chan, AST_CONTROL_RINGING);
1866                 } else {              
1867                         ast_moh_start(chan, qe.moh);
1868                 }
1869                 for (;;) {
1870                         /* This is the wait loop for callers 2 through maxlen */
1871
1872                         res = wait_our_turn(&qe, ringing);
1873                         /* If they hungup, return immediately */
1874                         if (res < 0) {
1875                                 /* Record this abandoned call */
1876                                 record_abandoned(&qe);
1877                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1878                                 if (option_verbose > 2) {
1879                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1880                                         res = -1;
1881                                 }
1882                                 break;
1883                         }
1884                         if (!res) 
1885                                 break;
1886                         if (valid_exit(&qe, res)) {
1887                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1888                                 break;
1889                         }
1890                 }
1891                 if (!res) {
1892                         int makeannouncement = 0;
1893                         for (;;) {
1894                                 /* This is the wait loop for the head caller*/
1895                                 /* To exit, they may get their call answered; */
1896                                 /* they may dial a digit from the queue context; */
1897                                 /* or, they may timeout. */
1898
1899                                 /* Leave if we have exceeded our queuetimeout */
1900                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1901                                         res = 0;
1902                                         break;
1903                                 }
1904
1905                                 if (makeannouncement) {
1906                                         /* Make a position announcement, if enabled */
1907                                         if (qe.parent->announcefrequency && !ringing)
1908                                                 say_position(&qe);
1909                                 }
1910                                 makeannouncement = 1;
1911
1912                                 /* Try calling all queue members for 'timeout' seconds */
1913                                 res = try_calling(&qe, options, announceoverride, url, &go_on);
1914                                 if (res) {
1915                                         if (res < 0) {
1916                                                 if (!qe.handled)
1917                                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1918                                         } else if (res > 0)
1919                                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1920                                         break;
1921                                 }
1922
1923                                 /* leave the queue if no agents, if enabled */
1924                                 if (has_no_members(qe.parent) && (qe.parent->leavewhenempty)) {
1925                                         res = 0;
1926                                         break;
1927                                 }
1928
1929                                 /* Leave if we have exceeded our queuetimeout */
1930                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1931                                         res = 0;
1932                                         break;
1933                                 }
1934
1935                                 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
1936                                 res = wait_a_bit(&qe);
1937                                 if (res < 0) {
1938                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1939                                         if (option_verbose > 2) {
1940                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1941                                                 res = -1;
1942                                         }
1943                                         break;
1944                                 }
1945                                 if (res && valid_exit(&qe, res)) {
1946                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1947                                         break;
1948                                 }
1949                                 /* exit after 'timeout' cycle if 'n' option enabled */
1950                                 if (go_on) {
1951                                         if (option_verbose > 2) {
1952                                                 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
1953                                                 res = -1;
1954                                         }
1955                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1956                                         res = 0;
1957                                         break;
1958                                 }
1959                                 /* Since this is a priority queue and 
1960                                  * it is not sure that we are still at the head
1961                                  * of the queue, go and check for our turn again.
1962                                  */
1963                                 if (!is_our_turn(&qe)) {
1964                                         ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
1965                                                                 qe.chan->name);
1966                                         goto check_turns;
1967                                 }
1968                         }
1969                 }
1970                 /* Don't allow return code > 0 */
1971                 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
1972                         res = 0;        
1973                         if (ringing) {
1974                                 ast_indicate(chan, -1);
1975                         } else {
1976                                 ast_moh_stop(chan);
1977                         }                       
1978                         ast_stopstream(chan);
1979                 }
1980                 leave_queue(&qe);
1981         } else {
1982                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1983                 res =  0;
1984         }
1985         LOCAL_USER_REMOVE(u);
1986         return res;
1987 }
1988
1989 static void reload_queues(void)
1990 {
1991         struct ast_call_queue *q, *ql, *qn;
1992         struct ast_config *cfg;
1993         char *cat, *tmp;
1994         struct ast_variable *var;
1995         struct member *prev, *cur;
1996         int new;
1997         cfg = ast_load("queues.conf");
1998         if (!cfg) {
1999                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
2000                 return;
2001         }
2002         ast_mutex_lock(&qlock);
2003         /* Mark all queues as dead for the moment */
2004         q = queues;
2005         while(q) {
2006                 q->dead = 1;
2007                 q = q->next;
2008         }
2009         /* Chug through config file */
2010         cat = ast_category_browse(cfg, NULL);
2011         while(cat) {
2012                 if (strcasecmp(cat, "general")) {
2013                         /* Look for an existing one */
2014                         q = queues;
2015                         while(q) {
2016                                 if (!strcmp(q->name, cat))
2017                                         break;
2018                                 q = q->next;
2019                         }
2020                         if (!q) {
2021                                 /* Make one then */
2022                                 q = malloc(sizeof(struct ast_call_queue));
2023                                 if (q) {
2024                                         /* Initialize it */
2025                                         memset(q, 0, sizeof(struct ast_call_queue));
2026                                         ast_mutex_init(&q->lock);
2027                                         strncpy(q->name, cat, sizeof(q->name) - 1);
2028                                         new = 1;
2029                                 } else new = 0;
2030                         } else
2031                                         new = 0;
2032                         if (q) {
2033                                 if (!new) 
2034                                         ast_mutex_lock(&q->lock);
2035                                 /* Re-initialize the queue */
2036                                 q->dead = 0;
2037                                 q->retry = 0;
2038                                 q->timeout = -1;
2039                                 q->maxlen = 0;
2040                                 q->announcefrequency = 0;
2041                                 q->announceholdtime = 0;
2042                                 q->roundingseconds = 0; /* Default - don't announce seconds */
2043                                 q->holdtime = 0;
2044                                 q->callscompleted = 0;
2045                                 q->callsabandoned = 0;
2046                                 q->callscompletedinsl = 0;
2047                                 q->servicelevel = 0;
2048                                 q->wrapuptime = 0;
2049                                 free_members(q, 0);
2050                                 q->moh[0] = '\0';
2051                                 q->announce[0] = '\0';
2052                                 q->context[0] = '\0';
2053                                 q->monfmt[0] = '\0';
2054                                 strncpy(q->sound_next, "queue-youarenext", sizeof(q->sound_next) - 1);
2055                                 strncpy(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare) - 1);
2056                                 strncpy(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls) - 1);
2057                                 strncpy(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime) - 1);
2058                                 strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1);
2059                                 strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1);
2060                                 strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1);
2061                                 strncpy(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan) - 1);
2062                                 strncpy(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold) - 1);
2063                                 prev = q->members;
2064                                 if (prev) {
2065                                         /* find the end of any dynamic members */
2066                                         while(prev->next)
2067                                                 prev = prev->next;
2068                                 }
2069                                 var = ast_variable_browse(cfg, cat);
2070                                 while(var) {
2071                                         if (!strcasecmp(var->name, "member")) {
2072                                                 /* Add a new member */
2073                                                 cur = malloc(sizeof(struct member));
2074                                                 if (cur) {
2075                                                         memset(cur, 0, sizeof(struct member));
2076                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
2077                                                         if ((tmp = strchr(cur->tech, ','))) {
2078                                                                 *tmp = '\0';
2079                                                                 tmp++;
2080                                                                 cur->penalty = atoi(tmp);
2081                                                                 if (cur->penalty < 0)
2082                                                                         cur->penalty = 0;
2083                                                         }
2084                                                         if ((tmp = strchr(cur->tech, '/')))
2085                                                                 *tmp = '\0';
2086                                                         if ((tmp = strchr(var->value, '/'))) {
2087                                                                 tmp++;
2088                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
2089                                                                 if ((tmp = strchr(cur->loc, ',')))
2090                                                                         *tmp = '\0';
2091                                                         } else
2092                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
2093                                                         if (prev)
2094                                                                 prev->next = cur;
2095                                                         else
2096                                                                 q->members = cur;
2097                                                         prev = cur;
2098                                                 }
2099                                         } else if (!strcasecmp(var->name, "music")) {
2100                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
2101                                         } else if (!strcasecmp(var->name, "announce")) {
2102                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
2103                                         } else if (!strcasecmp(var->name, "context")) {
2104                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
2105                                         } else if (!strcasecmp(var->name, "timeout")) {
2106                                                 q->timeout = atoi(var->value);
2107                                         } else if (!strcasecmp(var->name, "monitor-join")) {
2108                                                 q->monjoin = ast_true(var->value);
2109                                         } else if (!strcasecmp(var->name, "monitor-format")) {
2110                                                 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
2111                                         } else if (!strcasecmp(var->name, "queue-youarenext")) {
2112                                                 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
2113                                         } else if (!strcasecmp(var->name, "queue-thereare")) {
2114                                                 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
2115                                         } else if (!strcasecmp(var->name, "queue-callswaiting")) {
2116                                                 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
2117                                         } else if (!strcasecmp(var->name, "queue-holdtime")) {
2118                                                 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
2119                                         } else if (!strcasecmp(var->name, "queue-minutes")) {
2120                                                 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
2121                                         } else if (!strcasecmp(var->name, "queue-seconds")) {
2122                                                 strncpy(q->sound_seconds, var->value, sizeof(q->sound_seconds) - 1);
2123                                         } else if (!strcasecmp(var->name, "queue-lessthan")) {
2124                                                 strncpy(q->sound_lessthan, var->value, sizeof(q->sound_lessthan) - 1);
2125                                         } else if (!strcasecmp(var->name, "queue-thankyou")) {
2126                                                 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
2127                                         } else if (!strcasecmp(var->name, "queue-reporthold")) {
2128                                                 strncpy(q->sound_reporthold, var->value, sizeof(q->sound_reporthold) - 1);
2129                                         } else if (!strcasecmp(var->name, "announce-frequency")) {
2130                                                 q->announcefrequency = atoi(var->value);
2131                                         } else if (!strcasecmp(var->name, "announce-round-seconds")) {
2132                                                 q->roundingseconds = atoi(var->value);
2133                                                 if(q->roundingseconds>60 || q->roundingseconds<0) {
2134                                                         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);
2135                                                         q->roundingseconds=0;
2136                                                 }
2137                                         } else if (!strcasecmp(var->name, "announce-holdtime")) {
2138                                                 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
2139                                         } else if (!strcasecmp(var->name, "retry")) {
2140                                                 q->retry = atoi(var->value);
2141                                         } else if (!strcasecmp(var->name, "wrapuptime")) {
2142                                                 q->wrapuptime = atoi(var->value);
2143                                         } else if (!strcasecmp(var->name, "maxlen")) {
2144                                                 q->maxlen = atoi(var->value);
2145                                         } else if (!strcasecmp(var->name, "servicelevel")) {
2146                                                 q->servicelevel= atoi(var->value);
2147                                         } else if (!strcasecmp(var->name, "strategy")) {
2148                                                 q->strategy = strat2int(var->value);
2149                                                 if (q->strategy < 0) {
2150                                                         ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
2151                                                         q->strategy = 0;
2152                                                 }
2153                                         } else if (!strcasecmp(var->name, "joinempty")) {
2154                                                 q->joinempty = ast_true(var->value);
2155                                         } else if (!strcasecmp(var->name, "leavewhenempty")) {
2156                                                 q->leavewhenempty = ast_true(var->value);
2157                                         } else if (!strcasecmp(var->name, "eventwhencalled")) {
2158                                                 q->eventwhencalled = ast_true(var->value);
2159                                         } else if (!strcasecmp(var->name, "reportholdtime")) {
2160                                                 q->reportholdtime = ast_true(var->value);
2161                                         } else if (!strcasecmp(var->name, "memberdelay")) {
2162                                                 q->memberdelay = atoi(var->value);
2163                                         } else {
2164                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
2165                                         }
2166                                         var = var->next;
2167                                 }
2168                                 if (q->retry < 1)
2169                                         q->retry = DEFAULT_RETRY;
2170                                 if (q->timeout < 0)
2171                                         q->timeout = DEFAULT_TIMEOUT;
2172                                 if (q->maxlen < 0)
2173                                         q->maxlen = 0;
2174                                 if (!new) 
2175                                         ast_mutex_unlock(&q->lock);
2176                                 if (new) {
2177                                         q->next = queues;
2178                                         queues = q;
2179                                 }
2180                         }
2181                 }
2182                 cat = ast_category_browse(cfg, cat);
2183         }
2184         ast_destroy(cfg);
2185         q = queues;
2186         ql = NULL;
2187         while(q) {
2188                 qn = q->next;
2189                 if (q->dead) {
2190                         if (ql)
2191                                 ql->next = q->next;
2192                         else
2193                                 queues = q->next;
2194                         if (!q->count) {
2195                                 free(q);
2196                         } else
2197                                 ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
2198                 } else {
2199                         char tmp[256];
2200                         cur = q->members;
2201                         while(cur) {
2202                                 snprintf(tmp, sizeof(tmp), "%s/%s", cur->tech, cur->loc);
2203                                 cur->status = ast_device_state(tmp);
2204                                 cur = cur->next;
2205                         }
2206                         ql = q;
2207                 }
2208                 q = qn;
2209         }
2210         ast_mutex_unlock(&qlock);
2211 }
2212
2213 static char *status2str(int status, char *buf, int buflen)
2214 {
2215         switch(status) {
2216         case AST_DEVICE_UNKNOWN:
2217                 strncpy(buf, "unknown", buflen - 1);
2218                 break;
2219         case AST_DEVICE_NOT_INUSE:
2220                 strncpy(buf, "notinuse", buflen - 1);
2221                 break;
2222         case AST_DEVICE_INUSE:
2223                 strncpy(buf, "inuse", buflen - 1);
2224                 break;
2225         case AST_DEVICE_BUSY:
2226                 strncpy(buf, "busy", buflen - 1);
2227                 break;
2228         case AST_DEVICE_INVALID:
2229                 strncpy(buf, "invalid", buflen - 1);
2230                 break;
2231         case AST_DEVICE_UNAVAILABLE:
2232                 strncpy(buf, "unavailable", buflen - 1);
2233                 break;
2234         default:
2235                 snprintf(buf, buflen, "unknown status %d", status);
2236         }
2237         return buf;
2238 }
2239
2240 static int __queues_show(int fd, int argc, char **argv, int queue_show)
2241 {
2242         struct ast_call_queue *q;
2243         struct queue_ent *qe;
2244         struct member *mem;
2245         int pos;
2246         time_t now;
2247         char max[80] = "";
2248         char calls[80] = "";
2249         char tmpbuf[80] = "";
2250         float sl = 0;
2251
2252         time(&now);
2253         if ((!queue_show && argc != 2) || (queue_show && argc != 3))
2254                 return RESULT_SHOWUSAGE;
2255         ast_mutex_lock(&qlock);
2256         q = queues;
2257         if (!q) {       
2258                 ast_mutex_unlock(&qlock);
2259                 if (queue_show)
2260                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
2261                 else
2262                         ast_cli(fd, "No queues.\n");
2263                 return RESULT_SUCCESS;
2264         }
2265         while(q) {
2266                 ast_mutex_lock(&q->lock);
2267                 if (queue_show) {
2268                         if (strcasecmp(q->name, argv[2]) != 0) {
2269                                 ast_mutex_unlock(&q->lock);
2270                                 q = q->next;
2271                                 if (!q) {
2272                                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
2273                                         break;
2274                                 }
2275                                 continue;
2276                         }
2277                 }
2278                 if (q->maxlen)
2279                         snprintf(max, sizeof(max), "%d", q->maxlen);
2280                 else
2281                         strncpy(max, "unlimited", sizeof(max) - 1);
2282                 sl = 0;
2283                 if(q->callscompleted > 0)
2284                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
2285                 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",
2286                         q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
2287                 if (q->members) {
2288                         ast_cli(fd, "   Members: \n");
2289                         for (mem = q->members; mem; mem = mem->next) {
2290                                 if (mem->penalty)
2291                                         snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
2292                                 else
2293                                         max[0] = '\0';
2294                                 if (mem->dynamic)
2295                                         strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1);
2296                                 if (mem->status)
2297                                         snprintf(max + strlen(max), sizeof(max) - strlen(max), " (%s)", status2str(mem->status, tmpbuf, sizeof(tmpbuf)));
2298                                 if (mem->calls) {
2299                                         snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
2300                                                         mem->calls, (long)(time(NULL) - mem->lastcall));
2301                                 } else
2302                                         strncpy(calls, " has taken no calls yet", sizeof(calls) - 1);
2303                                 ast_cli(fd, "      %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
2304                         }
2305                 } else
2306                         ast_cli(fd, "   No Members\n");
2307                 if (q->head) {
2308                         pos = 1;
2309                         ast_cli(fd, "   Callers: \n");
2310                         for (qe = q->head; qe; qe = qe->next) 
2311                                 ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name,
2312                                                                 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio);
2313                 } else
2314                         ast_cli(fd, "   No Callers\n");
2315                 ast_cli(fd, "\n");
2316                 ast_mutex_unlock(&q->lock);
2317                 q = q->next;
2318                 if (queue_show)
2319                         break;
2320         }
2321         ast_mutex_unlock(&qlock);
2322         return RESULT_SUCCESS;
2323 }
2324
2325 static int queues_show(int fd, int argc, char **argv)
2326 {
2327         return __queues_show(fd, argc, argv, 0);
2328 }
2329
2330 static int queue_show(int fd, int argc, char **argv)
2331 {
2332         return __queues_show(fd, argc, argv, 1);
2333 }
2334
2335 static char *complete_queue(char *line, char *word, int pos, int state)
2336 {
2337         struct ast_call_queue *q;
2338         int which=0;
2339         
2340         ast_mutex_lock(&qlock);
2341         for (q = queues; q; q = q->next) {
2342                 if (!strncasecmp(word, q->name, strlen(word))) {
2343                         if (++which > state)
2344                                 break;
2345                 }
2346         }
2347         ast_mutex_unlock(&qlock);
2348         return q ? strdup(q->name) : NULL;
2349 }
2350
2351 /* JDG: callback to display queues status in manager */
2352 static int manager_queues_show( struct mansession *s, struct message *m )
2353 {
2354         char *a[] = { "show", "queues" };
2355         return queues_show( s->fd, 2, a );
2356 } /* /JDG */
2357
2358
2359 /* Dump queue status */
2360 static int manager_queues_status( struct mansession *s, struct message *m )
2361 {
2362         time_t now;
2363         int pos;
2364         char *id = astman_get_header(m,"ActionID");
2365         char idText[256] = "";
2366         struct ast_call_queue *q;
2367         struct queue_ent *qe;
2368         float sl = 0;
2369         struct member *mem;
2370         astman_send_ack(s, m, "Queue status will follow");
2371         time(&now);
2372         ast_mutex_lock(&qlock);
2373         if (!ast_strlen_zero(id)) {
2374                 snprintf(idText,256,"ActionID: %s\r\n",id);
2375         }
2376         for (q = queues; q; q = q->next) {
2377                 ast_mutex_lock(&q->lock);
2378
2379                 /* List queue properties */
2380                 if(q->callscompleted > 0)
2381                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
2382                 ast_mutex_lock(&s->lock);
2383                 ast_cli(s->fd, "Event: QueueParams\r\n"
2384                                         "Queue: %s\r\n"
2385                                         "Max: %d\r\n"
2386                                         "Calls: %d\r\n"
2387                                         "Holdtime: %d\r\n"
2388                                         "Completed: %d\r\n"
2389                                         "Abandoned: %d\r\n"
2390                                         "ServiceLevel: %d\r\n"
2391                                         "ServicelevelPerf: %2.1f\r\n"
2392                                         "%s"
2393                                         "\r\n",
2394                                                 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
2395                                                 q->callsabandoned, q->servicelevel, sl, idText);
2396
2397                 /* List Queue Members */
2398                 for (mem = q->members; mem; mem = mem->next) 
2399                         ast_cli(s->fd, "Event: QueueMember\r\n"
2400                                 "Queue: %s\r\n"
2401                                 "Location: %s/%s\r\n"
2402                                 "Membership: %s\r\n"
2403                                 "Penalty: %d\r\n"
2404                                 "CallsTaken: %d\r\n"
2405                                 "LastCall: %ld\r\n"
2406                                 "Status: %d\r\n"
2407                                 "%s"
2408                                 "\r\n",
2409                                         q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
2410                                         mem->penalty, mem->calls, mem->lastcall, mem->status, idText);
2411
2412                 /* List Queue Entries */
2413
2414                 pos = 1;
2415                 for (qe = q->head; qe; qe = qe->next) 
2416                         ast_cli(s->fd, "Event: QueueEntry\r\n"
2417                                 "Queue: %s\r\n"
2418                                 "Position: %d\r\n"
2419                                 "Channel: %s\r\n"
2420                                 "CallerID: %s\r\n"
2421                                 "CallerIDName: %s\r\n"
2422                                 "Wait: %ld\r\n"
2423                                 "%s"
2424                                 "\r\n", 
2425                                         q->name, pos++, qe->chan->name, 
2426                                         qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
2427                                         qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
2428                                         (long)(now - qe->start), idText);
2429                 ast_mutex_unlock(&s->lock);
2430                 ast_mutex_unlock(&q->lock);
2431         }
2432         ast_mutex_unlock(&qlock);
2433         return RESULT_SUCCESS;
2434 }
2435
2436 static int manager_add_queue_member(struct mansession *s, struct message *m)
2437 {
2438         char *queuename, *interface, *penalty_s;
2439         int penalty = 0;
2440
2441         queuename = astman_get_header(m, "Queue");
2442         interface = astman_get_header(m, "Interface");
2443         penalty_s = astman_get_header(m, "Penalty");
2444
2445         if (ast_strlen_zero(queuename)) {
2446                 astman_send_error(s, m, "'Queue' not specified.");
2447                 return 0;
2448         }
2449
2450         if (ast_strlen_zero(interface)) {
2451                 astman_send_error(s, m, "'Interface' not specified.");
2452                 return 0;
2453         }
2454
2455         if (ast_strlen_zero(penalty_s))
2456                 penalty = 0;
2457         else if (sscanf(penalty_s, "%d", &penalty) != 1) {
2458                 penalty = 0;
2459         }
2460
2461         switch (add_to_queue(queuename, interface, penalty)) {
2462         case RES_OKAY:
2463                 astman_send_ack(s, m, "Added interface to queue");
2464                 break;
2465         case RES_EXISTS:
2466                 astman_send_error(s, m, "Unable to add interface: Already there");
2467                 break;
2468         case RES_NOSUCHQUEUE:
2469                 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
2470                 break;
2471         case RES_OUTOFMEMORY:
2472                 astman_send_error(s, m, "Out of memory");
2473                 break;
2474         }
2475         return 0;
2476 }
2477
2478 static int manager_remove_queue_member(struct mansession *s, struct message *m)
2479 {
2480         char *queuename, *interface;
2481
2482         queuename = astman_get_header(m, "Queue");
2483         interface = astman_get_header(m, "Interface");
2484
2485         if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
2486                 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
2487                 return 0;
2488         }
2489
2490         switch (remove_from_queue(queuename, interface)) {
2491         case RES_OKAY:
2492                 astman_send_ack(s, m, "Removed interface from queue");
2493                 break;
2494         case RES_EXISTS:
2495                 astman_send_error(s, m, "Unable to remove interface: Not there");
2496                 break;
2497         case RES_NOSUCHQUEUE:
2498                 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
2499                 break;
2500         case RES_OUTOFMEMORY:
2501                 astman_send_error(s, m, "Out of memory");
2502                 break;
2503         }
2504         return 0;
2505 }
2506
2507 static int handle_add_queue_member(int fd, int argc, char *argv[])
2508 {
2509         char *queuename, *interface;
2510         int penalty;
2511
2512         if ((argc != 6) && (argc != 8)) {
2513                 return RESULT_SHOWUSAGE;
2514         } else if (strcmp(argv[4], "to")) {
2515                 return RESULT_SHOWUSAGE;
2516         } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
2517                 return RESULT_SHOWUSAGE;
2518         }
2519
2520         queuename = argv[5];
2521         interface = argv[3];
2522         if (argc == 8) {
2523                 if (sscanf(argv[7], "%d", &penalty) == 1) {
2524                         if (penalty < 0) {
2525                                 ast_cli(fd, "Penalty must be >= 0\n");
2526                                 penalty = 0;
2527                         }
2528                 } else {
2529                         ast_cli(fd, "Penalty must be an integer >= 0\n");
2530                         penalty = 0;
2531                 }
2532         } else {
2533                 penalty = 0;
2534         }
2535
2536         switch (add_to_queue(queuename, interface, penalty)) {
2537         case RES_OKAY:
2538                 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
2539                 return RESULT_SUCCESS;
2540         case RES_EXISTS:
2541                 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
2542                 return RESULT_FAILURE;
2543         case RES_NOSUCHQUEUE:
2544                 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
2545                 return RESULT_FAILURE;
2546         case RES_OUTOFMEMORY:
2547                 ast_cli(fd, "Out of memory\n");
2548                 return RESULT_FAILURE;
2549         default:
2550                 return RESULT_FAILURE;
2551         }
2552 }
2553
2554 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
2555 {
2556         /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
2557         switch (pos) {
2558         case 3:
2559                 /* Don't attempt to complete name of member (infinite possibilities) */
2560                 return NULL;
2561         case 4:
2562                 if (state == 0) {
2563                         return strdup("to");
2564                 } else {
2565                         return NULL;
2566                 }
2567         case 5:
2568                 /* No need to duplicate code */
2569                 return complete_queue(line, word, pos, state);
2570         case 6:
2571                 if (state == 0) {
2572                         return strdup("penalty");
2573                 } else {
2574                         return NULL;
2575                 }
2576         case 7:
2577                 if (state < 100) {      /* 0-99 */
2578                         char *num = malloc(3);
2579                         if (num) {
2580                                 sprintf(num, "%d", state);
2581                         }
2582                         return num;
2583                 } else {
2584                         return NULL;
2585                 }
2586         default:
2587                 return NULL;
2588         }
2589 }
2590
2591 static int handle_remove_queue_member(int fd, int argc, char *argv[])
2592 {
2593         char *queuename, *interface;
2594
2595         if (argc != 6) {
2596                 return RESULT_SHOWUSAGE;
2597         } else if (strcmp(argv[4], "from")) {
2598                 return RESULT_SHOWUSAGE;
2599         }
2600
2601         queuename = argv[5];
2602         interface = argv[3];
2603
2604         switch (remove_from_queue(queuename, interface)) {
2605         case RES_OKAY:
2606                 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
2607                 return RESULT_SUCCESS;
2608         case RES_EXISTS:
2609                 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
2610                 return RESULT_FAILURE;
2611         case RES_NOSUCHQUEUE:
2612                 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
2613                 return RESULT_FAILURE;
2614         case RES_OUTOFMEMORY:
2615                 ast_cli(fd, "Out of memory\n");
2616                 return RESULT_FAILURE;
2617         default:
2618                 return RESULT_FAILURE;
2619         }
2620 }
2621
2622 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
2623 {
2624         int which = 0;
2625         struct ast_call_queue *q;
2626         struct member *m;
2627
2628         /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
2629         if ((pos > 5) || (pos < 3)) {
2630                 return NULL;
2631         }
2632         if (pos == 4) {
2633                 if (state == 0) {
2634                         return strdup("from");
2635                 } else {
2636                         return NULL;
2637                 }
2638         }
2639
2640         if (pos == 5) {
2641                 /* No need to duplicate code */
2642                 return complete_queue(line, word, pos, state);
2643         }
2644
2645         if (queues != NULL) {
2646                 for (q = queues ; q ; q = q->next) {
2647                         ast_mutex_lock(&q->lock);
2648                         for (m = q->members ; m ; m = m->next) {
2649                                 if (++which > state) {
2650                                         char *tmp = malloc(strlen(m->tech) + strlen(m->loc) + 2);
2651                                         if (tmp) {
2652                                                 sprintf(tmp, "%s/%s", m->tech, m->loc);
2653                                         } else {
2654                                                 ast_log(LOG_ERROR, "Out of memory\n");
2655                                         }
2656                                         ast_mutex_unlock(&q->lock);
2657                                         return tmp;
2658                                 }
2659                         }
2660                         ast_mutex_unlock(&q->lock);
2661                 }
2662         }
2663         return NULL;
2664 }
2665
2666 static char show_queues_usage[] = 
2667 "Usage: show queues\n"
2668 "       Provides summary information on call queues.\n";
2669
2670 static struct ast_cli_entry cli_show_queues = {
2671         { "show", "queues", NULL }, queues_show, 
2672         "Show status of queues", show_queues_usage, NULL };
2673
2674 static char show_queue_usage[] = 
2675 "Usage: show queue\n"
2676 "       Provides summary information on a specified queue.\n";
2677
2678 static struct ast_cli_entry cli_show_queue = {
2679         { "show", "queue", NULL }, queue_show, 
2680         "Show status of a specified queue", show_queue_usage, complete_queue };
2681
2682 static char aqm_cmd_usage[] =
2683 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
2684
2685 static struct ast_cli_entry cli_add_queue_member = {
2686         { "add", "queue", "member", NULL }, handle_add_queue_member,
2687         "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
2688
2689 static char rqm_cmd_usage[] =
2690 "Usage: remove queue member <channel> from <queue>\n";
2691
2692 static struct ast_cli_entry cli_remove_queue_member = {
2693         { "remove", "queue", "member", NULL }, handle_remove_queue_member,
2694         "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
2695
2696 int unload_module(void)
2697 {
2698         STANDARD_HANGUP_LOCALUSERS;
2699         ast_cli_unregister(&cli_show_queue);
2700         ast_cli_unregister(&cli_show_queues);
2701         ast_cli_unregister(&cli_add_queue_member);
2702         ast_cli_unregister(&cli_remove_queue_member);
2703         ast_manager_unregister("Queues");
2704         ast_manager_unregister("QueueStatus");
2705         ast_manager_unregister("QueueAdd");
2706         ast_manager_unregister("QueueRemove");
2707         ast_devstate_del(statechange_queue, NULL);
2708         ast_unregister_application(app_aqm);
2709         ast_unregister_application(app_rqm);
2710         return ast_unregister_application(app);
2711 }
2712
2713 int load_module(void)
2714 {
2715         int res;
2716         res = ast_register_application(app, queue_exec, synopsis, descrip);
2717         if (!res) {
2718                 ast_cli_register(&cli_show_queue);
2719                 ast_cli_register(&cli_show_queues);
2720                 ast_cli_register(&cli_add_queue_member);
2721                 ast_cli_register(&cli_remove_queue_member);
2722                 ast_devstate_add(statechange_queue, NULL);
2723                 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
2724                 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
2725                 ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
2726                 ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
2727                 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
2728                 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
2729         }
2730         reload_queues();
2731         return res;
2732 }
2733
2734
2735 int reload(void)
2736 {
2737         reload_queues();
2738         return 0;
2739 }
2740
2741 char *description(void)
2742 {
2743         return tdesc;
2744 }
2745
2746 int usecount(void)
2747 {
2748         int res;
2749         STANDARD_USECOUNT(res);
2750         return res;
2751 }
2752
2753 char *key()
2754 {
2755         return ASTERISK_GPL_KEY;
2756 }