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