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