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