175f28df29bb67a3ee1858c65129abd5ab180432
[asterisk/asterisk.git] / apps / app_queue.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * True call queues
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/config.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <sys/time.h>
34 #include <sys/signal.h>
35 #include <netinet/in.h>
36
37 #include <pthread.h>
38
39 #define DEFAULT_RETRY           5
40 #define DEFAULT_TIMEOUT         15
41 #define RECHECK                         1               /* Recheck every second to see we we're at the top yet */
42
43 static char *tdesc = "True Call Queueing";
44
45 static char *app = "Queue";
46
47 static char *synopsis = "Queue a call for a call queue";
48
49 static char *descrip =
50 "  Queue(queuename[|timeout[|options]]):\n"
51 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
52 "  This application returns -1 if the originating channel hangs up, or if the\n"
53 "call is bridged and  either of the parties in the bridge terminate the call.\n"
54 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
55 "The option string may contain zero or more of the following characters:\n"
56 "      't' -- allow the called user transfer the calling user\n"
57 "      'T' -- to allow the calling user to transfer the call.\n"
58 "      'd' -- data-quality (modem) call (minimum delay).\n"
59 "      'H' -- allow caller to hang up by hitting *.\n"
60 "  In addition to transferring the call, a call may be parked and then picked\n"
61 "up by another user.\n";
62
63 /* We define a customer "local user" structure because we
64    use it not only for keeping track of what is in use but
65    also for keeping track of who we're dialing. */
66
67 struct localuser {
68         struct ast_channel *chan;
69         int stillgoing;
70         int allowredirect;
71         int ringbackonly;
72         int musiconhold;
73         int dataquality;
74         int allowdisconnect;
75         struct localuser *next;
76 };
77
78 LOCAL_USER_DECL;
79
80 struct queue_ent {
81         struct ast_call_queue *parent;  /* What queue is our parent */
82         char moh[80];                           /* Name of musiconhold to be used */
83         char announce[80];              /* Announcement to play */
84         char context[80];               /* Announcement to play */
85         int pos;                                        /* Where we are in the queue */
86         time_t start;                           /* When we started holding */
87         struct ast_channel *chan;       /* Our channel */
88         struct queue_ent *next;         /* The next queue entry */
89 };
90
91 struct member {
92         char tech[80];                          /* Technology */
93         char loc[256];                          /* Location */
94         struct member *next;            /* Next member */
95 };
96
97 struct ast_call_queue {
98         pthread_mutex_t lock;   
99         char name[80];                  /* Name of the queue */
100         char moh[80];                   /* Name of musiconhold to be used */
101         char announce[80];              /* Announcement to play */
102         char context[80];               /* Announcement to play */
103         int announcetimeout;    /* How often to announce their position */
104         int count;                              /* How many entries are in the queue */
105         int maxlen;                             /* Max number of entries in queue */
106
107         int dead;                               /* Whether this queue is dead or not */
108         int retry;                              /* Retry calling everyone after this amount of time */
109         int timeout;                    /* How long to wait for an answer */
110
111         struct member *members; /* Member channels to be tried */
112         struct queue_ent *head; /* Start of the actual queue */
113         struct ast_call_queue *next;    /* Next call queue */
114 };
115
116 static struct ast_call_queue *queues = NULL;
117 static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
118
119
120 static int join_queue(char *queuename, struct queue_ent *qe)
121 {
122         struct ast_call_queue *q;
123         struct queue_ent *cur, *prev = NULL;
124         int res = -1;
125         int pos = 0;
126         ast_pthread_mutex_lock(&qlock);
127         q = queues;
128         while(q) {
129                 if (!strcasecmp(q->name, queuename)) {
130                         /* This is our one */
131                         ast_pthread_mutex_lock(&q->lock);
132                         if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
133                                 /* There's space for us, put us at the end */
134                                 prev = NULL;
135                                 cur = q->head;
136                                 while(cur) {
137                                         cur->pos = ++pos;
138                                         prev = cur;
139                                         cur = cur->next;
140                                 }
141                                 if (prev)
142                                         prev->next = qe;
143                                 else
144                                         q->head = qe;
145                                 /* Fix additional pointers and
146                                   information  */
147                                 qe->next = NULL;
148                                 qe->parent = q;
149                                 qe->pos = ++pos;
150                                 strncpy(qe->moh, q->moh, sizeof(qe->moh));
151                                 strncpy(qe->announce, q->announce, sizeof(qe->announce));
152                                 strncpy(qe->context, q->context, sizeof(qe->context));
153                                 q->count++;
154                                 res = 0;
155                         }
156                         ast_pthread_mutex_unlock(&q->lock);
157                         break;
158                 }
159                 q = q->next;
160         }
161         ast_pthread_mutex_unlock(&qlock);
162         return res;
163 }
164
165 static void free_members(struct ast_call_queue *q)
166 {
167         struct member *curm, *next;
168         curm = q->members;
169         while(curm) {
170                 next = curm->next;
171                 free(curm);
172                 curm = next;
173         }
174         q->members = NULL;
175 }
176
177 static void destroy_queue(struct ast_call_queue *q)
178 {
179         struct ast_call_queue *cur, *prev = NULL;
180         ast_pthread_mutex_lock(&qlock);
181         cur = queues;
182         while(cur) {
183                 if (cur == q) {
184                         if (prev)
185                                 prev->next = cur->next;
186                         else
187                                 queues = cur->next;
188                 } else {
189                         prev = cur;
190                 }
191                 cur = cur->next;
192         }
193         ast_pthread_mutex_unlock(&qlock);
194         free_members(q);
195         free(q);
196 }
197
198 static void leave_queue(struct queue_ent *qe)
199 {
200         struct ast_call_queue *q;
201         struct queue_ent *cur, *prev = NULL;
202         int pos = 0;
203         q = qe->parent;
204         if (!q)
205                 return;
206         ast_pthread_mutex_lock(&q->lock);
207         /* Take us out of the queue */
208         prev = NULL;
209         cur = q->head;
210         while(cur) {
211                 if (cur == qe) {
212                         q->count--;
213                         /* Take us out of the queue */
214                         if (prev)
215                                 prev->next = cur->next;
216                         else
217                                 q->head = cur->next;
218                 } else {
219                         cur->pos = ++pos;
220                         prev = cur;
221                 }
222                 cur = cur->next;
223         }
224         ast_pthread_mutex_unlock(&q->lock);
225         if (q->dead && !q->count) {     
226                 /* It's dead and nobody is in it, so kill it */
227                 destroy_queue(q);
228         }
229 }
230
231 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
232 {
233         /* Hang up a tree of stuff */
234         struct localuser *oo;
235         while(outgoing) {
236                 /* Hangup any existing lines we have open */
237                 if (outgoing->chan != exception)
238                         ast_hangup(outgoing->chan);
239                 oo = outgoing;
240                 outgoing=outgoing->next;
241                 free(oo);
242         }
243 }
244
245 #define MAX 256
246
247 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir, int *allowdisconnect, char *queue)
248 {
249         struct localuser *o;
250         int found;
251         int numlines;
252         int sentringing = 0;
253         int numbusies = 0;
254         int orig = *to;
255         struct ast_frame *f;
256         struct ast_channel *peer = NULL;
257         struct ast_channel *watchers[MAX];
258         int pos;
259         struct ast_channel *winner;
260                 
261         while(*to && !peer) {
262                 o = outgoing;
263                 found = -1;
264                 pos = 1;
265                 numlines = 0;
266                 watchers[0] = in;
267                 while(o) {
268                         /* Keep track of important channels */
269                         if (o->stillgoing) {
270                                 watchers[pos++] = o->chan;
271                                 found = 1;
272                         }
273                         o = o->next;
274                         numlines++;
275                 }
276                 if (found < 0) {
277                         if (numlines == numbusies) {
278                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
279                         } else {
280                                 ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
281                         }
282                         *to = 0;
283                         return NULL;
284                 }
285                 winner = ast_waitfor_n(watchers, pos, to);
286                 o = outgoing;
287                 while(o) {
288                         if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) {
289                                 if (!peer) {
290                                         if (option_verbose > 2)
291                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
292                                         peer = o->chan;
293                                         *allowredir = o->allowredirect;
294                                         *allowdisconnect = o->allowdisconnect;
295                                 }
296                         } else if (o->chan == winner) {
297                                 f = ast_read(winner);
298                                 if (f) {
299                                         if (f->frametype == AST_FRAME_CONTROL) {
300                                                 switch(f->subclass) {
301                                             case AST_CONTROL_ANSWER:
302                                                         /* This is our guy if someone answered. */
303                                                         if (!peer) {
304                                                                 if (option_verbose > 2)
305                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
306                                                                 peer = o->chan;
307                                                                 *allowredir = o->allowredirect;
308                                                                 *allowdisconnect = o->allowdisconnect;
309                                                         }
310                                                         break;
311                                                 case AST_CONTROL_BUSY:
312                                                         if (option_verbose > 2)
313                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
314                                                         o->stillgoing = 0;
315                                                         if (in->cdr)
316                                                                 ast_cdr_busy(in->cdr);
317                                                         numbusies++;
318                                                         break;
319                                                 case AST_CONTROL_CONGESTION:
320                                                         if (option_verbose > 2)
321                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
322                                                         o->stillgoing = 0;
323                                                         if (in->cdr)
324                                                                 ast_cdr_busy(in->cdr);
325                                                         numbusies++;
326                                                         break;
327                                                 case AST_CONTROL_RINGING:
328                                                         if (option_verbose > 2)
329                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
330                                                         if (!sentringing) {
331 #if 0
332                                                                 ast_indicate(in, AST_CONTROL_RINGING);
333 #endif                                                          
334                                                                 sentringing++;
335                                                         }
336                                                         break;
337                                                 case AST_CONTROL_OFFHOOK:
338                                                         /* Ignore going off hook */
339                                                         break;
340                                                 default:
341                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
342                                                 }
343                                         }
344                                         ast_frfree(f);
345                                 } else {
346                                         o->stillgoing = 0;
347                                 }
348                         }
349                         o = o->next;
350                 }
351                 if (winner == in) {
352                         f = ast_read(in);
353 #if 0
354                         if (f && (f->frametype != AST_FRAME_VOICE))
355                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
356                         else if (!f || (f->frametype != AST_FRAME_VOICE))
357                                 printf("Hangup received on %s\n", in->name);
358 #endif
359                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
360                                 /* Got hung up */
361                                 *to=-1;
362                                 return NULL;
363                         }
364                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect &&
365                                 (f->subclass == '*')) {
366                             if (option_verbose > 3)
367                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
368                                 *to=0;
369                                 return NULL;
370                         }
371                 }
372                 if (!*to && (option_verbose > 2))
373                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
374         }
375
376         return peer;
377         
378 }
379
380 static int wait_our_turn(struct queue_ent *qe)
381 {
382         struct queue_ent *ch;
383         int res = 0;
384         for (;;) {
385                 /* Atomically read the parent head */
386                 pthread_mutex_lock(&qe->parent->lock);
387                 ch = qe->parent->head;
388                 pthread_mutex_unlock(&qe->parent->lock);
389                 /* If we are now at the top of the head, break out */
390                 if (qe->parent->head == qe)
391                         break;
392                 /* Wait a second before checking again */
393                 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
394                 if (res)
395                         break;
396         }
397         return res;
398 }
399
400 static int try_calling(struct queue_ent *qe, char *options)
401 {
402         struct member *cur;
403         struct localuser *outgoing=NULL, *tmp = NULL;
404         int to;
405         int allowredir=0;
406         int allowdisconnect=0;
407         char numsubst[AST_MAX_EXTENSION];
408         char restofit[AST_MAX_EXTENSION];
409         char *newnum;
410         struct ast_channel *peer;
411         int res = 0;
412         /* Hold the lock while we setup the outgoing calls */
413         ast_pthread_mutex_lock(&qe->parent->lock);
414         cur = qe->parent->members;
415         while(cur) {
416                 /* Get a technology/[device:]number pair */
417                 tmp = malloc(sizeof(struct localuser));
418                 if (!tmp) {
419                         ast_log(LOG_WARNING, "Out of memory\n");
420                         goto out;
421                 }
422                 memset(tmp, 0, sizeof(struct localuser));
423                 if (options) {
424                         if (strchr(options, 't'))
425                                 tmp->allowredirect = 1;
426                         if (strchr(options, 'r'))
427                                 tmp->ringbackonly = 1;
428                         if (strchr(options, 'm'))
429                                 tmp->musiconhold = 1;
430                         if (strchr(options, 'd'))
431                                 tmp->dataquality = 1;
432                         if (strchr(options, 'H'))
433                                 tmp->allowdisconnect = 1;
434                 }
435
436                 strncpy(numsubst, cur->loc, sizeof(numsubst)-1);
437                 /* If we're dialing by extension, look at the extension to know what to dial */
438                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
439                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
440                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", qe->chan->exten,restofit);
441                         if (option_debug)
442                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
443                 }
444                 /* Request the peer */
445                 tmp->chan = ast_request(cur->tech, qe->chan->nativeformats, numsubst);
446                 if (!tmp->chan) {
447                         /* If we can't, just go on to the next call */
448 #if 0
449                         ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
450 #endif                  
451                         if (qe->chan->cdr)
452                                 ast_cdr_busy(qe->chan->cdr);
453                         free(tmp);
454                         cur = cur->next;
455                         continue;
456                 }
457 #if 0           
458                 /* Don't honor call forwarding on a queue! */
459                 if (strlen(tmp->chan->call_forward)) {
460                         if (option_verbose > 2)
461                                 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
462                         /* Setup parameters */
463                         strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
464                         strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
465                         chan->priority = 0;
466                         to = 0;
467                         ast_hangup(tmp->chan);
468                         free(tmp);
469                         cur = rest;
470                         break;
471                 }
472 #endif          
473                 tmp->chan->appl = "AppQueue";
474                 tmp->chan->data = "(Outgoing Line)";
475                 tmp->chan->whentohangup = 0;
476                 if (tmp->chan->callerid)
477                         free(tmp->chan->callerid);
478                 if (tmp->chan->ani)
479                         free(tmp->chan->ani);
480                 if (qe->chan->callerid)
481                         tmp->chan->callerid = strdup(qe->chan->callerid);
482                 else
483                         tmp->chan->callerid = NULL;
484                 if (qe->chan->ani)
485                         tmp->chan->ani = strdup(qe->chan->ani);
486                 else
487                         tmp->chan->ani = NULL;
488                 /* Presense of ADSI CPE on outgoing channel follows ours */
489                 tmp->chan->adsicpe = qe->chan->adsicpe;
490                 /* Place the call, but don't wait on the answer */
491                 res = ast_call(tmp->chan, numsubst, 0);
492                 if (res) {
493                         /* Again, keep going even if there's an error */
494                         if (option_debug)
495                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
496                         else if (option_verbose > 2)
497                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
498                         ast_hangup(tmp->chan);
499                         free(tmp);
500                         cur = cur->next;
501                         continue;
502                 } else
503                         if (option_verbose > 2)
504                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
505                 /* Put them in the list of outgoing thingies...  We're ready now. 
506                    XXX If we're forcibly removed, these outgoing calls won't get
507                    hung up XXX */
508                 tmp->stillgoing = -1;
509                 tmp->next = outgoing;
510                 outgoing = tmp;         
511                 /* If this line is up, don't try anybody else */
512                 if (outgoing->chan->_state == AST_STATE_UP)
513                         break;
514
515                 cur = cur->next;
516         }
517         if (qe->parent->timeout)
518                 to = qe->parent->timeout * 1000;
519         else
520                 to = -1;
521         ast_pthread_mutex_unlock(&qe->parent->lock);
522         
523         peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir, &allowdisconnect, qe->parent->name);
524         if (!peer) {
525                 if (to) 
526                         /* Musta gotten hung up */
527                         res = -1;
528                  else 
529                         /* Nobody answered, next please? */
530                         res=0;
531                 
532                 goto out;
533         }
534         if (peer) {
535                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
536                    we will always return with -1 so that it is hung up properly after the 
537                    conversation.  */
538                 hanguptree(outgoing, peer);
539                 /* Stop music on hold */
540                 ast_moh_stop(qe->chan);
541                 outgoing = NULL;
542                 if (strlen(qe->announce)) {
543                         int res2;
544                         res2 = ast_streamfile(peer, qe->announce, peer->language);
545                         if (!res2)
546                                 res2 = ast_waitstream(peer, "");
547                         else
548                                 res2 = 0;
549                         if (res2) {
550                                 /* Agent must have hung up */
551                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
552                                 ast_hangup(peer);
553                                 return -1;
554                         }
555                 }
556                 /* If appropriate, log that we have a destination channel */
557                 if (qe->chan->cdr)
558                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
559                 /* Make sure channels are compatible */
560                 res = ast_channel_make_compatible(qe->chan, peer);
561                 if (res < 0) {
562                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
563                         ast_hangup(peer);
564                         return -1;
565                 }
566                 if (!strcmp(qe->chan->type,"Zap")) {
567                         int x = 2;
568                         if (tmp->dataquality) x = 0;
569                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
570                 }                       
571                 if (!strcmp(peer->type,"Zap")) {
572                         int x = 2;
573                         if (tmp->dataquality) x = 0;
574                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
575                 }
576                 /* Drop out of the queue at this point, to prepare for next caller */
577                 leave_queue(qe);                        
578                 res = ast_bridge_call(qe->chan, peer, allowredir, allowdisconnect);
579                 ast_hangup(peer);
580         }       
581 out:
582         hanguptree(outgoing, NULL);
583         return res;
584 }
585
586 static int wait_a_bit(struct queue_ent *qe)
587 {
588         int retrywait;
589         /* Hold the lock while we setup the outgoing calls */
590         ast_pthread_mutex_lock(&qe->parent->lock);
591         retrywait = qe->parent->retry * 1000;
592         ast_pthread_mutex_unlock(&qe->parent->lock);
593         return ast_waitfordigit(qe->chan, retrywait);
594 }
595
596 static int valid_exit(struct queue_ent *qe, char digit)
597 {
598         char tmp[2];
599         if (!strlen(qe->context))
600                 return 0;
601         tmp[0] = digit;
602         tmp[1] = '\0';
603         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
604                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
605                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
606                 qe->chan->priority = 0;
607                 return 1;
608         }
609         return 0;
610 }
611
612 static int queue_exec(struct ast_channel *chan, void *data)
613 {
614         int res=-1;
615         struct localuser *u;
616         char *queuename;
617         char info[512];
618         char *options = NULL;
619         
620         /* Our queue entry */
621         struct queue_ent qe;
622         
623         if (!data) {
624                 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout)\n");
625                 return -1;
626         }
627         
628         LOCAL_USER_ADD(u);
629         
630         /* Parse our arguments XXX Check for failure XXX */
631         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
632         queuename = info;
633         if (queuename) {
634                 options = strchr(queuename, '|');
635                 if (options) {
636                         *options = '\0';
637                         options++;
638                 }
639         }
640
641         /* Setup our queue entry */
642         memset(&qe, 0, sizeof(qe));
643         qe.chan = chan;
644         qe.start = time(NULL);
645         if (!join_queue(queuename, &qe)) {
646                 /* Start music on hold */
647                 ast_moh_start(chan, qe.moh);
648                 for (;;) {
649                         res = wait_our_turn(&qe);
650                         /* If they hungup, return immediately */
651                         if (res < 0) {
652                                 if (option_verbose > 2) {
653                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
654                                         res = -1;
655                                 }
656                                 break;
657                         }
658                         if (!res)
659                                 break;
660                         if (valid_exit(&qe, res))
661                                 break;
662                 }
663                 if (!res) {
664                         for (;;) {
665                                 res = try_calling(&qe, options);
666                                 if (res)
667                                         break;
668                                 res = wait_a_bit(&qe);
669                                 if (res < 0) {
670                                         if (option_verbose > 2) {
671                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
672                                                 res = -1;
673                                         }
674                                         break;
675                                 }
676                                 if (res && valid_exit(&qe, res))
677                                         break;
678                         }
679                 }
680                 /* Don't allow return code > 0 */
681                 if (res > 0)
682                         res = 0;        
683                 ast_moh_stop(chan);
684                 leave_queue(&qe);
685         } else {
686                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
687                 res =  0;
688         }
689         LOCAL_USER_REMOVE(u);
690         return res;
691 }
692
693 static void reload_queues(void)
694 {
695         struct ast_call_queue *q, *ql, *qn;
696         struct ast_config *cfg;
697         char *cat, *tmp;
698         struct ast_variable *var;
699         struct member *prev, *cur;
700         int new;
701         cfg = ast_load("queues.conf");
702         if (!cfg) {
703                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
704                 return;
705         }
706         ast_pthread_mutex_lock(&qlock);
707         /* Mark all queues as dead for the moment */
708         q = queues;
709         while(q) {
710                 q->dead = 1;
711                 q = q->next;
712         }
713         /* Chug through config file */
714         cat = ast_category_browse(cfg, NULL);
715         while(cat) {
716                 if (strcasecmp(cat, "general")) {
717                         /* Look for an existing one */
718                         q = queues;
719                         while(q) {
720                                 if (!strcmp(q->name, cat))
721                                         break;
722                                 q = q->next;
723                         }
724                         if (!q) {
725                                 /* Make one then */
726                                 q = malloc(sizeof(struct ast_call_queue));
727                                 if (q) {
728                                         /* Initialize it */
729                                         memset(q, 0, sizeof(struct ast_call_queue));
730                                         strncpy(q->name, cat, sizeof(q->name));
731                                         new = 1;
732                                 } else new = 0;
733                         } else
734                                         new = 0;
735                         if (q) {
736                                 if (!new) 
737                                         ast_pthread_mutex_lock(&q->lock);
738                                 /* Re-initialize the queue */
739                                 q->dead = 0;
740                                 q->retry = 0;
741                                 q->timeout = -1;
742                                 q->maxlen = 0;
743                                 free_members(q);
744                                 strcpy(q->moh, "");
745                                 strcpy(q->announce, "");
746                                 strcpy(q->context, "");
747                                 prev = NULL;
748                                 var = ast_variable_browse(cfg, cat);
749                                 while(var) {
750                                         if (!strcasecmp(var->name, "member")) {
751                                                 /* Add a new member */
752                                                 cur = malloc(sizeof(struct member));
753                                                 if (cur) {
754                                                         memset(cur, 0, sizeof(struct member));
755                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
756                                                         if ((tmp = strchr(cur->tech, '/')))
757                                                                 *tmp = '\0';
758                                                         if ((tmp = strchr(var->value, '/'))) {
759                                                                 tmp++;
760                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
761                                                         } else
762                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
763                                                         if (prev)
764                                                                 prev->next = cur;
765                                                         else
766                                                                 q->members = cur;
767                                                         prev = cur;
768                                                 }
769                                         } else if (!strcasecmp(var->name, "music")) {
770                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
771                                         } else if (!strcasecmp(var->name, "announce")) {
772                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
773                                         } else if (!strcasecmp(var->name, "context")) {
774                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
775                                         } else if (!strcasecmp(var->name, "timeout")) {
776                                                 q->timeout = atoi(var->value);
777                                         } else if (!strcasecmp(var->name, "retry")) {
778                                                 q->retry = atoi(var->value);
779                                         } else if (!strcasecmp(var->name, "maxlen")) {
780                                                 q->maxlen = atoi(var->value);
781                                         } else {
782                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
783                                         }
784                                         var = var->next;
785                                 }
786                                 if (q->retry < 1)
787                                         q->retry = DEFAULT_RETRY;
788                                 if (q->timeout < 0)
789                                         q->timeout = DEFAULT_TIMEOUT;
790                                 if (q->maxlen < 0)
791                                         q->maxlen = 0;
792                                 if (!new) 
793                                         ast_pthread_mutex_unlock(&q->lock);
794                                 if (new) {
795                                         q->next = queues;
796                                         queues = q;
797                                 }
798                         }
799                 }
800                 cat = ast_category_browse(cfg, cat);
801         }
802         q = queues;
803         ql = NULL;
804         while(q) {
805                 qn = q->next;
806                 if (q->dead) {
807                         if (ql)
808                                 ql->next = q->next;
809                         else
810                                 queues = q->next;
811                         if (!q->count) {
812                                 free(q);
813                         } else
814                                 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
815                 } else
816                         ql = q;
817                 q = qn;
818         }
819         ast_pthread_mutex_unlock(&qlock);
820 }
821
822 static int queues_show(int fd, int argc, char **argv)
823 {
824         struct ast_call_queue *q;
825         struct queue_ent *qe;
826         struct member *mem;
827         int pos;
828         time_t now;
829         char max[80];
830         
831         time(&now);
832         if (argc != 2)
833                 return RESULT_SHOWUSAGE;
834         q = queues;
835         if (!q) {       
836                 ast_cli(fd, "No queues.\n");
837                 return RESULT_SUCCESS;
838         }
839         while(q) {
840                 ast_pthread_mutex_lock(&q->lock);
841                 if (q->maxlen)
842                         snprintf(max, sizeof(max), "%d", q->maxlen);
843                 else
844                         strcpy(max, "unlimited");
845                 ast_cli(fd, "%-12.12s has %d calls (max %s)\n", q->name, q->count, max);
846                 if (q->members) {
847                         ast_cli(fd, "   Members: \n");
848                         for (mem = q->members; mem; mem = mem->next) 
849                                 ast_cli(fd, "      %s/%s\n", mem->tech, mem->loc);
850                 } else
851                         ast_cli(fd, "   No Members\n");
852                 if (q->head) {
853                         pos = 1;
854                         ast_cli(fd, "   Callers: \n");
855                         for (qe = q->head; qe; qe = qe->next) 
856                                 ast_cli(fd, "      %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
857                                                                 (now - qe->start) / 60, (now - qe->start) % 60);
858                 } else
859                         ast_cli(fd, "   No Callers\n");
860                 ast_cli(fd, "\n");
861                 ast_pthread_mutex_unlock(&q->lock);
862                 q = q->next;
863         }
864         return RESULT_SUCCESS;
865 }
866
867 static char show_queues_usage[] = 
868 "Usage: show queues\n"
869 "       Provides summary information on call queues.\n";
870
871 static struct ast_cli_entry cli_show_queues = {
872         { "show", "queues", NULL }, queues_show, 
873         "Show status of queues", show_queues_usage, NULL };
874
875
876
877 int unload_module(void)
878 {
879         STANDARD_HANGUP_LOCALUSERS;
880         ast_cli_unregister(&cli_show_queues);
881         return ast_unregister_application(app);
882 }
883
884 int load_module(void)
885 {
886         int res;
887         res = ast_register_application(app, queue_exec, synopsis, descrip);
888         if (!res)
889                 ast_cli_register(&cli_show_queues);
890         reload_queues();
891         return res;
892 }
893
894 int reload(void)
895 {
896         reload_queues();
897         return 0;
898 }
899
900 char *description(void)
901 {
902         return tdesc;
903 }
904
905 int usecount(void)
906 {
907         int res;
908         STANDARD_USECOUNT(res);
909         return res;
910 }
911
912 char *key()
913 {
914         return ASTERISK_GPL_KEY;
915 }