Flesh out remaining "rrmemory" logic
[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, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * These features added by David C. Troy <dave@toad.net>:
11  *    - Per-queue holdtime calculation
12  *    - Estimated holdtime announcement
13  *    - Position announcement
14  *    - Abandoned/completed call counters
15  *    - Failout timer passed as optional app parameter
16  *    - Optional monitoring of calls, started when call is answered
17  *
18  * Patch Version 1.07 2003-12-24 01
19  *
20  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
21  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
22  *
23  * Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
24  * by Matthew Enger <m.enger@xi.com.au>
25  *
26  * This program is free software, distributed under the terms of
27  * the GNU General Public License
28  */
29
30 #include <asterisk/lock.h>
31 #include <asterisk/file.h>
32 #include <asterisk/logger.h>
33 #include <asterisk/channel.h>
34 #include <asterisk/pbx.h>
35 #include <asterisk/options.h>
36 #include <asterisk/module.h>
37 #include <asterisk/translate.h>
38 #include <asterisk/say.h>
39 #include <asterisk/parking.h>
40 #include <asterisk/musiconhold.h>
41 #include <asterisk/cli.h>
42 #include <asterisk/manager.h>
43 #include <asterisk/config.h>
44 #include <asterisk/monitor.h>
45 #include <asterisk/utils.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <sys/time.h>
53 #include <sys/signal.h>
54 #include <netinet/in.h>
55
56 #include "../astconf.h"
57
58 #include <pthread.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 static char *tdesc = "True Call Queueing";
84
85 static char *app = "Queue";
86
87 static char *synopsis = "Queue a call for a call queue";
88
89 static char *descrip =
90 "  Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
91 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
92 "  This application returns -1 if the originating channel hangs up, or if the\n"
93 "call is bridged and  either of the parties in the bridge terminate the call.\n"
94 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
95 "The option string may contain zero or more of the following characters:\n"
96 "      't' -- allow the called user transfer the calling user\n"
97 "      'T' -- to allow the calling user to transfer the call.\n"
98 "      'd' -- data-quality (modem) call (minimum delay).\n"
99 "      'H' -- allow caller to hang up by hitting *.\n"
100 "      'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
101 "      'r' -- ring instead of playing MOH\n"
102 "  In addition to transferring the call, a call may be parked and then picked\n"
103 "up by another user.\n"
104 "  The optional URL will be sent to the called party if the channel supports\n"
105 "it.\n"
106 "  The timeout will cause the queue to fail out after a specified number of\n"
107 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
108
109 // [PHM 06/26/03]
110 static char *app_aqm = "AddQueueMember" ;
111 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
112 static char *app_aqm_descrip =
113 "   AddQueueMember(queuename[|interface]):\n"
114 "Dynamically adds interface to an existing queue.\n"
115 "If the interface is already in the queue and there exists an n+101 priority\n"
116 "then it will then jump to this priority.  Otherwise it will return an error\n"
117 "Returns -1 if there is an error.\n"
118 "Example: AddQueueMember(techsupport|SIP/3000)\n"
119 "";
120
121 static char *app_rqm = "RemoveQueueMember" ;
122 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
123 static char *app_rqm_descrip =
124 "   RemoveQueueMember(queuename[|interface]):\n"
125 "Dynamically removes interface to an existing queue\n"
126 "If the interface is NOT in the queue and there exists an n+101 priority\n"
127 "then it will then jump to this priority.  Otherwise it will return an error\n"
128 "Returns -1 if there is an error.\n"
129 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
130 "";
131
132 /* We define a customer "local user" structure because we
133    use it not only for keeping track of what is in use but
134    also for keeping track of who we're dialing. */
135
136 struct localuser {
137         struct ast_channel *chan;
138         char numsubst[256];
139         char tech[40];
140         int stillgoing;
141         int metric;
142         int allowredirect_in;
143         int allowredirect_out;
144         int ringbackonly;
145         int musiconhold;
146         int dataquality;
147         int allowdisconnect;
148         struct member *member;
149         struct localuser *next;
150 };
151
152 LOCAL_USER_DECL;
153
154 struct queue_ent {
155         struct ast_call_queue *parent;  /* What queue is our parent */
156         char moh[80];                           /* Name of musiconhold to be used */
157         char announce[80];              /* Announcement to play for member when call is answered */
158         char context[80];               /* Context when user exits queue */
159         int pos;                                        /* Where we are in the queue */
160         int last_pos_said;              /* Last position we told the user */
161         time_t last_pos;                /* Last time we told the user their position */
162         int opos;                                       /* Where we started in the queue */
163         int handled;                            /* Whether our call was handled */
164         time_t start;                           /* When we started holding */
165         int queuetimeout;               /* How many seconds before timing out of queue */
166         struct ast_channel *chan;       /* Our channel */
167         struct queue_ent *next;         /* The next queue entry */
168 };
169
170 struct member {
171         char tech[80];                          /* Technology */
172         char loc[256];                          /* Location */
173         int penalty;                            /* Are we a last resort? */
174         int calls;
175         int dynamic;                            /* Are we dynamically added? */
176         time_t lastcall;        /* When last successful call was hungup */
177         struct member *next;            /* Next member */
178 };
179
180 struct ast_call_queue {
181         ast_mutex_t     lock;   
182         char name[80];                  /* Name of the queue */
183         char moh[80];                   /* Name of musiconhold to be used */
184         char announce[80];              /* Announcement to play when call is answered */
185         char context[80];               /* Context for this queue */
186         int strategy;                   /* Queueing strategy */
187         int announcefrequency;          /* How often to announce their position */
188         int announceholdtime;           /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
189         int holdtime;                   /* Current avg holdtime for this queue, based on recursive boxcar filter */
190         int callscompleted;             /* Number of queue calls completed */
191         int callsabandoned;             /* Number of queue calls abandoned */
192         int servicelevel;               /* seconds setting for servicelevel*/
193         int callscompletedinsl;         /* Number of queue calls answererd with servicelevel*/
194         char monfmt[8];                 /* Format to use when recording calls */
195         char sound_next[80];            /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
196         char sound_thereare[80];        /* Sound file: "There are currently" (def. queue-thereare) */
197         char sound_calls[80];           /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
198         char sound_holdtime[80];        /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
199         char sound_minutes[80];         /* Sound file: "minutes." (def. queue-minutes) */
200         char sound_thanks[80];          /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
201
202         int count;                              /* How many entries are in the queue */
203         int maxlen;                             /* Max number of entries in queue */
204
205         int dead;                               /* Whether this queue is dead or not */
206         int retry;                              /* Retry calling everyone after this amount of time */
207         int timeout;                    /* How long to wait for an answer */
208         
209         /* Queue strategy things */
210         
211         int rrpos;                              /* Round Robin - position */
212         int wrapped;                    /* Round Robin - wrapped around? */
213
214         struct member *members; /* Member channels to be tried */
215         struct queue_ent *head; /* Start of the actual queue */
216         struct ast_call_queue *next;    /* Next call queue */
217 };
218
219 static struct ast_call_queue *queues = NULL;
220 static ast_mutex_t qlock = AST_MUTEX_INITIALIZER;
221
222 static char *int2strat(int strategy)
223 {
224         int x;
225         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
226                 if (strategy == strategies[x].strategy)
227                         return strategies[x].name;
228         }
229         return "<unknown>";
230 }
231
232 static int strat2int(char *strategy)
233 {
234         int x;
235         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
236                 if (!strcasecmp(strategy, strategies[x].name))
237                         return strategies[x].strategy;
238         }
239         return -1;
240 }
241
242 static int join_queue(char *queuename, struct queue_ent *qe)
243 {
244         struct ast_call_queue *q;
245         struct queue_ent *cur, *prev = NULL;
246         int res = -1;
247         int pos = 0;
248         ast_mutex_lock(&qlock);
249         q = queues;
250         while(q) {
251                 if (!strcasecmp(q->name, queuename)) {
252                         /* This is our one */
253                         ast_mutex_lock(&q->lock);
254                         if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
255                                 /* There's space for us, put us at the end */
256                                 prev = NULL;
257                                 cur = q->head;
258                                 while(cur) {
259                                         cur->pos = ++pos;
260                                         prev = cur;
261                                         cur = cur->next;
262                                 }
263                                 if (prev)
264                                         prev->next = qe;
265                                 else
266                                         q->head = qe;
267                                 /* Fix additional pointers and
268                                   information  */
269                                 qe->next = NULL;
270                                 qe->parent = q;
271                                 qe->pos = ++pos;
272                                 qe->opos = pos;
273                                 strncpy(qe->moh, q->moh, sizeof(qe->moh));
274                                 strncpy(qe->announce, q->announce, sizeof(qe->announce));
275                                 strncpy(qe->context, q->context, sizeof(qe->context));
276                                 q->count++;
277                                 res = 0;
278                                 manager_event(EVENT_FLAG_CALL, "Join", 
279                                                                 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
280                                                                 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count );
281 #if 0
282 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
283 #endif
284                         }
285                         ast_mutex_unlock(&q->lock);
286                         break;
287                 }
288                 q = q->next;
289         }
290         ast_mutex_unlock(&qlock);
291         return res;
292 }
293
294 static void free_members(struct ast_call_queue *q, int all)
295 {
296         /* Free non-dynamic members */
297         struct member *curm, *next, *prev;
298         curm = q->members;
299         prev = NULL;
300         while(curm) {
301                 next = curm->next;
302                 if (all || !curm->dynamic) {
303                         if (prev)
304                                 prev->next = next;
305                         else
306                                 q->members = next;
307                         free(curm);
308                 } else 
309                         prev = curm;
310                 curm = next;
311         }
312 }
313
314 static void destroy_queue(struct ast_call_queue *q)
315 {
316         struct ast_call_queue *cur, *prev = NULL;
317         ast_mutex_lock(&qlock);
318         cur = queues;
319         while(cur) {
320                 if (cur == q) {
321                         if (prev)
322                                 prev->next = cur->next;
323                         else
324                                 queues = cur->next;
325                 } else {
326                         prev = cur;
327                 }
328                 cur = cur->next;
329         }
330         ast_mutex_unlock(&qlock);
331         free_members(q, 1);
332         free(q);
333 }
334
335 static int play_file(struct ast_channel *chan, char *filename)
336 {
337         int res;
338
339         ast_stopstream(chan);
340         res = ast_streamfile(chan, filename, chan->language);
341
342         if (!res)
343                 res = ast_waitstream(chan, "");
344         else
345                 res = 0;
346
347         if (res) {
348                 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
349                 res = 0;
350         }
351         ast_stopstream(chan);
352
353         return res;
354 }
355
356 static int say_position(struct queue_ent *qe)
357 {
358         int res = 0, avgholdmins;
359         time_t now;
360
361         /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
362         time(&now);
363         if ( (now - qe->last_pos) < 15 )
364                 return -1;
365
366         /* If either our position has changed, or we are over the freq timer, say position */
367         if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
368                 return -1;
369
370         ast_moh_stop(qe->chan);
371         /* Say we're next, if we are */
372         if (qe->pos == 1) {
373                 res += play_file(qe->chan, qe->parent->sound_next);
374                 goto posout;
375         } else {
376                 res += play_file(qe->chan, qe->parent->sound_thereare);
377                 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
378                 res += play_file(qe->chan, qe->parent->sound_calls);
379         }
380
381         /* Round hold time to nearest minute */
382         avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60;
383         if (option_verbose > 2)
384                 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins);
385
386         /* If the hold time is >1 min, if it's enabled, and if it's not
387            supposed to be only once and we have already said it, say it */
388         if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
389                 res += play_file(qe->chan, qe->parent->sound_holdtime);
390                 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
391                 res += play_file(qe->chan, qe->parent->sound_minutes);
392         }
393
394         posout:
395         /* Set our last_pos indicators */
396         qe->last_pos = now;
397         qe->last_pos_said = qe->pos;
398
399         if (option_verbose > 2)
400                 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
401         res += play_file(qe->chan, qe->parent->sound_thanks);
402         ast_moh_start(qe->chan, qe->moh);
403
404         return (res>0);
405 }
406
407 static void record_abandoned(struct queue_ent *qe)
408 {
409         ast_mutex_lock(&qe->parent->lock);
410         qe->parent->callsabandoned++;
411         ast_mutex_unlock(&qe->parent->lock);
412 }
413
414 static void recalc_holdtime(struct queue_ent *qe)
415 {
416         int oldvalue, newvalue;
417
418         /* Calculate holdtime using a recursive boxcar filter */
419         /* Thanks to SRT for this contribution */
420         /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
421
422         newvalue = time(NULL) - qe->start;
423
424         ast_mutex_lock(&qe->parent->lock);
425         if (newvalue <= qe->parent->servicelevel)
426                 qe->parent->callscompletedinsl++;
427         oldvalue = qe->parent->holdtime;
428         qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
429         ast_mutex_unlock(&qe->parent->lock);
430 }
431
432
433 static void leave_queue(struct queue_ent *qe)
434 {
435         struct ast_call_queue *q;
436         struct queue_ent *cur, *prev = NULL;
437         int pos = 0;
438         q = qe->parent;
439         if (!q)
440                 return;
441         ast_mutex_lock(&q->lock);
442
443         prev = NULL;
444         cur = q->head;
445         while(cur) {
446                 if (cur == qe) {
447                         q->count--;
448
449                         /* Take us out of the queue */
450                         manager_event(EVENT_FLAG_CALL, "Leave",
451                                  "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
452                                  qe->chan->name, q->name,  q->count);
453 #if 0
454 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
455 #endif
456                         /* Take us out of the queue */
457                         if (prev)
458                                 prev->next = cur->next;
459                         else
460                                 q->head = cur->next;
461                 } else {
462                         /* Renumber the people after us in the queue based on a new count */
463                         cur->pos = ++pos;
464                         prev = cur;
465                 }
466                 cur = cur->next;
467         }
468         ast_mutex_unlock(&q->lock);
469         if (q->dead && !q->count) {     
470                 /* It's dead and nobody is in it, so kill it */
471                 destroy_queue(q);
472         }
473 }
474
475 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
476 {
477         /* Hang up a tree of stuff */
478         struct localuser *oo;
479         while(outgoing) {
480                 /* Hangup any existing lines we have open */
481                 if (outgoing->chan && (outgoing->chan != exception))
482                         ast_hangup(outgoing->chan);
483                 oo = outgoing;
484                 outgoing=outgoing->next;
485                 free(oo);
486         }
487 }
488
489 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
490 {
491         int res;
492         /* Request the peer */
493         tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
494         if (!tmp->chan) {                       /* If we can't, just go on to the next call */
495 #if 0
496                 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
497 #endif                  
498                 if (qe->chan->cdr)
499                         ast_cdr_busy(qe->chan->cdr);
500                 tmp->stillgoing = 0;
501                 return 0;
502         }
503         tmp->chan->appl = "AppQueue";
504         tmp->chan->data = "(Outgoing Line)";
505         tmp->chan->whentohangup = 0;
506         if (tmp->chan->callerid)
507                 free(tmp->chan->callerid);
508         if (tmp->chan->ani)
509                 free(tmp->chan->ani);
510         if (qe->chan->callerid)
511                 tmp->chan->callerid = strdup(qe->chan->callerid);
512         else
513                 tmp->chan->callerid = NULL;
514         if (qe->chan->ani)
515                 tmp->chan->ani = strdup(qe->chan->ani);
516         else
517                 tmp->chan->ani = NULL;
518         /* Presense of ADSI CPE on outgoing channel follows ours */
519         tmp->chan->adsicpe = qe->chan->adsicpe;
520         /* Place the call, but don't wait on the answer */
521         res = ast_call(tmp->chan, tmp->numsubst, 0);
522         if (res) {
523                 /* Again, keep going even if there's an error */
524                 if (option_debug)
525                         ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
526                 else if (option_verbose > 2)
527                         ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
528                 ast_hangup(tmp->chan);
529                 tmp->chan = NULL;
530                 tmp->stillgoing = 0;
531                 return 0;
532         } else
533                 if (option_verbose > 2)
534                         ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
535         return 0;
536 }
537
538 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
539 {
540         struct localuser *cur;
541         struct localuser *best;
542         int bestmetric=0;
543         do {
544                 best = NULL;
545                 cur = outgoing;
546                 while(cur) {
547                         if (cur->stillgoing &&                                                  /* Not already done */
548                                 !cur->chan &&                                                           /* Isn't already going */
549                                 (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
550                                         bestmetric = cur->metric;
551                                         best = cur;
552                         }
553                         cur = cur->next;
554                 }
555                 if (best) {
556                         if (!qe->parent->strategy) {
557                                 /* Ring everyone who shares this best metric (for ringall) */
558                                 cur = outgoing;
559                                 while(cur) {
560                                         if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
561                                                 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
562                                                 ring_entry(qe, cur);
563                                         }
564                                         cur = cur->next;
565                                 }
566                         } else {
567                                 /* Ring just the best channel */
568                                 ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
569                                 ring_entry(qe, best);
570                         }
571                 }
572         } while (best && !best->chan);
573         if (!best) {
574                 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
575                 return 0;
576         }
577         return 1;
578 }
579
580 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
581 {
582         struct localuser *cur;
583         struct localuser *best;
584         int bestmetric=0;
585         best = NULL;
586         cur = outgoing;
587         while(cur) {
588                 if (cur->stillgoing &&                                                  /* Not already done */
589                         !cur->chan &&                                                           /* Isn't already going */
590                         (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
591                                 bestmetric = cur->metric;
592                                 best = cur;
593                 }
594                 cur = cur->next;
595         }
596         if (best) {
597                 /* Ring just the best channel */
598                 ast_log(LOG_DEBUG, "Next is '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
599                 qe->parent->rrpos = best->metric % 1000;
600         } else {
601                 /* Just increment rrpos */
602                 if (!qe->parent->wrapped) {
603                         /* No more channels, start over */
604                         qe->parent->rrpos = 0;
605                 } else {
606                         /* Prioritize next entry */
607                         qe->parent->rrpos++;
608                 }
609         }
610         qe->parent->wrapped = 0;
611         return 0;
612 }
613
614 static int valid_exit(struct queue_ent *qe, char digit)
615 {
616         char tmp[2];
617         if (ast_strlen_zero(qe->context))
618                 return 0;
619         tmp[0] = digit;
620         tmp[1] = '\0';
621         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
622                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
623                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
624                 qe->chan->priority = 0;
625                 return 1;
626         }
627         return 0;
628 }
629
630 #define MAX 256
631
632 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *digit)
633 {
634         char *queue = qe->parent->name;
635         struct localuser *o;
636         int found;
637         int numlines;
638         int sentringing = 0;
639         int numbusies = 0;
640         int orig = *to;
641         struct ast_frame *f;
642         struct localuser *peer = NULL;
643         struct ast_channel *watchers[MAX];
644         int pos;
645         struct ast_channel *winner;
646         struct ast_channel *in = qe->chan;
647         
648         while(*to && !peer) {
649                 o = outgoing;
650                 found = -1;
651                 pos = 1;
652                 numlines = 0;
653                 watchers[0] = in;
654                 while(o) {
655                         /* Keep track of important channels */
656                         if (o->stillgoing && o->chan) {
657                                 watchers[pos++] = o->chan;
658                                 found = 1;
659                         }
660                         o = o->next;
661                         numlines++;
662                 }
663                 if (found < 0) {
664                         if (numlines == numbusies) {
665                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
666                         } else {
667                                 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
668                         }
669                         *to = 0;
670                         return NULL;
671                 }
672                 winner = ast_waitfor_n(watchers, pos, to);
673                 o = outgoing;
674                 while(o) {
675                         if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
676                                 if (!peer) {
677                                         if (option_verbose > 2)
678                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
679                                         peer = o;
680                                         *allowredir_in = o->allowredirect_in;
681                                         *allowredir_out = o->allowredirect_out;
682                                         *allowdisconnect = o->allowdisconnect;
683                                 }
684                         } else if (o->chan && (o->chan == winner)) {
685                                 f = ast_read(winner);
686                                 if (f) {
687                                         if (f->frametype == AST_FRAME_CONTROL) {
688                                                 switch(f->subclass) {
689                                             case AST_CONTROL_ANSWER:
690                                                         /* This is our guy if someone answered. */
691                                                         if (!peer) {
692                                                                 if (option_verbose > 2)
693                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
694                                                                 peer = o;
695                                                                 *allowredir_in = o->allowredirect_in;
696                                                                 *allowredir_out = o->allowredirect_out;
697                                                                 *allowdisconnect = o->allowdisconnect;
698                                                         }
699                                                         break;
700                                                 case AST_CONTROL_BUSY:
701                                                         if (option_verbose > 2)
702                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
703                                                         o->stillgoing = 0;
704                                                         if (in->cdr)
705                                                                 ast_cdr_busy(in->cdr);
706                                                         ast_hangup(o->chan);
707                                                         o->chan = NULL;
708                                                         if (qe->parent->strategy)
709                                                                 ring_one(qe, outgoing);
710                                                         numbusies++;
711                                                         break;
712                                                 case AST_CONTROL_CONGESTION:
713                                                         if (option_verbose > 2)
714                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
715                                                         o->stillgoing = 0;
716                                                         if (in->cdr)
717                                                                 ast_cdr_busy(in->cdr);
718                                                         ast_hangup(o->chan);
719                                                         o->chan = NULL;
720                                                         if (qe->parent->strategy)
721                                                                 ring_one(qe, outgoing);
722                                                         numbusies++;
723                                                         break;
724                                                 case AST_CONTROL_RINGING:
725                                                         if (option_verbose > 2)
726                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
727                                                         if (!sentringing) {
728 #if 0
729                                                                 ast_indicate(in, AST_CONTROL_RINGING);
730 #endif                                                          
731                                                                 sentringing++;
732                                                         }
733                                                         break;
734                                                 case AST_CONTROL_OFFHOOK:
735                                                         /* Ignore going off hook */
736                                                         break;
737                                                 default:
738                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
739                                                 }
740                                         }
741                                         ast_frfree(f);
742                                 } else {
743                                         o->stillgoing = 0;
744                                         ast_hangup(o->chan);
745                                         o->chan = NULL;
746                                         if (qe->parent->strategy)
747                                                 ring_one(qe, outgoing);
748                                 }
749                         }
750                         o = o->next;
751                 }
752                 if (winner == in) {
753                         f = ast_read(in);
754 #if 0
755                         if (f && (f->frametype != AST_FRAME_VOICE))
756                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
757                         else if (!f || (f->frametype != AST_FRAME_VOICE))
758                                 printf("Hangup received on %s\n", in->name);
759 #endif
760                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
761                                 /* Got hung up */
762                                 *to=-1;
763                                 return NULL;
764                         }
765                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
766                             if (option_verbose > 3)
767                                         ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
768                                 *to=0;
769                                 return NULL;
770                         }
771                         if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
772                                 if (option_verbose > 3)
773                                         ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
774                                 *to=0;
775                                 *digit=f->subclass;
776                                 return NULL;
777                         }
778                 }
779                 if (!*to && (option_verbose > 2))
780                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
781         }
782
783         return peer;
784         
785 }
786
787 static int wait_our_turn(struct queue_ent *qe, int ringing)
788 {
789         struct queue_ent *ch;
790         int res = 0;
791         time_t now;
792
793         /* This is the holding pen for callers 2 through maxlen */
794         for (;;) {
795                 /* Atomically read the parent head -- does not need a lock */
796                 ch = qe->parent->head;
797
798                 /* If we are now at the top of the head, break out */
799                 if (qe == ch)
800                         break;
801
802                 /* If we have timed out, break out */
803                 if ( qe->queuetimeout ) {
804                         time(&now);
805                         if ( (now - qe->start) >= qe->queuetimeout )
806                         break;
807                 }
808
809                 /* Make a position announcement, if enabled */
810                 if (qe->parent->announcefrequency && !ringing)
811                         say_position(qe);
812
813
814                 /* Wait a second before checking again */
815                 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
816                 if (res)
817                         break;
818         }
819         return res;
820 }
821
822 static int update_queue(struct ast_call_queue *q, struct localuser *user)
823 {
824         struct member *cur;
825         /* Since a reload could have taken place, we have to traverse the list to
826                 be sure it's still valid */
827         ast_mutex_lock(&q->lock);
828         cur = q->members;
829         while(cur) {
830                 if (user->member == cur) {
831                         time(&cur->lastcall);
832                         cur->calls++;
833                         break;
834                 }
835                 cur = cur->next;
836         }
837         q->callscompleted++;
838         ast_mutex_unlock(&q->lock);
839         return 0;
840 }
841
842 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
843 {
844         switch (q->strategy) {
845         case QUEUE_STRATEGY_RINGALL:
846                 /* Everyone equal, except for penalty */
847                 tmp->metric = mem->penalty * 1000000;
848                 break;
849         case QUEUE_STRATEGY_ROUNDROBIN:
850                 if (!pos) {
851                         if (!q->wrapped) {
852                                 /* No more channels, start over */
853                                 q->rrpos = 0;
854                         } else {
855                                 /* Prioritize next entry */
856                                 q->rrpos++;
857                         }
858                         q->wrapped = 0;
859                 }
860                 /* Fall through */
861         case QUEUE_STRATEGY_RRMEMORY:
862                 if (pos < q->rrpos) {
863                         tmp->metric = 1000 + pos;
864                 } else {
865                         if (pos > q->rrpos) {
866                                 /* Indicate there is another priority */
867                                 q->wrapped = 1;
868                         }
869                         tmp->metric = pos;
870                 }
871                 tmp->metric += mem->penalty * 1000000;
872                 break;
873         case QUEUE_STRATEGY_RANDOM:
874                 tmp->metric = rand() % 1000;
875                 tmp->metric += mem->penalty * 1000000;
876                 break;
877         case QUEUE_STRATEGY_FEWESTCALLS:
878                 tmp->metric = mem->calls;
879                 tmp->metric += mem->penalty * 1000000;
880                 break;
881         case QUEUE_STRATEGY_LEASTRECENT:
882                 if (!mem->lastcall)
883                         tmp->metric = 0;
884                 else
885                         tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
886                 tmp->metric += mem->penalty * 1000000;
887                 break;
888         default:
889                 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
890                 break;
891         }
892         return 0;
893 }
894
895 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
896 {
897         struct member *cur;
898         struct localuser *outgoing=NULL, *tmp = NULL;
899         int to;
900         int allowredir_in=0;
901         int allowredir_out=0;
902         int allowdisconnect=0;
903         char restofit[AST_MAX_EXTENSION];
904         char oldexten[AST_MAX_EXTENSION]="";
905         char oldcontext[AST_MAX_EXTENSION]="";
906         char queuename[256]="";
907         char *newnum;
908         struct ast_channel *peer;
909         struct localuser *lpeer;
910         int res = 0, bridge = 0;
911         int zapx = 2;
912         int x=0;
913         char *announce = NULL;
914         char digit = 0;
915         time_t callstart;
916         time_t now;
917         struct ast_bridge_config config;
918         /* Hold the lock while we setup the outgoing calls */
919         ast_mutex_lock(&qe->parent->lock);
920         strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
921         time(&now);
922         cur = qe->parent->members;
923         if (!ast_strlen_zero(qe->announce))
924                 announce = qe->announce;
925         if (announceoverride && !ast_strlen_zero(announceoverride))
926                 announce = announceoverride;
927         while(cur) {
928                 /* Get a technology/[device:]number pair */
929                 tmp = malloc(sizeof(struct localuser));
930                 if (!tmp) {
931                         ast_mutex_unlock(&qe->parent->lock);
932                         ast_log(LOG_WARNING, "Out of memory\n");
933                         goto out;
934                 }
935                 memset(tmp, 0, sizeof(struct localuser));
936                 tmp->stillgoing = -1;
937                 if (options) {
938                         if (strchr(options, 't'))
939                                 tmp->allowredirect_in = 1;
940                         if (strchr(options, 'T'))
941                                 tmp->allowredirect_out = 1;
942                         if (strchr(options, 'r'))
943                                 tmp->ringbackonly = 1;
944                         if (strchr(options, 'm'))
945                                 tmp->musiconhold = 1;
946                         if (strchr(options, 'd'))
947                                 tmp->dataquality = 1;
948                         if (strchr(options, 'H'))
949                                 tmp->allowdisconnect = 1;
950                         if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout))
951                                 *go_on = 1;
952                 }
953                 if (url) {
954                         ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
955                 } else 
956                         ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
957
958                 tmp->member = cur;              /* Never directly dereference!  Could change on reload */
959                 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
960                 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
961                 /* If we're dialing by extension, look at the extension to know what to dial */
962                 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
963                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
964                         snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
965                         if (option_debug)
966                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
967                 }
968                 /* Special case: If we ring everyone, go ahead and ring them, otherwise
969                    just calculate their metric for the appropriate strategy */
970                 calc_metric(qe->parent, cur, x++, qe, tmp);
971                 /* Put them in the list of outgoing thingies...  We're ready now. 
972                    XXX If we're forcibly removed, these outgoing calls won't get
973                    hung up XXX */
974                 tmp->next = outgoing;
975                 outgoing = tmp;         
976                 /* If this line is up, don't try anybody else */
977                 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
978                         break;
979
980                 cur = cur->next;
981         }
982         if (qe->parent->timeout)
983                 to = qe->parent->timeout * 1000;
984         else
985                 to = -1;
986         ring_one(qe, outgoing);
987         ast_mutex_unlock(&qe->parent->lock);
988         lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
989         ast_mutex_lock(&qe->parent->lock);
990         if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
991                 store_next(qe, outgoing);
992         }
993         ast_mutex_unlock(&qe->parent->lock);
994         if (lpeer)
995                 peer = lpeer->chan;
996         else
997                 peer = NULL;
998         if (!peer) {
999                 if (to) {
1000                         /* Musta gotten hung up */
1001                         record_abandoned(qe);
1002                         res = -1;
1003                 } else {
1004                         if (digit && valid_exit(qe, digit))
1005                                 res=digit;
1006                         else
1007                                 /* Nobody answered, next please? */
1008                                 res=0;
1009                 }
1010                 goto out;
1011         }
1012         if (peer) {
1013                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
1014                    we will always return with -1 so that it is hung up properly after the 
1015                    conversation.  */
1016                 qe->handled++;
1017                 if (!strcmp(qe->chan->type,"Zap")) {
1018                         if (tmp->dataquality) zapx = 0;
1019                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1020                 }                       
1021                 if (!strcmp(peer->type,"Zap")) {
1022                         if (tmp->dataquality) zapx = 0;
1023                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1024                 }
1025                 /* Update parameters for the queue */
1026                 recalc_holdtime(qe);
1027                 update_queue(qe->parent, lpeer);
1028                 hanguptree(outgoing, peer);
1029                 outgoing = NULL;
1030                 if (announce) {
1031                         int res2;
1032                         res2 = ast_autoservice_start(qe->chan);
1033                         if (!res2) {
1034                                 res2 = ast_streamfile(peer, announce, peer->language);
1035                                 if (!res2)
1036                                         res2 = ast_waitstream(peer, "");
1037                                 else {
1038                                         ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
1039                                         res2 = 0;
1040                                 }
1041                         }
1042                         res2 |= ast_autoservice_stop(qe->chan);
1043                         if (res2) {
1044                                 /* Agent must have hung up */
1045                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
1046                                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
1047                                 ast_hangup(peer);
1048                                 return -1;
1049                         }
1050                 }
1051                 /* Stop music on hold */
1052                 ast_moh_stop(qe->chan);
1053                 /* If appropriate, log that we have a destination channel */
1054                 if (qe->chan->cdr)
1055                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
1056                 /* Make sure channels are compatible */
1057                 res = ast_channel_make_compatible(qe->chan, peer);
1058                 if (res < 0) {
1059                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1060                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1061                         ast_hangup(peer);
1062                         return -1;
1063                 }
1064                 /* Begin Monitoring */
1065                 if (qe->parent->monfmt && *qe->parent->monfmt) {
1066                         ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1067                 }
1068                 /* Drop out of the queue at this point, to prepare for next caller */
1069                 leave_queue(qe);                        
1070                 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
1071                         ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1072                         ast_channel_sendurl( peer, url );
1073                 }
1074                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1075                 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1076                 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1077                 time(&callstart);
1078
1079                 memset(&config,0,sizeof(struct ast_bridge_config));
1080         config.allowredirect_in = allowredir_in;
1081         config.allowredirect_out = allowredir_out;
1082         config.allowdisconnect = allowdisconnect;
1083         bridge = ast_bridge_call(qe->chan,peer,&config);
1084
1085                 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1086                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1087                 } else if (qe->chan->_softhangup) {
1088                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1089                 } else {
1090                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1091                 }
1092
1093                 if(bridge != AST_PBX_NO_HANGUP_PEER)
1094                         ast_hangup(peer);
1095
1096                 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1097                 else res = bridge; /* bridge error, stay in the queue */
1098         }       
1099 out:
1100         hanguptree(outgoing, NULL);
1101         return res;
1102 }
1103
1104 static int wait_a_bit(struct queue_ent *qe)
1105 {
1106         /* Don't need to hold the lock while we setup the outgoing calls */
1107         int retrywait = qe->parent->retry * 1000;
1108         return ast_waitfordigit(qe->chan, retrywait);
1109 }
1110
1111 // [PHM 06/26/03]
1112
1113 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
1114 {
1115         struct member * ret = NULL ;
1116         struct member *mem;
1117         char buf[500] ;
1118
1119         if( q != NULL )
1120         {
1121                 mem = q->members ;
1122
1123                 while( mem != NULL ) {
1124                         sprintf( buf, "%s/%s", mem->tech, mem->loc);
1125
1126                         if( strcmp( buf, interface ) == 0 ) {
1127                                 ret = mem ;
1128                                 break ;
1129                         }
1130                         else
1131                                 mem = mem->next ;
1132                 }
1133         }
1134
1135         return( ret ) ;
1136 }
1137
1138
1139 static struct member * create_queue_node( char * interface )
1140 {
1141         struct member * cur ;
1142         char * tmp ;
1143         
1144         /* Add a new member */
1145
1146         cur = malloc(sizeof(struct member));
1147
1148         if (cur) {
1149                 memset(cur, 0, sizeof(struct member));
1150                 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
1151                 if ((tmp = strchr(cur->tech, '/')))
1152                         *tmp = '\0';
1153                 if ((tmp = strchr(interface, '/'))) {
1154                         tmp++;
1155                         strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1156                 } else
1157                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1158         }
1159
1160         return( cur ) ;
1161 }
1162
1163
1164 static int rqm_exec(struct ast_channel *chan, void *data)
1165 {
1166         int res=-1;
1167         struct localuser *u;
1168         char *queuename;
1169         struct member * node ;
1170         struct member * look ;
1171         char info[512];
1172         char tmpchan[256]="";
1173         char *interface=NULL;
1174         struct ast_call_queue *q;
1175         int found=0 ;
1176
1177         if (!data) {
1178                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
1179                 return -1;
1180         }
1181         
1182         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1183         
1184         /* Parse our arguments XXX Check for failure XXX */
1185         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1186         queuename = info;
1187         if (queuename) {
1188                 interface = strchr(queuename, '|');
1189                 if (interface) {
1190                         *interface = '\0';
1191                         interface++;
1192                 }
1193                 else {
1194                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1195                         interface = strrchr(tmpchan, '-');
1196                         if (interface)
1197                                 *interface = '\0';
1198                         interface = tmpchan;
1199                 }
1200         }
1201
1202         if( ( q = queues) != NULL )
1203         {
1204                 while( q && ( res != 0 ) && (!found) ) 
1205                 {
1206                         ast_mutex_lock(&q->lock);
1207                         if( strcmp( q->name, queuename) == 0 )
1208                         {
1209                                 // found queue, try to remove  interface
1210                                 found=1 ;
1211
1212                                 if( ( node = interface_exists( q, interface ) ) != NULL )
1213                                 {
1214                                         if( ( look = q->members ) == node )
1215                                         {
1216                                                 // 1st
1217                                                 q->members = node->next;
1218                                         }
1219                                         else
1220                                         {
1221                                                 while( look != NULL )
1222                                                         if( look->next == node )
1223                                                         {
1224                                                                 look->next = node->next ;
1225                                                                 break ;
1226                                                         }
1227                                                         else
1228                                                                 look = look->next ;
1229                                         }
1230
1231                                         free( node ) ;
1232
1233                                         ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", 
1234                                                 interface, queuename);
1235                                         res = 0 ;
1236                                 }
1237                                 else
1238                                 {
1239                                         ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
1240                                                 "Not there\n", interface, queuename);
1241                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1242                                                 {
1243                                                 chan->priority += 100;
1244                                                 res = 0 ;
1245                                                 }
1246                                 }
1247                         }
1248
1249                         ast_mutex_unlock(&q->lock);
1250                         q = q->next;
1251                 }
1252         }
1253
1254         if( ! found )
1255                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1256
1257         LOCAL_USER_REMOVE(u);
1258         return res;
1259 }
1260
1261
1262
1263 static int aqm_exec(struct ast_channel *chan, void *data)
1264 {
1265         int res=-1;
1266         struct localuser *u;
1267         char *queuename;
1268         char info[512];
1269         char tmpchan[512]="";
1270         char *interface=NULL;
1271         struct ast_call_queue *q;
1272         struct member *save;
1273         int found=0 ;
1274
1275         if (!data) {
1276                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
1277                 return -1;
1278         }
1279         
1280         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1281         
1282         /* Parse our arguments XXX Check for failure XXX */
1283         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1284         queuename = info;
1285         if (queuename) {
1286                 interface = strchr(queuename, '|');
1287                 if (interface) {
1288                         *interface = '\0';
1289                         interface++;
1290                 }
1291                 else {
1292                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1293                         interface = strrchr(tmpchan, '-');
1294                         if (interface)
1295                                 *interface = '\0';
1296                         interface = tmpchan;
1297                 }
1298         }
1299
1300         if( ( q = queues) != NULL )
1301         {
1302                 while( q && ( res != 0 ) && (!found) ) 
1303                 {
1304                         ast_mutex_lock(&q->lock);
1305                         if( strcmp( q->name, queuename) == 0 )
1306                         {
1307                                 // found queue, try to enable interface
1308                                 found=1 ;
1309
1310                                 if( interface_exists( q, interface ) == NULL )
1311                                 {
1312                                         save = q->members ;
1313                                         q->members = create_queue_node( interface ) ;
1314
1315                                         if( q->members != NULL ) {
1316                                                 q->members->dynamic = 1;
1317                                                 q->members->next = save ;
1318                                         } else
1319                                                 q->members = save ;
1320
1321                                         ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1322                                         res = 0 ;
1323                                 }
1324                                 else
1325                                 {
1326                                         ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
1327                                                 "Already there\n", interface, queuename);
1328                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1329                                         {
1330                                                 chan->priority += 100;
1331                                                 res = 0 ;
1332                                         }
1333                                 }
1334                         }
1335
1336                         ast_mutex_unlock(&q->lock);
1337                         q = q->next;
1338                 }
1339         }
1340
1341         if( ! found )
1342                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1343
1344         LOCAL_USER_REMOVE(u);
1345         return res;
1346 }
1347
1348
1349 static int queue_exec(struct ast_channel *chan, void *data)
1350 {
1351         int res=-1;
1352         int ringing=0;
1353         struct localuser *u;
1354         char *queuename;
1355         char info[512];
1356         char *options = NULL;
1357         char *url = NULL;
1358         char *announceoverride = NULL;
1359         char *queuetimeoutstr = NULL;
1360         /* whether to exit Queue application after the timeout hits */
1361         int go_on = 0;
1362
1363
1364
1365         /* Our queue entry */
1366         struct queue_ent qe;
1367         
1368         if (!data) {
1369                 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
1370                 return -1;
1371         }
1372         
1373         LOCAL_USER_ADD(u);
1374
1375         /* Setup our queue entry */
1376         memset(&qe, 0, sizeof(qe));
1377         
1378         /* Parse our arguments XXX Check for failure XXX */
1379         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1380         queuename = info;
1381         if (queuename) {
1382                 options = strchr(queuename, '|');
1383                 if (options) {
1384                         *options = '\0';
1385                         options++;
1386                         url = strchr(options, '|');
1387                         if (url) {
1388                                 *url = '\0';
1389                                 url++;
1390                                 announceoverride = strchr(url, '|');
1391                                 if (announceoverride) {
1392                                         *announceoverride = '\0';
1393                                         announceoverride++;
1394                                         queuetimeoutstr = strchr(announceoverride, '|');
1395                                         if (queuetimeoutstr) {
1396                                                 *queuetimeoutstr = '\0';
1397                                                 queuetimeoutstr++;
1398                                                 qe.queuetimeout = atoi(queuetimeoutstr);
1399                                         } else {
1400                                                 qe.queuetimeout = 0;
1401                                 }
1402                         }
1403                 }
1404         }
1405         }
1406
1407         if (options) {
1408                 if (strchr(options, 'r')) {
1409                         ringing = 1;
1410                 }
1411         }
1412
1413         printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
1414                 queuename, options, url, announceoverride, qe.queuetimeout);
1415
1416
1417         qe.chan = chan;
1418         qe.start = time(NULL);
1419         qe.last_pos_said = 0;
1420         qe.last_pos = 0;
1421         if (!join_queue(queuename, &qe)) {
1422                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
1423                 /* Start music on hold */
1424                 if (ringing) {
1425                         ast_indicate(chan, AST_CONTROL_RINGING);
1426                 } else {              
1427                         ast_moh_start(chan, qe.moh);
1428                 }
1429                 for (;;) {
1430                         /* This is the wait loop for callers 2 through maxlen */
1431
1432                         res = wait_our_turn(&qe, ringing);
1433                         /* If they hungup, return immediately */
1434                         if (res < 0) {
1435                                 /* Record this abandoned call */
1436                                 record_abandoned(&qe);
1437                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1438                                 if (option_verbose > 2) {
1439                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1440                                         res = -1;
1441                                 }
1442                                 break;
1443                         }
1444                         if (!res) 
1445                                 break;
1446                         if (valid_exit(&qe, res)) {
1447                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1448                                 break;
1449                         }
1450                 }
1451                 if (!res) {
1452                         for (;;) {
1453                                 /* This is the wait loop for the head caller*/
1454                                 /* To exit, they may get their call answered; */
1455                                 /* they may dial a digit from the queue context; */
1456                                 /* or, they may may timeout. */
1457
1458                                 /* Leave if we have exceeded our queuetimeout */
1459                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1460                                         res = 0;
1461                                         break;
1462                                 }
1463
1464                                 /* Make a position announcement, if enabled */
1465                                 if (qe.parent->announcefrequency && !ringing)
1466                                         say_position(&qe);
1467
1468                                 /* Try calling all queue members for 'timeout' seconds */
1469                                 res = try_calling(&qe, options, announceoverride, url, &go_on);
1470                                 if (res) {
1471                                         if (res < 0) {
1472                                                 if (!qe.handled)
1473                                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1474                                         } else if (res > 0)
1475                                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1476                                         break;
1477                                 }
1478
1479                                 /* Leave if we have exceeded our queuetimeout */
1480                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1481                                         res = 0;
1482                                         break;
1483                                 }
1484
1485                                 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
1486                                 res = wait_a_bit(&qe);
1487                                 if (res < 0) {
1488                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1489                                         if (option_verbose > 2) {
1490                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1491                                                 res = -1;
1492                                         }
1493                                         break;
1494                                 }
1495                                 if (res && valid_exit(&qe, res)) {
1496                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1497                                         break;
1498                                 }
1499                                 /* exit after 'timeout' cycle if 'n' option enabled */
1500                                 if (go_on) {
1501                                         if (option_verbose > 2) {
1502                                                 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
1503                                                 res = -1;
1504                                         }
1505                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1506                                         res = 0;
1507                                         break;
1508                                 }
1509
1510                         }
1511                 }
1512                 /* Don't allow return code > 0 */
1513                 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1514                         res = 0;        
1515                         if (ringing) {
1516                                 ast_indicate(chan, -1);
1517                         } else {
1518                                 ast_moh_stop(chan);
1519                         }                       
1520                         ast_stopstream(chan);
1521                 }
1522                 leave_queue(&qe);
1523         } else {
1524                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1525                 res =  0;
1526         }
1527         LOCAL_USER_REMOVE(u);
1528         return res;
1529 }
1530
1531 static void reload_queues(void)
1532 {
1533         struct ast_call_queue *q, *ql, *qn;
1534         struct ast_config *cfg;
1535         char *cat, *tmp;
1536         struct ast_variable *var;
1537         struct member *prev, *cur;
1538         int new;
1539         cfg = ast_load("queues.conf");
1540         if (!cfg) {
1541                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1542                 return;
1543         }
1544         ast_mutex_lock(&qlock);
1545         /* Mark all queues as dead for the moment */
1546         q = queues;
1547         while(q) {
1548                 q->dead = 1;
1549                 q = q->next;
1550         }
1551         /* Chug through config file */
1552         cat = ast_category_browse(cfg, NULL);
1553         while(cat) {
1554                 if (strcasecmp(cat, "general")) {
1555                         /* Look for an existing one */
1556                         q = queues;
1557                         while(q) {
1558                                 if (!strcmp(q->name, cat))
1559                                         break;
1560                                 q = q->next;
1561                         }
1562                         if (!q) {
1563                                 /* Make one then */
1564                                 q = malloc(sizeof(struct ast_call_queue));
1565                                 if (q) {
1566                                         /* Initialize it */
1567                                         memset(q, 0, sizeof(struct ast_call_queue));
1568                                         ast_mutex_init(&q->lock);
1569                                         strncpy(q->name, cat, sizeof(q->name));
1570                                         new = 1;
1571                                 } else new = 0;
1572                         } else
1573                                         new = 0;
1574                         if (q) {
1575                                 if (!new) 
1576                                         ast_mutex_lock(&q->lock);
1577                                 /* Re-initialize the queue */
1578                                 q->dead = 0;
1579                                 q->retry = 0;
1580                                 q->timeout = -1;
1581                                 q->maxlen = 0;
1582                                 q->announcefrequency = 0;
1583                                 q->announceholdtime = 0;
1584                                 q->holdtime = 0;
1585                                 q->callscompleted = 0;
1586                                 q->callsabandoned = 0;
1587                                 q->callscompletedinsl = 0;
1588                                 q->servicelevel = 0;
1589                                 free_members(q, 0);
1590                                 strcpy(q->moh, "");
1591                                 strcpy(q->announce, "");
1592                                 strcpy(q->context, "");
1593                                 strcpy(q->monfmt, "");
1594                                 strcpy(q->sound_next, "queue-youarenext");
1595                                 strcpy(q->sound_thereare, "queue-thereare");
1596                                 strcpy(q->sound_calls, "queue-callswaiting");
1597                                 strcpy(q->sound_holdtime, "queue-holdtime");
1598                                 strcpy(q->sound_minutes, "queue-minutes");
1599                                 strcpy(q->sound_thanks, "queue-thankyou");
1600                                 prev = q->members;
1601                                 if (prev) {
1602                                         /* find the end of any dynamic members */
1603                                         while(prev->next)
1604                                                 prev = prev->next;
1605                                 }
1606                                 var = ast_variable_browse(cfg, cat);
1607                                 while(var) {
1608                                         if (!strcasecmp(var->name, "member")) {
1609                                                 /* Add a new member */
1610                                                 cur = malloc(sizeof(struct member));
1611                                                 if (cur) {
1612                                                         memset(cur, 0, sizeof(struct member));
1613                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1614                                                         if ((tmp = strchr(cur->tech, ','))) {
1615                                                                 *tmp = '\0';
1616                                                                 tmp++;
1617                                                                 cur->penalty = atoi(tmp);
1618                                                                 if (cur->penalty < 0)
1619                                                                         cur->penalty = 0;
1620                                                         }
1621                                                         if ((tmp = strchr(cur->tech, '/')))
1622                                                                 *tmp = '\0';
1623                                                         if ((tmp = strchr(var->value, '/'))) {
1624                                                                 tmp++;
1625                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1626                                                                 if ((tmp = strchr(cur->loc, ',')))
1627                                                                         *tmp = '\0';
1628                                                         } else
1629                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1630                                                         if (prev)
1631                                                                 prev->next = cur;
1632                                                         else
1633                                                                 q->members = cur;
1634                                                         prev = cur;
1635                                                 }
1636                                         } else if (!strcasecmp(var->name, "music")) {
1637                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1638                                         } else if (!strcasecmp(var->name, "announce")) {
1639                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1640                                         } else if (!strcasecmp(var->name, "context")) {
1641                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
1642                                         } else if (!strcasecmp(var->name, "timeout")) {
1643                                                 q->timeout = atoi(var->value);
1644                                         } else if (!strcasecmp(var->name, "monitor-format")) {
1645                                                 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
1646                                         } else if (!strcasecmp(var->name, "queue-youarenext")) {
1647                                                 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
1648                                         } else if (!strcasecmp(var->name, "queue-thereare")) {
1649                                                 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
1650                                         } else if (!strcasecmp(var->name, "queue-callswaiting")) {
1651                                                 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
1652                                         } else if (!strcasecmp(var->name, "queue-holdtime")) {
1653                                                 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
1654                                         } else if (!strcasecmp(var->name, "queue-minutes")) {
1655                                                 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
1656                                         } else if (!strcasecmp(var->name, "queue-thankyou")) {
1657                                                 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
1658                                         } else if (!strcasecmp(var->name, "announce-frequency")) {
1659                                                 q->announcefrequency = atoi(var->value);
1660                                         } else if (!strcasecmp(var->name, "announce-holdtime")) {
1661                                                 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
1662                                         } else if (!strcasecmp(var->name, "retry")) {
1663                                                 q->retry = atoi(var->value);
1664                                         } else if (!strcasecmp(var->name, "maxlen")) {
1665                                                 q->maxlen = atoi(var->value);
1666                                         } else if (!strcasecmp(var->name, "servicelevel")) {
1667                                                 q->servicelevel= atoi(var->value);
1668                                         } else if (!strcasecmp(var->name, "strategy")) {
1669                                                 q->strategy = strat2int(var->value);
1670                                                 if (q->strategy < 0) {
1671                                                         ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1672                                                         q->strategy = 0;
1673                                                 }
1674                                         } else {
1675                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1676                                         }
1677                                         var = var->next;
1678                                 }
1679                                 if (q->retry < 1)
1680                                         q->retry = DEFAULT_RETRY;
1681                                 if (q->timeout < 0)
1682                                         q->timeout = DEFAULT_TIMEOUT;
1683                                 if (q->maxlen < 0)
1684                                         q->maxlen = 0;
1685                                 if (!new) 
1686                                         ast_mutex_unlock(&q->lock);
1687                                 if (new) {
1688                                         q->next = queues;
1689                                         queues = q;
1690                                 }
1691                         }
1692                 }
1693                 cat = ast_category_browse(cfg, cat);
1694         }
1695         ast_destroy(cfg);
1696         q = queues;
1697         ql = NULL;
1698         while(q) {
1699                 qn = q->next;
1700                 if (q->dead) {
1701                         if (ql)
1702                                 ql->next = q->next;
1703                         else
1704                                 queues = q->next;
1705                         if (!q->count) {
1706                                 free(q);
1707                         } else
1708                                 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1709                 } else
1710                         ql = q;
1711                 q = qn;
1712         }
1713         ast_mutex_unlock(&qlock);
1714 }
1715
1716 static int __queues_show(int fd, int argc, char **argv, int queue_show)
1717 {
1718         struct ast_call_queue *q;
1719         struct queue_ent *qe;
1720         struct member *mem;
1721         int pos;
1722         time_t now;
1723         char max[80];
1724         char calls[80];
1725         float sl = 0;
1726
1727         time(&now);
1728         if ((!queue_show && argc != 2) || (queue_show && argc != 3))
1729                 return RESULT_SHOWUSAGE;
1730         ast_mutex_lock(&qlock);
1731         q = queues;
1732         if (!q) {       
1733                 ast_mutex_unlock(&qlock);
1734                 if (queue_show)
1735                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
1736                 else
1737                         ast_cli(fd, "No queues.\n");
1738                 return RESULT_SUCCESS;
1739         }
1740         while(q) {
1741                 ast_mutex_lock(&q->lock);
1742                 if (queue_show) {
1743                         if (strcasecmp(q->name, argv[2]) != 0) {
1744                                 ast_mutex_unlock(&q->lock);
1745                                 q = q->next;
1746                                 if (!q) {
1747                                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
1748                                         break;
1749                                 }
1750                                 continue;
1751                         }
1752                 }
1753                 if (q->maxlen)
1754                         snprintf(max, sizeof(max), "%d", q->maxlen);
1755                 else
1756                         strcpy(max, "unlimited");
1757                 sl = 0;
1758                 if(q->callscompleted > 0)
1759                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1760                 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",
1761                         q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
1762                 if (q->members) {
1763                         ast_cli(fd, "   Members: \n");
1764                         for (mem = q->members; mem; mem = mem->next) {
1765                                 if (mem->penalty)
1766                                         snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
1767                                 else
1768                                         strcpy(max, "");
1769                                 if (mem->dynamic)
1770                                         strcat(max, " (dynamic)");
1771                                 if (mem->calls) {
1772                                         snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
1773                                                         mem->calls, (long)(time(NULL) - mem->lastcall));
1774                                 } else
1775                                         strcpy(calls, " has taken no calls yet");
1776                                 ast_cli(fd, "      %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
1777                         }
1778                 } else
1779                         ast_cli(fd, "   No Members\n");
1780                 if (q->head) {
1781                         pos = 1;
1782                         ast_cli(fd, "   Callers: \n");
1783                         for (qe = q->head; qe; qe = qe->next) 
1784                                 ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name,
1785                                                                 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60);
1786                 } else
1787                         ast_cli(fd, "   No Callers\n");
1788                 ast_cli(fd, "\n");
1789                 ast_mutex_unlock(&q->lock);
1790                 q = q->next;
1791                 if (queue_show)
1792                         break;
1793         }
1794         ast_mutex_unlock(&qlock);
1795         return RESULT_SUCCESS;
1796 }
1797
1798 static int queues_show(int fd, int argc, char **argv)
1799 {
1800         return __queues_show(fd, argc, argv, 0);
1801 }
1802
1803 static int queue_show(int fd, int argc, char **argv)
1804 {
1805         return __queues_show(fd, argc, argv, 1);
1806 }
1807
1808 static char *complete_queue(char *line, char *word, int pos, int state)
1809 {
1810         struct ast_call_queue *q;
1811         int which=0;
1812         
1813         ast_mutex_lock(&qlock);
1814         q = queues;
1815         while(q) {
1816                 if (!strncasecmp(word, q->name, strlen(word))) {
1817                         if (++which > state)
1818                                 break;
1819                 }
1820                 q = q->next;
1821         }
1822         ast_mutex_unlock(&qlock);
1823         return q ? strdup(q->name) : NULL;
1824 }
1825
1826 /* JDG: callback to display queues status in manager */
1827 static int manager_queues_show( struct mansession *s, struct message *m )
1828 {
1829         char *a[] = { "show", "queues" };
1830         return queues_show( s->fd, 2, a );
1831 } /* /JDG */
1832
1833
1834 /* Dump queue status */
1835 static int manager_queues_status( struct mansession *s, struct message *m )
1836 {
1837         time_t now;
1838         int pos;
1839         char *id = astman_get_header(m,"ActionID");
1840         char idText[256] = "";
1841         struct ast_call_queue *q;
1842         struct queue_ent *qe;
1843         float sl = 0;
1844         struct member *mem;
1845         astman_send_ack(s, m, "Queue status will follow");
1846         time(&now);
1847         ast_mutex_lock(&qlock);
1848         q = queues;
1849         if (id && &id) {
1850                 snprintf(idText,256,"ActionID: %s\r\n",id);
1851         }
1852         while(q) {
1853                 ast_mutex_lock(&q->lock);
1854
1855                 /* List queue properties */
1856                 if(q->callscompleted > 0)
1857                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1858                 ast_cli(s->fd, "Event: QueueParams\r\n"
1859                                         "Queue: %s\r\n"
1860                                         "Max: %d\r\n"
1861                                         "Calls: %d\r\n"
1862                                         "Holdtime: %d\r\n"
1863                                         "Completed: %d\r\n"
1864                                         "Abandoned: %d\r\n"
1865                                         "ServiceLevel: %d\r\n"
1866                                         "ServicelevelPerf: %2.1f\r\n"
1867                                         "%s"
1868                                         "\r\n",
1869                                                 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
1870                                                 q->callsabandoned, q->servicelevel, sl, idText);
1871
1872                 /* List Queue Members */
1873                 for (mem = q->members; mem; mem = mem->next) 
1874                         ast_cli(s->fd, "Event: QueueMember\r\n"
1875                                 "Queue: %s\r\n"
1876                                 "Location: %s/%s\r\n"
1877                                 "Membership: %s\r\n"
1878                                 "Penalty: %d\r\n"
1879                                 "CallsTaken: %d\r\n"
1880                                 "LastCall: %ld\r\n"
1881                                 "%s"
1882                                 "\r\n",
1883                                         q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
1884                                         mem->penalty, mem->calls, mem->lastcall, idText);
1885
1886                 /* List Queue Entries */
1887
1888                 pos = 1;
1889                 for (qe = q->head; qe; qe = qe->next) 
1890                         ast_cli(s->fd, "Event: QueueEntry\r\n"
1891                                 "Queue: %s\r\n"
1892                                 "Position: %d\r\n"
1893                                 "Channel: %s\r\n"
1894                                 "CallerID: %s\r\n"
1895                                 "Wait: %ld\r\n"
1896                                 "%s"
1897                                 "\r\n", 
1898                                         q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
1899                 ast_mutex_unlock(&q->lock);
1900                 q = q->next;
1901         }
1902         ast_mutex_unlock(&qlock);
1903         return RESULT_SUCCESS;
1904 }
1905
1906 static char show_queues_usage[] = 
1907 "Usage: show queues\n"
1908 "       Provides summary information on call queues.\n";
1909
1910 static struct ast_cli_entry cli_show_queues = {
1911         { "show", "queues", NULL }, queues_show, 
1912         "Show status of queues", show_queues_usage, NULL };
1913
1914 static char show_queue_usage[] = 
1915 "Usage: show queue\n"
1916 "       Provides summary information on a specified queue.\n";
1917
1918 static struct ast_cli_entry cli_show_queue = {
1919         { "show", "queue", NULL }, queue_show, 
1920         "Show status of a specified queue", show_queue_usage, complete_queue };
1921
1922 int unload_module(void)
1923 {
1924         STANDARD_HANGUP_LOCALUSERS;
1925         ast_cli_unregister(&cli_show_queue);
1926         ast_cli_unregister(&cli_show_queues);
1927         ast_manager_unregister( "Queues" );
1928         ast_manager_unregister( "QueueStatus" );
1929         ast_unregister_application(app_aqm);
1930         ast_unregister_application(app_rqm);
1931         return ast_unregister_application(app);
1932 }
1933
1934 int load_module(void)
1935 {
1936         int res;
1937         res = ast_register_application(app, queue_exec, synopsis, descrip);
1938         if (!res) {
1939                 ast_cli_register(&cli_show_queue);
1940                 ast_cli_register(&cli_show_queues);
1941                 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1942                 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1943
1944                 // [PHM 06/26/03]
1945                 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1946                 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1947         }
1948         reload_queues();
1949         return res;
1950 }
1951
1952
1953 int reload(void)
1954 {
1955         reload_queues();
1956         return 0;
1957 }
1958
1959 char *description(void)
1960 {
1961         return tdesc;
1962 }
1963
1964 int usecount(void)
1965 {
1966         int res;
1967         STANDARD_USECOUNT(res);
1968         return res;
1969 }
1970
1971 char *key()
1972 {
1973         return ASTERISK_GPL_KEY;
1974 }