6fec751a2c87cb7a9cc59f7282e56af76293934d
[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  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/parking.h>
24 #include <asterisk/musiconhold.h>
25 #include <asterisk/cli.h>
26 #include <asterisk/manager.h> /* JDG */
27 #include <asterisk/config.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <sys/time.h>
35 #include <sys/signal.h>
36 #include <netinet/in.h>
37
38 #include <pthread.h>
39
40 #define QUEUE_STRATEGY_RINGALL          0
41 #define QUEUE_STRATEGY_ROUNDROBIN       1
42 #define QUEUE_STRATEGY_LEASTRECENT      2
43 #define QUEUE_STRATEGY_FEWESTCALLS      3
44 #define QUEUE_STRATEGY_RANDOM           4
45
46 static struct strategy {
47         int strategy;
48         char *name;
49 } strategies[] = {
50         { QUEUE_STRATEGY_RINGALL, "ringall" },
51         { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
52         { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
53         { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
54         { QUEUE_STRATEGY_RANDOM, "random" },
55 };
56
57 #define DEFAULT_RETRY           5
58 #define DEFAULT_TIMEOUT         15
59 #define RECHECK                         1               /* Recheck every second to see we we're at the top yet */
60
61 static char *tdesc = "True Call Queueing";
62
63 static char *app = "Queue";
64
65 static char *synopsis = "Queue a call for a call queue";
66
67 static char *descrip =
68 "  Queue(queuename[|options[|URL][|announceoverride]]):\n"
69 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
70 "  This application returns -1 if the originating channel hangs up, or if the\n"
71 "call is bridged and  either of the parties in the bridge terminate the call.\n"
72 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
73 "The option string may contain zero or more of the following characters:\n"
74 "      't' -- allow the called user transfer the calling user\n"
75 "      'T' -- to allow the calling user to transfer the call.\n"
76 "      'd' -- data-quality (modem) call (minimum delay).\n"
77 "      'H' -- allow caller to hang up by hitting *.\n"
78 "  In addition to transferring the call, a call may be parked and then picked\n"
79 "up by another user.\n"
80 "  The optionnal URL will be sent to the called party if the channel supports\n"
81 "it.\n";
82
83 // [PHM 06/26/03]
84 static char *app_aqm = "AddQueueMember" ;
85 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
86 static char *app_aqm_descrip =
87 "   AddQueueMember(queuename[|interface]):\n"
88 "Dynamically adds interface to an existing queue\n"
89 "Returns -1 if there is an error.\n"
90 "Example: AddQueueMember(techsupport|SIP/3000)\n"
91 "";
92
93 static char *app_rqm = "RemoveQueueMember" ;
94 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
95 static char *app_rqm_descrip =
96 "   RemoveQueueMember(queuename[|interface]):\n"
97 "Dynamically removes interface to an existing queue\n"
98 "Returns -1 if there is an error.\n"
99 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
100 "";
101
102
103
104
105 /* We define a customer "local user" structure because we
106    use it not only for keeping track of what is in use but
107    also for keeping track of who we're dialing. */
108
109 struct localuser {
110         struct ast_channel *chan;
111         char numsubst[256];
112         char tech[40];
113         int stillgoing;
114         int penalty;
115         int metric;
116         int allowredirect_in;
117         int allowredirect_out;
118         int ringbackonly;
119         int musiconhold;
120         int dataquality;
121         int allowdisconnect;
122         struct localuser *next;
123 };
124
125 LOCAL_USER_DECL;
126
127 struct queue_ent {
128         struct ast_call_queue *parent;  /* What queue is our parent */
129         char moh[80];                           /* Name of musiconhold to be used */
130         char announce[80];              /* Announcement to play */
131         char context[80];               /* Context when user exits queue */
132         int pos;                                        /* Where we are in the queue */
133         time_t start;                           /* When we started holding */
134         struct ast_channel *chan;       /* Our channel */
135         struct queue_ent *next;         /* The next queue entry */
136 };
137
138 struct member {
139         char tech[80];                          /* Technology */
140         char loc[256];                          /* Location */
141         int penalty;                            /* Are we a last resort? */
142         struct timeval lastcall;        /* When last successful call was hungup */
143         struct member *next;            /* Next member */
144 };
145
146 struct ast_call_queue {
147         pthread_mutex_t lock;   
148         char name[80];                  /* Name of the queue */
149         char moh[80];                   /* Name of musiconhold to be used */
150         char announce[80];              /* Announcement to play */
151         char context[80];               /* Announcement to play */
152         int strategy;                   /* Queueing strategy */
153         int announcetimeout;    /* How often to announce their position */
154         int count;                              /* How many entries are in the queue */
155         int maxlen;                             /* Max number of entries in queue */
156
157         int dead;                               /* Whether this queue is dead or not */
158         int retry;                              /* Retry calling everyone after this amount of time */
159         int timeout;                    /* How long to wait for an answer */
160         
161         /* Queue strategy things */
162         
163         int rrpos;                              /* Round Robin - position */
164         int wrapped;                    /* Round Robin - wrapped around? */
165
166         struct member *members; /* Member channels to be tried */
167         struct queue_ent *head; /* Start of the actual queue */
168         struct ast_call_queue *next;    /* Next call queue */
169 };
170
171 static struct ast_call_queue *queues = NULL;
172 static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
173
174 static char *int2strat(int strategy)
175 {
176         int x;
177         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
178                 if (strategy == strategies[x].strategy)
179                         return strategies[x].name;
180         }
181         return "<unknown>";
182 }
183
184 static int strat2int(char *strategy)
185 {
186         int x;
187         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
188                 if (!strcasecmp(strategy, strategies[x].name))
189                         return strategies[x].strategy;
190         }
191         return -1;
192 }
193
194 static int join_queue(char *queuename, struct queue_ent *qe)
195 {
196         struct ast_call_queue *q;
197         struct queue_ent *cur, *prev = NULL;
198         int res = -1;
199         int pos = 0;
200         ast_pthread_mutex_lock(&qlock);
201         q = queues;
202         while(q) {
203                 if (!strcasecmp(q->name, queuename)) {
204                         /* This is our one */
205                         ast_pthread_mutex_lock(&q->lock);
206                         if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
207                                 /* There's space for us, put us at the end */
208                                 prev = NULL;
209                                 cur = q->head;
210                                 while(cur) {
211                                         cur->pos = ++pos;
212                                         prev = cur;
213                                         cur = cur->next;
214                                 }
215                                 if (prev)
216                                         prev->next = qe;
217                                 else
218                                         q->head = qe;
219                                 /* Fix additional pointers and
220                                   information  */
221                                 qe->next = NULL;
222                                 qe->parent = q;
223                                 qe->pos = ++pos;
224                                 strncpy(qe->moh, q->moh, sizeof(qe->moh));
225                                 strncpy(qe->announce, q->announce, sizeof(qe->announce));
226                                 strncpy(qe->context, q->context, sizeof(qe->context));
227                                 q->count++;
228                                 res = 0;
229                                 manager_event(EVENT_FLAG_CALL, "Join", 
230                                                                 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
231                                                                 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), q->name, qe->pos, q->count );
232 #if 0
233 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
234 #endif
235                         }
236                         ast_pthread_mutex_unlock(&q->lock);
237                         break;
238                 }
239                 q = q->next;
240         }
241         ast_pthread_mutex_unlock(&qlock);
242         return res;
243 }
244
245 static void free_members(struct ast_call_queue *q)
246 {
247         struct member *curm, *next;
248         curm = q->members;
249         while(curm) {
250                 next = curm->next;
251                 free(curm);
252                 curm = next;
253         }
254         q->members = NULL;
255 }
256
257 static void destroy_queue(struct ast_call_queue *q)
258 {
259         struct ast_call_queue *cur, *prev = NULL;
260         ast_pthread_mutex_lock(&qlock);
261         cur = queues;
262         while(cur) {
263                 if (cur == q) {
264                         if (prev)
265                                 prev->next = cur->next;
266                         else
267                                 queues = cur->next;
268                 } else {
269                         prev = cur;
270                 }
271                 cur = cur->next;
272         }
273         ast_pthread_mutex_unlock(&qlock);
274         free_members(q);
275         free(q);
276 }
277
278 static void leave_queue(struct queue_ent *qe)
279 {
280         struct ast_call_queue *q;
281         struct queue_ent *cur, *prev = NULL;
282         int pos = 0;
283         q = qe->parent;
284         if (!q)
285                 return;
286         ast_pthread_mutex_lock(&q->lock);
287
288         prev = NULL;
289         cur = q->head;
290         while(cur) {
291                 if (cur == qe) {
292                         q->count--;
293
294                         /* Take us out of the queue */
295                         manager_event(EVENT_FLAG_CALL, "Leave",
296                                  "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
297                                  qe->chan->name, q->name,  q->count);
298 #if 0
299 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
300 #endif
301                         /* Take us out of the queue */
302                         if (prev)
303                                 prev->next = cur->next;
304                         else
305                                 q->head = cur->next;
306                 } else {
307                         cur->pos = ++pos;
308                         prev = cur;
309                 }
310                 cur = cur->next;
311         }
312         ast_pthread_mutex_unlock(&q->lock);
313         if (q->dead && !q->count) {     
314                 /* It's dead and nobody is in it, so kill it */
315                 destroy_queue(q);
316         }
317 }
318
319 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
320 {
321         /* Hang up a tree of stuff */
322         struct localuser *oo;
323         while(outgoing) {
324                 /* Hangup any existing lines we have open */
325                 if (outgoing->chan && (outgoing->chan != exception))
326                         ast_hangup(outgoing->chan);
327                 oo = outgoing;
328                 outgoing=outgoing->next;
329                 free(oo);
330         }
331 }
332
333 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
334 {
335         int res;
336         /* Request the peer */
337         tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
338         if (!tmp->chan) {                       /* If we can't, just go on to the next call */
339 #if 0
340                 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
341 #endif                  
342                 if (qe->chan->cdr)
343                         ast_cdr_busy(qe->chan->cdr);
344                 tmp->stillgoing = 0;
345                 return 0;
346         }
347         tmp->chan->appl = "AppQueue";
348         tmp->chan->data = "(Outgoing Line)";
349         tmp->chan->whentohangup = 0;
350         if (tmp->chan->callerid)
351                 free(tmp->chan->callerid);
352         if (tmp->chan->ani)
353                 free(tmp->chan->ani);
354         if (qe->chan->callerid)
355                 tmp->chan->callerid = strdup(qe->chan->callerid);
356         else
357                 tmp->chan->callerid = NULL;
358         if (qe->chan->ani)
359                 tmp->chan->ani = strdup(qe->chan->ani);
360         else
361                 tmp->chan->ani = NULL;
362         /* Presense of ADSI CPE on outgoing channel follows ours */
363         tmp->chan->adsicpe = qe->chan->adsicpe;
364         /* Place the call, but don't wait on the answer */
365         res = ast_call(tmp->chan, tmp->numsubst, 0);
366         if (res) {
367                 /* Again, keep going even if there's an error */
368                 if (option_debug)
369                         ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
370                 else if (option_verbose > 2)
371                         ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
372                 ast_hangup(tmp->chan);
373                 tmp->chan = NULL;
374                 tmp->stillgoing = 0;
375                 return 0;
376         } else
377                 if (option_verbose > 2)
378                         ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
379         return 0;
380 }
381
382 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
383 {
384         struct localuser *cur;
385         struct localuser *best;
386         int bestmetric=0;
387         do {
388                 best = NULL;
389                 cur = outgoing;
390                 while(cur) {
391                         if (cur->stillgoing &&                                                  /* Not already done */
392                                 !cur->chan &&                                                           /* Isn't already going */
393                                 (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
394                                         bestmetric = cur->metric;
395                                         best = cur;
396                         }
397                         cur = cur->next;
398                 }
399                 if (best) {
400                         /* Ring the best channel, and remember the best
401                            metric for the next pass */
402                         ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
403                         ring_entry(qe, best);
404                 }
405         } while (best && !best->chan);
406         
407         if (!best) {
408                 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
409                 return 0;
410         }
411         return 1;
412 }
413
414 static int valid_exit(struct queue_ent *qe, char digit)
415 {
416         char tmp[2];
417         if (!strlen(qe->context))
418                 return 0;
419         tmp[0] = digit;
420         tmp[1] = '\0';
421         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
422                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
423                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
424                 qe->chan->priority = 0;
425                 return 1;
426         }
427         return 0;
428 }
429
430 #define MAX 256
431
432 static struct ast_channel *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *digit)
433 {
434         char *queue = qe->parent->name;
435         struct localuser *o;
436         int found;
437         int numlines;
438         int sentringing = 0;
439         int numbusies = 0;
440         int orig = *to;
441         struct ast_frame *f;
442         struct ast_channel *peer = NULL;
443         struct ast_channel *watchers[MAX];
444         int pos;
445         struct ast_channel *winner;
446         struct ast_channel *in = qe->chan;
447         
448         while(*to && !peer) {
449                 o = outgoing;
450                 found = -1;
451                 pos = 1;
452                 numlines = 0;
453                 watchers[0] = in;
454                 while(o) {
455                         /* Keep track of important channels */
456                         if (o->stillgoing && o->chan) {
457                                 watchers[pos++] = o->chan;
458                                 found = 1;
459                         }
460                         o = o->next;
461                         numlines++;
462                 }
463                 if (found < 0) {
464                         if (numlines == numbusies) {
465                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
466                         } else {
467                                 ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
468                         }
469                         *to = 0;
470                         return NULL;
471                 }
472                 winner = ast_waitfor_n(watchers, pos, to);
473                 o = outgoing;
474                 while(o) {
475                         if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
476                                 if (!peer) {
477                                         if (option_verbose > 2)
478                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
479                                         peer = o->chan;
480                                         *allowredir_in = o->allowredirect_in;
481                                         *allowredir_out = o->allowredirect_out;
482                                         *allowdisconnect = o->allowdisconnect;
483                                 }
484                         } else if (o->chan && (o->chan == winner)) {
485                                 f = ast_read(winner);
486                                 if (f) {
487                                         if (f->frametype == AST_FRAME_CONTROL) {
488                                                 switch(f->subclass) {
489                                             case AST_CONTROL_ANSWER:
490                                                         /* This is our guy if someone answered. */
491                                                         if (!peer) {
492                                                                 if (option_verbose > 2)
493                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
494                                                                 peer = o->chan;
495                                                                 *allowredir_in = o->allowredirect_in;
496                                                                 *allowredir_out = o->allowredirect_out;
497                                                                 *allowdisconnect = o->allowdisconnect;
498                                                         }
499                                                         break;
500                                                 case AST_CONTROL_BUSY:
501                                                         if (option_verbose > 2)
502                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
503                                                         o->stillgoing = 0;
504                                                         if (in->cdr)
505                                                                 ast_cdr_busy(in->cdr);
506                                                         ast_hangup(o->chan);
507                                                         o->chan = NULL;
508                                                         if (qe->parent->strategy)
509                                                                 ring_one(qe, outgoing);
510                                                         numbusies++;
511                                                         break;
512                                                 case AST_CONTROL_CONGESTION:
513                                                         if (option_verbose > 2)
514                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
515                                                         o->stillgoing = 0;
516                                                         if (in->cdr)
517                                                                 ast_cdr_busy(in->cdr);
518                                                         ast_hangup(o->chan);
519                                                         o->chan = NULL;
520                                                         if (qe->parent->strategy)
521                                                                 ring_one(qe, outgoing);
522                                                         numbusies++;
523                                                         break;
524                                                 case AST_CONTROL_RINGING:
525                                                         if (option_verbose > 2)
526                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
527                                                         if (!sentringing) {
528 #if 0
529                                                                 ast_indicate(in, AST_CONTROL_RINGING);
530 #endif                                                          
531                                                                 sentringing++;
532                                                         }
533                                                         break;
534                                                 case AST_CONTROL_OFFHOOK:
535                                                         /* Ignore going off hook */
536                                                         break;
537                                                 default:
538                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
539                                                 }
540                                         }
541                                         ast_frfree(f);
542                                 } else {
543                                         o->stillgoing = 0;
544                                         ast_hangup(o->chan);
545                                         o->chan = NULL;
546                                         if (qe->parent->strategy)
547                                                 ring_one(qe, outgoing);
548                                 }
549                         }
550                         o = o->next;
551                 }
552                 if (winner == in) {
553                         f = ast_read(in);
554 #if 0
555                         if (f && (f->frametype != AST_FRAME_VOICE))
556                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
557                         else if (!f || (f->frametype != AST_FRAME_VOICE))
558                                 printf("Hangup received on %s\n", in->name);
559 #endif
560                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
561                                 /* Got hung up */
562                                 *to=-1;
563                                 return NULL;
564                         }
565                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
566                             if (option_verbose > 3)
567                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
568                                 *to=0;
569                                 return NULL;
570                         }
571                         if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
572                                 if (option_verbose > 3)
573                                         ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
574                                 *to=0;
575                                 *digit=f->subclass;
576                                 return NULL;
577                         }
578                 }
579                 if (!*to && (option_verbose > 2))
580                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
581         }
582
583         return peer;
584         
585 }
586
587 static int wait_our_turn(struct queue_ent *qe)
588 {
589         struct queue_ent *ch;
590         int res = 0;
591         for (;;) {
592                 /* Atomically read the parent head */
593                 pthread_mutex_lock(&qe->parent->lock);
594                 ch = qe->parent->head;
595                 pthread_mutex_unlock(&qe->parent->lock);
596                 /* If we are now at the top of the head, break out */
597                 if (qe->parent->head == qe)
598                         break;
599                 /* Wait a second before checking again */
600                 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
601                 if (res)
602                         break;
603         }
604         return res;
605 }
606
607 static int calc_metric(struct ast_call_queue *q, int pos, struct queue_ent *qe, struct localuser *tmp)
608 {
609         switch (q->strategy) {
610         case QUEUE_STRATEGY_RINGALL:
611                 ast_log(LOG_WARNING, "Can't calculate metric for ringall strategy\n");
612                 break;
613         case QUEUE_STRATEGY_ROUNDROBIN:
614                 if (!pos) {
615                         /* rrpos > number of queue entries */
616                         if (!q->wrapped)
617                                 q->rrpos = 1;
618                         else
619                                 q->rrpos++;
620                         q->wrapped = 0;
621                 }
622                 if (pos < q->rrpos) {
623                         tmp->metric = 1000 + pos;
624                 } else {
625                         if (pos > q->rrpos)
626                                 q->wrapped = 1;
627                         tmp->metric = pos;
628                 }
629                 tmp->metric += tmp->penalty * 1000000;
630                 break;
631         case QUEUE_STRATEGY_RANDOM:
632                 tmp->metric = rand() % 1000;
633                 tmp->metric += tmp->penalty * 1000000;
634                 break;
635         default:
636                 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
637                 break;
638         }
639         return 0;
640 }
641
642 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url)
643 {
644         struct member *cur;
645         struct localuser *outgoing=NULL, *tmp = NULL;
646         int to;
647         int allowredir_in=0;
648         int allowredir_out=0;
649         int allowdisconnect=0;
650         char restofit[AST_MAX_EXTENSION];
651         char *newnum;
652         struct ast_channel *peer;
653         int res = 0, bridge = 0;
654         int zapx = 2;
655         int x=0;
656         char *announce = NULL;
657         char digit = 0;
658         /* Hold the lock while we setup the outgoing calls */
659         ast_pthread_mutex_lock(&qe->parent->lock);
660         cur = qe->parent->members;
661         if (strlen(qe->announce))
662                 announce = qe->announce;
663         if (announceoverride && strlen(announceoverride))
664                 announce = announceoverride;
665         while(cur) {
666                 /* Get a technology/[device:]number pair */
667                 tmp = malloc(sizeof(struct localuser));
668                 if (!tmp) {
669                         ast_log(LOG_WARNING, "Out of memory\n");
670                         goto out;
671                 }
672                 memset(tmp, 0, sizeof(struct localuser));
673                 tmp->stillgoing = -1;
674                 if (options) {
675                         if (strchr(options, 't'))
676                                 tmp->allowredirect_in = 1;
677                         if (strchr(options, 'T'))
678                                 tmp->allowredirect_out = 1;
679                         if (strchr(options, 'r'))
680                                 tmp->ringbackonly = 1;
681                         if (strchr(options, 'm'))
682                                 tmp->musiconhold = 1;
683                         if (strchr(options, 'd'))
684                                 tmp->dataquality = 1;
685                         if (strchr(options, 'H'))
686                                 tmp->allowdisconnect = 1;
687                 }
688                 if (url) {
689                         ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
690                 } else 
691                         ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
692
693                 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
694                 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
695                 tmp->penalty = cur->penalty;
696                 /* If we're dialing by extension, look at the extension to know what to dial */
697                 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
698                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
699                         snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
700                         if (option_debug)
701                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
702                 }
703                 /* Special case: If we ring everyone, go ahead and ring them, otherwise
704                    just calculate their metric for the appropriate strategy */
705                 if (!qe->parent->strategy)
706                         ring_entry(qe, tmp);
707                 else
708                         calc_metric(qe->parent, x++, qe, tmp);
709                 /* Put them in the list of outgoing thingies...  We're ready now. 
710                    XXX If we're forcibly removed, these outgoing calls won't get
711                    hung up XXX */
712                 tmp->next = outgoing;
713                 outgoing = tmp;         
714                 /* If this line is up, don't try anybody else */
715                 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
716                         break;
717
718                 cur = cur->next;
719         }
720         if (qe->parent->timeout)
721                 to = qe->parent->timeout * 1000;
722         else
723                 to = -1;
724         if (qe->parent->strategy)
725                 ring_one(qe, outgoing);
726         ast_pthread_mutex_unlock(&qe->parent->lock);
727         peer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
728         if (!peer) {
729                 if (to) {
730                         /* Musta gotten hung up */
731                         res = -1;
732                 } else {
733                         if (digit && valid_exit(qe, digit))
734                                 res=digit;
735                         else
736                                 /* Nobody answered, next please? */
737                                 res=0;
738                 }
739                 goto out;
740         }
741         if (peer) {
742                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
743                    we will always return with -1 so that it is hung up properly after the 
744                    conversation.  */
745                 if (!strcmp(qe->chan->type,"Zap")) {
746                         if (tmp->dataquality) zapx = 0;
747                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
748                 }                       
749                 if (!strcmp(peer->type,"Zap")) {
750                         if (tmp->dataquality) zapx = 0;
751                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
752                 }
753                 hanguptree(outgoing, peer);
754                 /* Stop music on hold */
755                 ast_moh_stop(qe->chan);
756                 outgoing = NULL;
757                 if (announce) {
758                         int res2;
759                         res2 = ast_autoservice_start(qe->chan);
760                         if (!res2)
761                                 res2 = ast_streamfile(peer, announce, peer->language);
762                         /* XXX Need a function to wait on *both* streams XXX */
763                         if (!res2)
764                                 res2 = ast_waitstream(peer, "");
765                         res2 |= ast_autoservice_stop(qe->chan);
766                         if (res2) {
767                                 /* Agent must have hung up */
768                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
769                                 ast_hangup(peer);
770                                 return -1;
771                         }
772                 }
773                 /* If appropriate, log that we have a destination channel */
774                 if (qe->chan->cdr)
775                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
776                 /* Make sure channels are compatible */
777                 res = ast_channel_make_compatible(qe->chan, peer);
778                 if (res < 0) {
779                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
780                         ast_hangup(peer);
781                         return -1;
782                 }
783                 /* Drop out of the queue at this point, to prepare for next caller */
784                 leave_queue(qe);                        
785                 /* JDG: sendurl */
786                 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
787                         ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
788                         ast_channel_sendurl( peer, url );
789                 } /* /JDG */
790                 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
791
792                 if(bridge != AST_PBX_NO_HANGUP_PEER)
793                         ast_hangup(peer);
794
795                 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
796                 else res = bridge; /* bridge error, stay in the queue */
797         }       
798 out:
799         hanguptree(outgoing, NULL);
800         return res;
801 }
802
803 static int wait_a_bit(struct queue_ent *qe)
804 {
805         int retrywait;
806         /* Hold the lock while we setup the outgoing calls */
807         ast_pthread_mutex_lock(&qe->parent->lock);
808         retrywait = qe->parent->retry * 1000;
809         ast_pthread_mutex_unlock(&qe->parent->lock);
810         return ast_waitfordigit(qe->chan, retrywait);
811 }
812
813 // [PHM 06/26/03]
814
815 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
816 {
817         struct member * ret = NULL ;
818         struct member *mem;
819         char buf[500] ;
820
821         if( q != NULL )
822         {
823                 mem = q->members ;
824
825                 while( mem != NULL ) {
826                         sprintf( buf, "%s/%s", mem->tech, mem->loc);
827
828                         if( strcmp( buf, interface ) == 0 ) {
829                                 ret = mem ;
830                                 break ;
831                         }
832                         else
833                                 mem = mem->next ;
834                 }
835         }
836
837         return( ret ) ;
838 }
839
840
841 static struct member * create_queue_node( char * interface )
842 {
843         struct member * cur ;
844         char * tmp ;
845         
846         /* Add a new member */
847
848         cur = malloc(sizeof(struct member));
849
850         if (cur) {
851                 memset(cur, 0, sizeof(struct member));
852                 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
853                 if ((tmp = strchr(cur->tech, '/')))
854                         *tmp = '\0';
855                 if ((tmp = strchr(interface, '/'))) {
856                         tmp++;
857                         strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
858                 } else
859                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
860         }
861
862         return( cur ) ;
863 }
864
865
866 static int rqm_exec(struct ast_channel *chan, void *data)
867 {
868         int res=-1;
869         struct localuser *u;
870         char *queuename;
871         struct member * node ;
872         struct member * look ;
873         char info[512];
874         char *interface=NULL;
875         struct ast_call_queue *q;
876         int found=0 ;
877
878         if (!data) {
879                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
880                 return -1;
881         }
882         
883         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
884         
885         /* Parse our arguments XXX Check for failure XXX */
886         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
887         queuename = info;
888         if (queuename) {
889                 interface = strchr(queuename, '|');
890                 if (interface) {
891                         *interface = '\0';
892                         interface++;
893                 }
894                 else
895                         interface = chan->name ;
896         }
897
898         if( ( q = queues) != NULL )
899         {
900                 while( q && ( res != 0 ) && (!found) ) 
901                 {
902                         ast_pthread_mutex_lock(&q->lock);
903                         if( strcmp( q->name, queuename) == 0 )
904                         {
905                                 // found queue, try to remove  interface
906                                 found=1 ;
907
908                                 if( ( node = interface_exists( q, interface ) ) != NULL )
909                                 {
910                                         if( ( look = q->members ) == node )
911                                         {
912                                                 // 1st
913                                                 q->members = node->next;
914                                         }
915                                         else
916                                         {
917                                                 while( look != NULL )
918                                                         if( look->next == node )
919                                                         {
920                                                                 look->next = node->next ;
921                                                                 break ;
922                                                         }
923                                                         else
924                                                                 look = look->next ;
925                                         }
926
927                                         free( node ) ;
928
929                                         ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", 
930                                                 interface, queuename);
931                                         res = 0 ;
932                                 }
933                                 else
934                                         ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
935                                                 "Not there\n", interface, queuename);
936                         }
937
938                         ast_pthread_mutex_unlock(&q->lock);
939                         q = q->next;
940                 }
941         }
942
943         if( ! found )
944                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
945
946         LOCAL_USER_REMOVE(u);
947         return res;
948 }
949
950
951
952 static int aqm_exec(struct ast_channel *chan, void *data)
953 {
954         int res=-1;
955         struct localuser *u;
956         char *queuename;
957         char info[512];
958         char *interface=NULL;
959         struct ast_call_queue *q;
960         struct member *save;
961         int found=0 ;
962
963         if (!data) {
964                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
965                 return -1;
966         }
967         
968         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
969         
970         /* Parse our arguments XXX Check for failure XXX */
971         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
972         queuename = info;
973         if (queuename) {
974                 interface = strchr(queuename, '|');
975                 if (interface) {
976                         *interface = '\0';
977                         interface++;
978                 }
979                 else
980                         interface = chan->name ;
981         }
982
983         if( ( q = queues) != NULL )
984         {
985                 while( q && ( res != 0 ) && (!found) ) 
986                 {
987                         ast_pthread_mutex_lock(&q->lock);
988                         if( strcmp( q->name, queuename) == 0 )
989                         {
990                                 // found queue, try to enable interface
991                                 found=1 ;
992
993                                 if( interface_exists( q, interface ) == NULL )
994                                 {
995                                         save = q->members ;
996                                         q->members = create_queue_node( interface ) ;
997
998                                         if( q->members != NULL )
999                                                 q->members->next = save ;
1000                                         else
1001                                                 q->members = save ;
1002
1003                                         ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1004                                         res = 0 ;
1005                                 }
1006                                 else
1007                                         ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
1008                                                 "Already there\n", interface, queuename);
1009                         }
1010
1011                         ast_pthread_mutex_unlock(&q->lock);
1012                         q = q->next;
1013                 }
1014         }
1015
1016         if( ! found )
1017                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1018
1019         LOCAL_USER_REMOVE(u);
1020         return res;
1021 }
1022
1023
1024 static int queue_exec(struct ast_channel *chan, void *data)
1025 {
1026         int res=-1;
1027         struct localuser *u;
1028         char *queuename;
1029         char info[512];
1030         char *options = NULL;
1031         char *url = NULL;
1032         char *announceoverride = NULL;
1033         
1034         /* Our queue entry */
1035         struct queue_ent qe;
1036         
1037         if (!data) {
1038                 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
1039                 return -1;
1040         }
1041         
1042         LOCAL_USER_ADD(u);
1043         
1044         /* Parse our arguments XXX Check for failure XXX */
1045         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1046         queuename = info;
1047         if (queuename) {
1048                 options = strchr(queuename, '|');
1049                 if (options) {
1050                         *options = '\0';
1051                         options++;
1052                         url = strchr(options, '|');
1053                         if (url) {
1054                                 *url = '\0';
1055                                 url++;
1056                                 announceoverride = strchr(url, '|');
1057                                 if (announceoverride) {
1058                                         *announceoverride = '\0';
1059                                         announceoverride++;
1060                                 }
1061                         }
1062                 }
1063         }
1064         printf("queue: %s, options: %s, url: %s, announce: %s\n",
1065                 queuename, options, url, announceoverride);
1066         /* Setup our queue entry */
1067         memset(&qe, 0, sizeof(qe));
1068         qe.chan = chan;
1069         qe.start = time(NULL);
1070         if (!join_queue(queuename, &qe)) {
1071                 /* Start music on hold */
1072                 ast_moh_start(chan, qe.moh);
1073                 for (;;) {
1074                         res = wait_our_turn(&qe);
1075                         /* If they hungup, return immediately */
1076                         if (res < 0) {
1077                                 if (option_verbose > 2) {
1078                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1079                                         res = -1;
1080                                 }
1081                                 break;
1082                         }
1083                         if (!res)
1084                                 break;
1085                         if (valid_exit(&qe, res))
1086                                 break;
1087                 }
1088                 if (!res) {
1089                         for (;;) {
1090                                 res = try_calling(&qe, options, announceoverride, url);
1091                                 if (res)
1092                                         break;
1093                                 res = wait_a_bit(&qe);
1094                                 if (res < 0) {
1095                                         if (option_verbose > 2) {
1096                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1097                                                 res = -1;
1098                                         }
1099                                         break;
1100                                 }
1101                                 if (res && valid_exit(&qe, res))
1102                                         break;
1103                         }
1104                 }
1105                 /* Don't allow return code > 0 */
1106                 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1107                         res = 0;        
1108                         ast_moh_stop(chan);
1109                 }
1110                 leave_queue(&qe);
1111         } else {
1112                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1113                 res =  0;
1114         }
1115         LOCAL_USER_REMOVE(u);
1116         return res;
1117 }
1118
1119 static void reload_queues(void)
1120 {
1121         struct ast_call_queue *q, *ql, *qn;
1122         struct ast_config *cfg;
1123         char *cat, *tmp;
1124         struct ast_variable *var;
1125         struct member *prev, *cur;
1126         int new;
1127         cfg = ast_load("queues.conf");
1128         if (!cfg) {
1129                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1130                 return;
1131         }
1132         ast_pthread_mutex_lock(&qlock);
1133         /* Mark all queues as dead for the moment */
1134         q = queues;
1135         while(q) {
1136                 q->dead = 1;
1137                 q = q->next;
1138         }
1139         /* Chug through config file */
1140         cat = ast_category_browse(cfg, NULL);
1141         while(cat) {
1142                 if (strcasecmp(cat, "general")) {
1143                         /* Look for an existing one */
1144                         q = queues;
1145                         while(q) {
1146                                 if (!strcmp(q->name, cat))
1147                                         break;
1148                                 q = q->next;
1149                         }
1150                         if (!q) {
1151                                 /* Make one then */
1152                                 q = malloc(sizeof(struct ast_call_queue));
1153                                 if (q) {
1154                                         /* Initialize it */
1155                                         memset(q, 0, sizeof(struct ast_call_queue));
1156                                         ast_pthread_mutex_init(&q->lock);
1157                                         strncpy(q->name, cat, sizeof(q->name));
1158                                         new = 1;
1159                                 } else new = 0;
1160                         } else
1161                                         new = 0;
1162                         if (q) {
1163                                 if (!new) 
1164                                         ast_pthread_mutex_lock(&q->lock);
1165                                 /* Re-initialize the queue */
1166                                 q->dead = 0;
1167                                 q->retry = 0;
1168                                 q->timeout = -1;
1169                                 q->maxlen = 0;
1170                                 free_members(q);
1171                                 strcpy(q->moh, "");
1172                                 strcpy(q->announce, "");
1173                                 strcpy(q->context, "");
1174                                 prev = NULL;
1175                                 var = ast_variable_browse(cfg, cat);
1176                                 while(var) {
1177                                         if (!strcasecmp(var->name, "member")) {
1178                                                 /* Add a new member */
1179                                                 cur = malloc(sizeof(struct member));
1180                                                 if (cur) {
1181                                                         memset(cur, 0, sizeof(struct member));
1182                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1183                                                         if ((tmp = strchr(cur->tech, ','))) {
1184                                                                 *tmp = '\0';
1185                                                                 tmp++;
1186                                                                 cur->penalty = atoi(tmp);
1187                                                                 if (cur->penalty < 0)
1188                                                                         cur->penalty = 0;
1189                                                         }
1190                                                         if ((tmp = strchr(cur->tech, '/')))
1191                                                                 *tmp = '\0';
1192                                                         if ((tmp = strchr(var->value, '/'))) {
1193                                                                 tmp++;
1194                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1195                                                                 if ((tmp = strchr(cur->loc, ',')))
1196                                                                         *tmp = '\0';
1197                                                         } else
1198                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1199                                                         if (prev)
1200                                                                 prev->next = cur;
1201                                                         else
1202                                                                 q->members = cur;
1203                                                         prev = cur;
1204                                                 }
1205                                         } else if (!strcasecmp(var->name, "music")) {
1206                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1207                                         } else if (!strcasecmp(var->name, "announce")) {
1208                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1209                                         } else if (!strcasecmp(var->name, "context")) {
1210                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
1211                                         } else if (!strcasecmp(var->name, "timeout")) {
1212                                                 q->timeout = atoi(var->value);
1213                                         } else if (!strcasecmp(var->name, "retry")) {
1214                                                 q->retry = atoi(var->value);
1215                                         } else if (!strcasecmp(var->name, "maxlen")) {
1216                                                 q->maxlen = atoi(var->value);
1217                                         } else if (!strcasecmp(var->name, "strategy")) {
1218                                                 q->strategy = strat2int(var->value);
1219                                                 if (q->strategy < 0) {
1220                                                         ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1221                                                         q->strategy = 0;
1222                                                 }
1223                                         } else {
1224                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1225                                         }
1226                                         var = var->next;
1227                                 }
1228                                 if (q->retry < 1)
1229                                         q->retry = DEFAULT_RETRY;
1230                                 if (q->timeout < 0)
1231                                         q->timeout = DEFAULT_TIMEOUT;
1232                                 if (q->maxlen < 0)
1233                                         q->maxlen = 0;
1234                                 if (!new) 
1235                                         ast_pthread_mutex_unlock(&q->lock);
1236                                 if (new) {
1237                                         q->next = queues;
1238                                         queues = q;
1239                                 }
1240                         }
1241                 }
1242                 cat = ast_category_browse(cfg, cat);
1243         }
1244         ast_destroy(cfg);
1245         q = queues;
1246         ql = NULL;
1247         while(q) {
1248                 qn = q->next;
1249                 if (q->dead) {
1250                         if (ql)
1251                                 ql->next = q->next;
1252                         else
1253                                 queues = q->next;
1254                         if (!q->count) {
1255                                 free(q);
1256                         } else
1257                                 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1258                 } else
1259                         ql = q;
1260                 q = qn;
1261         }
1262         ast_pthread_mutex_unlock(&qlock);
1263 }
1264
1265 static int queues_show(int fd, int argc, char **argv)
1266 {
1267         struct ast_call_queue *q;
1268         struct queue_ent *qe;
1269         struct member *mem;
1270         int pos;
1271         time_t now;
1272         char max[80];
1273         
1274         time(&now);
1275         if (argc != 2)
1276                 return RESULT_SHOWUSAGE;
1277         q = queues;
1278         if (!q) {       
1279                 ast_cli(fd, "No queues.\n");
1280                 return RESULT_SUCCESS;
1281         }
1282         while(q) {
1283                 ast_pthread_mutex_lock(&q->lock);
1284                 if (q->maxlen)
1285                         snprintf(max, sizeof(max), "%d", q->maxlen);
1286                 else
1287                         strcpy(max, "unlimited");
1288                 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy));
1289                 if (q->members) {
1290                         ast_cli(fd, "   Members: \n");
1291                         for (mem = q->members; mem; mem = mem->next) {
1292                                 if (mem->penalty)
1293                                         snprintf(max, sizeof(max), " with penalty %d", mem->penalty);
1294                                 else
1295                                         strcpy(max, "");
1296                                 ast_cli(fd, "      %s/%s%s\n", mem->tech, mem->loc, max);
1297                         }
1298                 } else
1299                         ast_cli(fd, "   No Members\n");
1300                 if (q->head) {
1301                         pos = 1;
1302                         ast_cli(fd, "   Callers: \n");
1303                         for (qe = q->head; qe; qe = qe->next) 
1304                                 ast_cli(fd, "      %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
1305                                                                 (now - qe->start) / 60, (now - qe->start) % 60);
1306                 } else
1307                         ast_cli(fd, "   No Callers\n");
1308                 ast_cli(fd, "\n");
1309                 ast_pthread_mutex_unlock(&q->lock);
1310                 q = q->next;
1311         }
1312         return RESULT_SUCCESS;
1313 }
1314
1315 /* JDG: callback to display queues status in manager */
1316 static int manager_queues_show( struct mansession *s, struct message *m )
1317 {
1318         char *a[] = { "show", "queues" };
1319         return queues_show( s->fd, 2, a );
1320 } /* /JDG */
1321
1322
1323 /* Dump queue status */
1324 static int manager_queues_status( struct mansession *s, struct message *m )
1325 {
1326         time_t now;
1327         int pos;
1328         struct ast_call_queue *q;
1329         struct queue_ent *qe;
1330         astman_send_ack(s, "Queue status will follow");
1331         time(&now);
1332         q = queues;
1333         while(q) {
1334                 ast_pthread_mutex_lock(&q->lock);
1335                 ast_cli(s->fd, "Event: QueueParams\r\n"
1336                                         "Queue: %s\r\n"
1337                                         "Max: %d\r\n"
1338                                         "Calls: %d\r\n"
1339                                         "\r\n",
1340                                                 q->name, q->maxlen, q->count);
1341 #if 0
1342                 /* Do we care about queue members? */                                   
1343                 for (mem = q->members; mem; mem = mem->next) 
1344                         ast_cli(fd, "      %s/%s\n", mem->tech, mem->loc);
1345 #endif                  
1346                 pos = 1;
1347                 for (qe = q->head; qe; qe = qe->next) 
1348                         ast_cli(s->fd, "Event: QueueMember\r\n"
1349                                 "Queue: %s\r\n"
1350                                 "Position: %d\r\n"
1351                                 "Channel: %s\r\n"
1352                                 "CallerID: %s\r\n"
1353                                 "Wait: %ld\r\n"
1354                                 "\r\n", 
1355                                         q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), now - qe->start);
1356                 ast_pthread_mutex_unlock(&q->lock);
1357                 q = q->next;
1358         }
1359         return RESULT_SUCCESS;
1360 }
1361
1362 static char show_queues_usage[] = 
1363 "Usage: show queues\n"
1364 "       Provides summary information on call queues.\n";
1365
1366 static struct ast_cli_entry cli_show_queues = {
1367         { "show", "queues", NULL }, queues_show, 
1368         "Show status of queues", show_queues_usage, NULL };
1369
1370 int unload_module(void)
1371 {
1372         STANDARD_HANGUP_LOCALUSERS;
1373         ast_cli_unregister(&cli_show_queues);
1374         ast_manager_unregister( "Queues" );
1375         ast_manager_unregister( "QueueStatus" );
1376         return ast_unregister_application(app);
1377 }
1378
1379 int load_module(void)
1380 {
1381         int res;
1382         res = ast_register_application(app, queue_exec, synopsis, descrip);
1383         if (!res) {
1384                 ast_cli_register(&cli_show_queues);
1385                 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1386                 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1387
1388                 // [PHM 06/26/03]
1389                 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1390                 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1391         }
1392         reload_queues();
1393         return res;
1394 }
1395
1396
1397 int reload(void)
1398 {
1399         reload_queues();
1400         return 0;
1401 }
1402
1403 char *description(void)
1404 {
1405         return tdesc;
1406 }
1407
1408 int usecount(void)
1409 {
1410         int res;
1411         STANDARD_USECOUNT(res);
1412         return res;
1413 }
1414
1415 char *key()
1416 {
1417         return ASTERISK_GPL_KEY;
1418 }