5ba34ee6f853cd83de8b0c74afc7011543da5672
[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         char *announce = NULL;
456         /* Hold the lock while we setup the outgoing calls */
457         ast_pthread_mutex_lock(&qe->parent->lock);
458         cur = qe->parent->members;
459         if (strlen(qe->announce))
460                 announce = qe->announce;
461         if (announceoverride && strlen(announceoverride))
462                 announce = announceoverride;
463         while(cur) {
464                 /* Get a technology/[device:]number pair */
465                 tmp = malloc(sizeof(struct localuser));
466                 if (!tmp) {
467                         ast_log(LOG_WARNING, "Out of memory\n");
468                         goto out;
469                 }
470                 memset(tmp, 0, sizeof(struct localuser));
471                 if (options) {
472                         if (strchr(options, 't'))
473                                 tmp->allowredirect_in = 1;
474                         if (strchr(options, 'T'))
475                                 tmp->allowredirect_out = 1;
476                         if (strchr(options, 'r'))
477                                 tmp->ringbackonly = 1;
478                         if (strchr(options, 'm'))
479                                 tmp->musiconhold = 1;
480                         if (strchr(options, 'd'))
481                                 tmp->dataquality = 1;
482                         if (strchr(options, 'H'))
483                                 tmp->allowdisconnect = 1;
484                 }
485                 if (url) {
486                         ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
487                 } else 
488                         ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
489
490                 strncpy(numsubst, cur->loc, sizeof(numsubst)-1);
491                 /* If we're dialing by extension, look at the extension to know what to dial */
492                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
493                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
494                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", qe->chan->exten,restofit);
495                         if (option_debug)
496                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
497                 }
498                 /* Request the peer */
499                 tmp->chan = ast_request(cur->tech, qe->chan->nativeformats, numsubst);
500                 if (!tmp->chan) {
501                         /* If we can't, just go on to the next call */
502 #if 0
503                         ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
504 #endif                  
505                         if (qe->chan->cdr)
506                                 ast_cdr_busy(qe->chan->cdr);
507                         free(tmp);
508                         cur = cur->next;
509                         continue;
510                 }
511 #if 0           
512                 /* Don't honor call forwarding on a queue! */
513                 if (strlen(tmp->chan->call_forward)) {
514                         if (option_verbose > 2)
515                                 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
516                         /* Setup parameters */
517                         strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
518                         strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
519                         chan->priority = 0;
520                         to = 0;
521                         ast_hangup(tmp->chan);
522                         free(tmp);
523                         cur = rest;
524                         break;
525                 }
526 #endif          
527                 tmp->chan->appl = "AppQueue";
528                 tmp->chan->data = "(Outgoing Line)";
529                 tmp->chan->whentohangup = 0;
530                 if (tmp->chan->callerid)
531                         free(tmp->chan->callerid);
532                 if (tmp->chan->ani)
533                         free(tmp->chan->ani);
534                 if (qe->chan->callerid)
535                         tmp->chan->callerid = strdup(qe->chan->callerid);
536                 else
537                         tmp->chan->callerid = NULL;
538                 if (qe->chan->ani)
539                         tmp->chan->ani = strdup(qe->chan->ani);
540                 else
541                         tmp->chan->ani = NULL;
542                 /* Presense of ADSI CPE on outgoing channel follows ours */
543                 tmp->chan->adsicpe = qe->chan->adsicpe;
544                 /* Place the call, but don't wait on the answer */
545                 res = ast_call(tmp->chan, numsubst, 0);
546                 if (res) {
547                         /* Again, keep going even if there's an error */
548                         if (option_debug)
549                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
550                         else if (option_verbose > 2)
551                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
552                         ast_hangup(tmp->chan);
553                         free(tmp);
554                         cur = cur->next;
555                         continue;
556                 } else
557                         if (option_verbose > 2)
558                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
559                 /* Put them in the list of outgoing thingies...  We're ready now. 
560                    XXX If we're forcibly removed, these outgoing calls won't get
561                    hung up XXX */
562                 tmp->stillgoing = -1;
563                 tmp->next = outgoing;
564                 outgoing = tmp;         
565                 /* If this line is up, don't try anybody else */
566                 if (outgoing->chan->_state == AST_STATE_UP)
567                         break;
568
569                 cur = cur->next;
570         }
571         if (qe->parent->timeout)
572                 to = qe->parent->timeout * 1000;
573         else
574                 to = -1;
575         ast_pthread_mutex_unlock(&qe->parent->lock);
576         
577         peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
578         if (!peer) {
579                 if (to) 
580                         /* Musta gotten hung up */
581                         res = -1;
582                  else 
583                         /* Nobody answered, next please? */
584                         res=0;
585                 
586                 goto out;
587         }
588         if (peer) {
589                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
590                    we will always return with -1 so that it is hung up properly after the 
591                    conversation.  */
592                 hanguptree(outgoing, peer);
593                 /* Stop music on hold */
594                 ast_moh_stop(qe->chan);
595                 outgoing = NULL;
596                 if (announce) {
597                         int res2;
598                         res2 = ast_streamfile(peer, announce, peer->language);
599                         /* XXX Need a function to wait on *both* streams XXX */
600                         if (!res2)
601                                 res2 = ast_waitstream(peer, "");
602                         else
603                                 res2 = 0;
604                         if (res2) {
605                                 /* Agent must have hung up */
606                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
607                                 ast_hangup(peer);
608                                 return -1;
609                         }
610                 }
611                 /* If appropriate, log that we have a destination channel */
612                 if (qe->chan->cdr)
613                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
614                 /* Make sure channels are compatible */
615                 res = ast_channel_make_compatible(qe->chan, peer);
616                 if (res < 0) {
617                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
618                         ast_hangup(peer);
619                         return -1;
620                 }
621                 if (!strcmp(qe->chan->type,"Zap")) {
622                         int x = 2;
623                         if (tmp->dataquality) x = 0;
624                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
625                 }                       
626                 if (!strcmp(peer->type,"Zap")) {
627                         int x = 2;
628                         if (tmp->dataquality) x = 0;
629                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
630                 }
631                 /* Drop out of the queue at this point, to prepare for next caller */
632                 leave_queue(qe);                        
633                 /* JDG: sendurl */
634                 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
635                         ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
636                         ast_channel_sendurl( peer, url );
637                 } /* /JDG */
638                 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
639
640                 if(bridge != AST_PBX_NO_HANGUP_PEER)
641                         ast_hangup(peer);
642
643                 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
644                 else res = bridge; /* bridge error, stay in the queue */
645         }       
646 out:
647         hanguptree(outgoing, NULL);
648         return res;
649 }
650
651 static int wait_a_bit(struct queue_ent *qe)
652 {
653         int retrywait;
654         /* Hold the lock while we setup the outgoing calls */
655         ast_pthread_mutex_lock(&qe->parent->lock);
656         retrywait = qe->parent->retry * 1000;
657         ast_pthread_mutex_unlock(&qe->parent->lock);
658         return ast_waitfordigit(qe->chan, retrywait);
659 }
660
661 static int valid_exit(struct queue_ent *qe, char digit)
662 {
663         char tmp[2];
664         if (!strlen(qe->context))
665                 return 0;
666         tmp[0] = digit;
667         tmp[1] = '\0';
668         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
669                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
670                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
671                 qe->chan->priority = 0;
672                 return 1;
673         }
674         return 0;
675 }
676
677 // [PHM 06/26/03]
678
679 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
680 {
681         struct member * ret = NULL ;
682         struct member *mem;
683         char buf[500] ;
684
685         if( q != NULL )
686         {
687                 mem = q->members ;
688
689                 while( mem != NULL ) {
690                         sprintf( buf, "%s/%s", mem->tech, mem->loc);
691
692                         if( strcmp( buf, interface ) == 0 ) {
693                                 ret = mem ;
694                                 break ;
695                         }
696                         else
697                                 mem = mem->next ;
698                 }
699         }
700
701         return( ret ) ;
702 }
703
704
705 static struct member * create_queue_node( char * interface )
706 {
707         struct member * cur ;
708         char * tmp ;
709         
710         /* Add a new member */
711
712         cur = malloc(sizeof(struct member));
713
714         if (cur) {
715                 memset(cur, 0, sizeof(struct member));
716                 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
717                 if ((tmp = strchr(cur->tech, '/')))
718                         *tmp = '\0';
719                 if ((tmp = strchr(interface, '/'))) {
720                         tmp++;
721                         strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
722                 } else
723                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
724         }
725
726         return( cur ) ;
727 }
728
729
730 static int rqm_exec(struct ast_channel *chan, void *data)
731 {
732         int res=-1;
733         struct localuser *u;
734         char *queuename;
735         struct member * node ;
736         struct member * look ;
737         char info[512];
738         char *interface=NULL;
739         struct ast_call_queue *q;
740         int found=0 ;
741
742         if (!data) {
743                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
744                 return -1;
745         }
746         
747         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
748         
749         /* Parse our arguments XXX Check for failure XXX */
750         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
751         queuename = info;
752         if (queuename) {
753                 interface = strchr(queuename, '|');
754                 if (interface) {
755                         *interface = '\0';
756                         interface++;
757                 }
758                 else
759                         interface = chan->name ;
760         }
761
762         if( ( q = queues) != NULL )
763         {
764                 while( q && ( res != 0 ) && (!found) ) 
765                 {
766                         ast_pthread_mutex_lock(&q->lock);
767                         if( strcmp( q->name, queuename) == 0 )
768                         {
769                                 // found queue, try to remove  interface
770                                 found=1 ;
771
772                                 if( ( node = interface_exists( q, interface ) ) != NULL )
773                                 {
774                                         if( ( look = q->members ) == node )
775                                         {
776                                                 // 1st
777                                                 q->members = node->next;
778                                         }
779                                         else
780                                         {
781                                                 while( look != NULL )
782                                                         if( look->next == node )
783                                                         {
784                                                                 look->next = node->next ;
785                                                                 break ;
786                                                         }
787                                                         else
788                                                                 look = look->next ;
789                                         }
790
791                                         free( node ) ;
792
793                                         ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", 
794                                                 interface, queuename);
795                                         res = 0 ;
796                                 }
797                                 else
798                                         ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
799                                                 "Not there\n", interface, queuename);
800                         }
801
802                         ast_pthread_mutex_unlock(&q->lock);
803                         q = q->next;
804                 }
805         }
806
807         if( ! found )
808                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
809
810         LOCAL_USER_REMOVE(u);
811         return res;
812 }
813
814
815
816 static int aqm_exec(struct ast_channel *chan, void *data)
817 {
818         int res=-1;
819         struct localuser *u;
820         char *queuename;
821         char info[512];
822         char *interface=NULL;
823         struct ast_call_queue *q;
824         struct member *save;
825         int found=0 ;
826
827         if (!data) {
828                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
829                 return -1;
830         }
831         
832         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
833         
834         /* Parse our arguments XXX Check for failure XXX */
835         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
836         queuename = info;
837         if (queuename) {
838                 interface = strchr(queuename, '|');
839                 if (interface) {
840                         *interface = '\0';
841                         interface++;
842                 }
843                 else
844                         interface = chan->name ;
845         }
846
847         if( ( q = queues) != NULL )
848         {
849                 while( q && ( res != 0 ) && (!found) ) 
850                 {
851                         ast_pthread_mutex_lock(&q->lock);
852                         if( strcmp( q->name, queuename) == 0 )
853                         {
854                                 // found queue, try to enable interface
855                                 found=1 ;
856
857                                 if( interface_exists( q, interface ) == NULL )
858                                 {
859                                         save = q->members ;
860                                         q->members = create_queue_node( interface ) ;
861
862                                         if( q->members != NULL )
863                                                 q->members->next = save ;
864                                         else
865                                                 q->members = save ;
866
867                                         ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
868                                         res = 0 ;
869                                 }
870                                 else
871                                         ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
872                                                 "Already there\n", interface, queuename);
873                         }
874
875                         ast_pthread_mutex_unlock(&q->lock);
876                         q = q->next;
877                 }
878         }
879
880         if( ! found )
881                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
882
883         LOCAL_USER_REMOVE(u);
884         return res;
885 }
886
887
888 static int queue_exec(struct ast_channel *chan, void *data)
889 {
890         int res=-1;
891         struct localuser *u;
892         char *queuename;
893         char info[512];
894         char *options = NULL;
895         char *url = NULL;
896         char *announceoverride = NULL;
897         
898         /* Our queue entry */
899         struct queue_ent qe;
900         
901         if (!data) {
902                 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
903                 return -1;
904         }
905         
906         LOCAL_USER_ADD(u);
907         
908         /* Parse our arguments XXX Check for failure XXX */
909         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
910         queuename = info;
911         if (queuename) {
912                 options = strchr(queuename, '|');
913                 if (options) {
914                         *options = '\0';
915                         options++;
916                         url = strchr(options, '|');
917                         if (url) {
918                                 *url = '\0';
919                                 url++;
920                                 announceoverride = strchr(url, '|');
921                                 if (announceoverride) {
922                                         *announceoverride = '\0';
923                                         announceoverride++;
924                                 }
925                         }
926                 }
927         }
928         printf("queue: %s, options: %s, url: %s, announce: %s\n",
929                 queuename, options, url, announceoverride);
930         /* Setup our queue entry */
931         memset(&qe, 0, sizeof(qe));
932         qe.chan = chan;
933         qe.start = time(NULL);
934         if (!join_queue(queuename, &qe)) {
935                 /* Start music on hold */
936                 ast_moh_start(chan, qe.moh);
937                 for (;;) {
938                         res = wait_our_turn(&qe);
939                         /* If they hungup, return immediately */
940                         if (res < 0) {
941                                 if (option_verbose > 2) {
942                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
943                                         res = -1;
944                                 }
945                                 break;
946                         }
947                         if (!res)
948                                 break;
949                         if (valid_exit(&qe, res))
950                                 break;
951                 }
952                 if (!res) {
953                         for (;;) {
954                                 res = try_calling(&qe, options, announceoverride, url);
955                                 if (res)
956                                         break;
957                                 res = wait_a_bit(&qe);
958                                 if (res < 0) {
959                                         if (option_verbose > 2) {
960                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
961                                                 res = -1;
962                                         }
963                                         break;
964                                 }
965                                 if (res && valid_exit(&qe, res))
966                                         break;
967                         }
968                 }
969                 /* Don't allow return code > 0 */
970                 if (res > 0 && res != AST_PBX_KEEPALIVE) {
971                         res = 0;        
972                         ast_moh_stop(chan);
973                 }
974                 leave_queue(&qe);
975         } else {
976                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
977                 res =  0;
978         }
979         LOCAL_USER_REMOVE(u);
980         return res;
981 }
982
983 static void reload_queues(void)
984 {
985         struct ast_call_queue *q, *ql, *qn;
986         struct ast_config *cfg;
987         char *cat, *tmp;
988         struct ast_variable *var;
989         struct member *prev, *cur;
990         int new;
991         cfg = ast_load("queues.conf");
992         if (!cfg) {
993                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
994                 return;
995         }
996         ast_pthread_mutex_lock(&qlock);
997         /* Mark all queues as dead for the moment */
998         q = queues;
999         while(q) {
1000                 q->dead = 1;
1001                 q = q->next;
1002         }
1003         /* Chug through config file */
1004         cat = ast_category_browse(cfg, NULL);
1005         while(cat) {
1006                 if (strcasecmp(cat, "general")) {
1007                         /* Look for an existing one */
1008                         q = queues;
1009                         while(q) {
1010                                 if (!strcmp(q->name, cat))
1011                                         break;
1012                                 q = q->next;
1013                         }
1014                         if (!q) {
1015                                 /* Make one then */
1016                                 q = malloc(sizeof(struct ast_call_queue));
1017                                 if (q) {
1018                                         /* Initialize it */
1019                                         memset(q, 0, sizeof(struct ast_call_queue));
1020                                         ast_pthread_mutex_init(&q->lock);
1021                                         strncpy(q->name, cat, sizeof(q->name));
1022                                         new = 1;
1023                                 } else new = 0;
1024                         } else
1025                                         new = 0;
1026                         if (q) {
1027                                 if (!new) 
1028                                         ast_pthread_mutex_lock(&q->lock);
1029                                 /* Re-initialize the queue */
1030                                 q->dead = 0;
1031                                 q->retry = 0;
1032                                 q->timeout = -1;
1033                                 q->maxlen = 0;
1034                                 free_members(q);
1035                                 strcpy(q->moh, "");
1036                                 strcpy(q->announce, "");
1037                                 strcpy(q->context, "");
1038                                 prev = NULL;
1039                                 var = ast_variable_browse(cfg, cat);
1040                                 while(var) {
1041                                         if (!strcasecmp(var->name, "member")) {
1042                                                 /* Add a new member */
1043                                                 cur = malloc(sizeof(struct member));
1044                                                 if (cur) {
1045                                                         memset(cur, 0, sizeof(struct member));
1046                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1047                                                         if ((tmp = strchr(cur->tech, '/')))
1048                                                                 *tmp = '\0';
1049                                                         if ((tmp = strchr(var->value, '/'))) {
1050                                                                 tmp++;
1051                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1052                                                         } else
1053                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1054                                                         if (prev)
1055                                                                 prev->next = cur;
1056                                                         else
1057                                                                 q->members = cur;
1058                                                         prev = cur;
1059                                                 }
1060                                         } else if (!strcasecmp(var->name, "music")) {
1061                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1062                                         } else if (!strcasecmp(var->name, "announce")) {
1063                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1064                                         } else if (!strcasecmp(var->name, "context")) {
1065                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
1066                                         } else if (!strcasecmp(var->name, "timeout")) {
1067                                                 q->timeout = atoi(var->value);
1068                                         } else if (!strcasecmp(var->name, "retry")) {
1069                                                 q->retry = atoi(var->value);
1070                                         } else if (!strcasecmp(var->name, "maxlen")) {
1071                                                 q->maxlen = atoi(var->value);
1072                                         } else {
1073                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1074                                         }
1075                                         var = var->next;
1076                                 }
1077                                 if (q->retry < 1)
1078                                         q->retry = DEFAULT_RETRY;
1079                                 if (q->timeout < 0)
1080                                         q->timeout = DEFAULT_TIMEOUT;
1081                                 if (q->maxlen < 0)
1082                                         q->maxlen = 0;
1083                                 if (!new) 
1084                                         ast_pthread_mutex_unlock(&q->lock);
1085                                 if (new) {
1086                                         q->next = queues;
1087                                         queues = q;
1088                                 }
1089                         }
1090                 }
1091                 cat = ast_category_browse(cfg, cat);
1092         }
1093         ast_destroy(cfg);
1094         q = queues;
1095         ql = NULL;
1096         while(q) {
1097                 qn = q->next;
1098                 if (q->dead) {
1099                         if (ql)
1100                                 ql->next = q->next;
1101                         else
1102                                 queues = q->next;
1103                         if (!q->count) {
1104                                 free(q);
1105                         } else
1106                                 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1107                 } else
1108                         ql = q;
1109                 q = qn;
1110         }
1111         ast_pthread_mutex_unlock(&qlock);
1112 }
1113
1114 static int queues_show(int fd, int argc, char **argv)
1115 {
1116         struct ast_call_queue *q;
1117         struct queue_ent *qe;
1118         struct member *mem;
1119         int pos;
1120         time_t now;
1121         char max[80];
1122         
1123         time(&now);
1124         if (argc != 2)
1125                 return RESULT_SHOWUSAGE;
1126         q = queues;
1127         if (!q) {       
1128                 ast_cli(fd, "No queues.\n");
1129                 return RESULT_SUCCESS;
1130         }
1131         while(q) {
1132                 ast_pthread_mutex_lock(&q->lock);
1133                 if (q->maxlen)
1134                         snprintf(max, sizeof(max), "%d", q->maxlen);
1135                 else
1136                         strcpy(max, "unlimited");
1137                 ast_cli(fd, "%-12.12s has %d calls (max %s)\n", q->name, q->count, max);
1138                 if (q->members) {
1139                         ast_cli(fd, "   Members: \n");
1140                         for (mem = q->members; mem; mem = mem->next) 
1141                                 ast_cli(fd, "      %s/%s\n", mem->tech, mem->loc);
1142                 } else
1143                         ast_cli(fd, "   No Members\n");
1144                 if (q->head) {
1145                         pos = 1;
1146                         ast_cli(fd, "   Callers: \n");
1147                         for (qe = q->head; qe; qe = qe->next) 
1148                                 ast_cli(fd, "      %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
1149                                                                 (now - qe->start) / 60, (now - qe->start) % 60);
1150                 } else
1151                         ast_cli(fd, "   No Callers\n");
1152                 ast_cli(fd, "\n");
1153                 ast_pthread_mutex_unlock(&q->lock);
1154                 q = q->next;
1155         }
1156         return RESULT_SUCCESS;
1157 }
1158
1159 /* JDG: callback to display queues status in manager */
1160 static int manager_queues_show( struct mansession *s, struct message *m )
1161 {
1162         char *a[] = { "show", "queues" };
1163         return queues_show( s->fd, 2, a );
1164 } /* /JDG */
1165
1166
1167 /* Dump queue status */
1168 static int manager_queues_status( struct mansession *s, struct message *m )
1169 {
1170         time_t now;
1171         int pos;
1172         struct ast_call_queue *q;
1173         struct queue_ent *qe;
1174         astman_send_ack(s, "Queue status will follow");
1175         time(&now);
1176         q = queues;
1177         while(q) {
1178                 ast_pthread_mutex_lock(&q->lock);
1179                 ast_cli(s->fd, "Event: QueueParams\r\n"
1180                                         "Queue: %s\r\n"
1181                                         "Max: %d\r\n"
1182                                         "Calls: %d\r\n"
1183                                         "\r\n",
1184                                                 q->name, q->maxlen, q->count);
1185 #if 0
1186                 /* Do we care about queue members? */                                   
1187                 for (mem = q->members; mem; mem = mem->next) 
1188                         ast_cli(fd, "      %s/%s\n", mem->tech, mem->loc);
1189 #endif                  
1190                 pos = 1;
1191                 for (qe = q->head; qe; qe = qe->next) 
1192                         ast_cli(s->fd, "Event: QueueMember\r\n"
1193                                 "Queue: %s\r\n"
1194                                 "Position: %d\r\n"
1195                                 "Channel: %s\r\n"
1196                                 "CallerID: %s\r\n"
1197                                 "Wait: %ld\r\n"
1198                                 "\r\n", 
1199                                         q->name, pos++, qe->chan->name, qe->chan->callerid ? qe->chan->callerid : "", now - qe->start);
1200                 ast_pthread_mutex_unlock(&q->lock);
1201                 q = q->next;
1202         }
1203         return RESULT_SUCCESS;
1204 }
1205
1206 static char show_queues_usage[] = 
1207 "Usage: show queues\n"
1208 "       Provides summary information on call queues.\n";
1209
1210 static struct ast_cli_entry cli_show_queues = {
1211         { "show", "queues", NULL }, queues_show, 
1212         "Show status of queues", show_queues_usage, NULL };
1213
1214 int unload_module(void)
1215 {
1216         STANDARD_HANGUP_LOCALUSERS;
1217         ast_cli_unregister(&cli_show_queues);
1218         ast_manager_unregister( "Queues" );
1219         ast_manager_unregister( "QueueStatus" );
1220         return ast_unregister_application(app);
1221 }
1222
1223 int load_module(void)
1224 {
1225         int res;
1226         res = ast_register_application(app, queue_exec, synopsis, descrip);
1227         if (!res) {
1228                 ast_cli_register(&cli_show_queues);
1229                 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1230                 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1231
1232                 // [PHM 06/26/03]
1233                 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1234                 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1235         }
1236         reload_queues();
1237         return res;
1238 }
1239
1240
1241 int reload(void)
1242 {
1243         reload_queues();
1244         return 0;
1245 }
1246
1247 char *description(void)
1248 {
1249         return tdesc;
1250 }
1251
1252 int usecount(void)
1253 {
1254         int res;
1255         STANDARD_USECOUNT(res);
1256         return res;
1257 }
1258
1259 char *key()
1260 {
1261         return ASTERISK_GPL_KEY;
1262 }