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