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