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