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