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