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