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