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