a406eea435803890f7a615d69aa47ac672efb4d6
[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  * These features added by David C. Troy <dave@toad.net>:
11  *    - Per-queue holdtime calculation
12  *    - Estimated holdtime announcement
13  *    - Position announcement
14  *    - Abandoned/completed call counters
15  *    - Failout timer passed as optional app parameter
16  *    - Optional monitoring of calls, started when call is answered
17  *
18  * Patch Version 1.07 2003-12-24 01
19  *
20  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
21  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
22  *
23  * Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
24  * by Matthew Enger <m.enger@xi.com.au>
25  *
26  * This program is free software, distributed under the terms of
27  * the GNU General Public License
28  */
29
30 #include <asterisk/lock.h>
31 #include <asterisk/file.h>
32 #include <asterisk/logger.h>
33 #include <asterisk/channel.h>
34 #include <asterisk/pbx.h>
35 #include <asterisk/options.h>
36 #include <asterisk/module.h>
37 #include <asterisk/translate.h>
38 #include <asterisk/say.h>
39 #include <asterisk/parking.h>
40 #include <asterisk/musiconhold.h>
41 #include <asterisk/cli.h>
42 #include <asterisk/manager.h>
43 #include <asterisk/config.h>
44 #include <asterisk/monitor.h>
45 #include <asterisk/utils.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <sys/time.h>
53 #include <sys/signal.h>
54 #include <netinet/in.h>
55
56 #include "../astconf.h"
57
58 #include <pthread.h>
59
60 #define QUEUE_STRATEGY_RINGALL          0
61 #define QUEUE_STRATEGY_ROUNDROBIN       1
62 #define QUEUE_STRATEGY_LEASTRECENT      2
63 #define QUEUE_STRATEGY_FEWESTCALLS      3
64 #define QUEUE_STRATEGY_RANDOM           4
65 #define QUEUE_STRATEGY_RRMEMORY         5
66
67 static struct strategy {
68         int strategy;
69         char *name;
70 } strategies[] = {
71         { QUEUE_STRATEGY_RINGALL, "ringall" },
72         { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
73         { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
74         { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
75         { QUEUE_STRATEGY_RANDOM, "random" },
76         { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
77 };
78
79 #define DEFAULT_RETRY           5
80 #define DEFAULT_TIMEOUT         15
81 #define RECHECK                         1               /* Recheck every second to see we we're at the top yet */
82
83 static char *tdesc = "True Call Queueing";
84
85 static char *app = "Queue";
86
87 static char *synopsis = "Queue a call for a call queue";
88
89 static char *descrip =
90 "  Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
91 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
92 "  This application returns -1 if the originating channel hangs up, or if the\n"
93 "call is bridged and  either of the parties in the bridge terminate the call.\n"
94 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
95 "The option string may contain zero or more of the following characters:\n"
96 "      't' -- allow the called user transfer the calling user\n"
97 "      'T' -- to allow the calling user to transfer the call.\n"
98 "      'd' -- data-quality (modem) call (minimum delay).\n"
99 "      'H' -- allow caller to hang up by hitting *.\n"
100 "      'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
101 "      'r' -- ring instead of playing MOH\n"
102 "  In addition to transferring the call, a call may be parked and then picked\n"
103 "up by another user.\n"
104 "  The optional URL will be sent to the called party if the channel supports\n"
105 "it.\n"
106 "  The timeout will cause the queue to fail out after a specified number of\n"
107 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
108
109 // [PHM 06/26/03]
110 static char *app_aqm = "AddQueueMember" ;
111 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
112 static char *app_aqm_descrip =
113 "   AddQueueMember(queuename[|interface[|penalty]]):\n"
114 "Dynamically adds interface to an existing queue.\n"
115 "If the interface is already in the queue and there exists an n+101 priority\n"
116 "then it will then jump to this priority.  Otherwise it will return an error\n"
117 "Returns -1 if there is an error.\n"
118 "Example: AddQueueMember(techsupport|SIP/3000)\n"
119 "";
120
121 static char *app_rqm = "RemoveQueueMember" ;
122 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
123 static char *app_rqm_descrip =
124 "   RemoveQueueMember(queuename[|interface]):\n"
125 "Dynamically removes interface to an existing queue\n"
126 "If the interface is NOT in the queue and there exists an n+101 priority\n"
127 "then it will then jump to this priority.  Otherwise it will return an error\n"
128 "Returns -1 if there is an error.\n"
129 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
130 "";
131
132 /* We define a customer "local user" structure because we
133    use it not only for keeping track of what is in use but
134    also for keeping track of who we're dialing. */
135
136 struct localuser {
137         struct ast_channel *chan;
138         char numsubst[256];
139         char tech[40];
140         int stillgoing;
141         int metric;
142         int allowredirect_in;
143         int allowredirect_out;
144         int ringbackonly;
145         int musiconhold;
146         int dataquality;
147         int allowdisconnect;
148         struct member *member;
149         struct localuser *next;
150 };
151
152 LOCAL_USER_DECL;
153
154 struct queue_ent {
155         struct ast_call_queue *parent;  /* What queue is our parent */
156         char moh[80];                   /* Name of musiconhold to be used */
157         char announce[80];              /* Announcement to play for member when call is answered */
158         char context[80];               /* Context when user exits queue */
159         int pos;                        /* Where we are in the queue */
160         int last_pos_said;              /* Last position we told the user */
161         time_t last_pos;                /* Last time we told the user their position */
162         int opos;                       /* Where we started in the queue */
163         int handled;                    /* Whether our call was handled */
164         time_t start;                   /* When we started holding */
165         int queuetimeout;               /* How many seconds before timing out of queue */
166         struct ast_channel *chan;       /* Our channel */
167         struct queue_ent *next;         /* The next queue entry */
168 };
169
170 struct member {
171         char tech[80];                  /* Technology */
172         char loc[256];                  /* Location */
173         int penalty;                    /* Are we a last resort? */
174         int calls;
175         int dynamic;                    /* Are we dynamically added? */
176         time_t lastcall;                /* When last successful call was hungup */
177         struct member *next;            /* Next member */
178 };
179
180 struct ast_call_queue {
181         ast_mutex_t     lock;   
182         char name[80];                  /* Name of the queue */
183         char moh[80];                   /* Name of musiconhold to be used */
184         char announce[80];              /* Announcement to play when call is answered */
185         char context[80];               /* Context for this queue */
186         int strategy;                   /* Queueing strategy */
187         int announcefrequency;          /* How often to announce their position */
188         int announceholdtime;           /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
189         int holdtime;                   /* Current avg holdtime for this queue, based on recursive boxcar filter */
190         int callscompleted;             /* Number of queue calls completed */
191         int callsabandoned;             /* Number of queue calls abandoned */
192         int servicelevel;               /* seconds setting for servicelevel*/
193         int callscompletedinsl;         /* Number of queue calls answererd with servicelevel*/
194         char monfmt[8];                 /* Format to use when recording calls */
195         int monjoin;                    /* Should we join the two files when we are done with the call */
196         char sound_next[80];            /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
197         char sound_thereare[80];        /* Sound file: "There are currently" (def. queue-thereare) */
198         char sound_calls[80];           /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
199         char sound_holdtime[80];        /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
200         char sound_minutes[80];         /* Sound file: "minutes." (def. queue-minutes) */
201         char sound_thanks[80];          /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
202
203         int count;                      /* How many entries are in the queue */
204         int maxlen;                     /* Max number of entries in queue */
205
206         int dead;                       /* Whether this queue is dead or not */
207         int retry;                      /* Retry calling everyone after this amount of time */
208         int timeout;                    /* How long to wait for an answer */
209         
210         /* Queue strategy things */
211         
212         int rrpos;                      /* Round Robin - position */
213         int wrapped;                    /* Round Robin - wrapped around? */
214
215         struct member *members;         /* Member channels to be tried */
216         struct queue_ent *head;         /* Start of the actual queue */
217         struct ast_call_queue *next;    /* Next call queue */
218 };
219
220 static struct ast_call_queue *queues = NULL;
221 AST_MUTEX_DEFINE_STATIC(qlock);
222
223 static char *int2strat(int strategy)
224 {
225         int x;
226         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
227                 if (strategy == strategies[x].strategy)
228                         return strategies[x].name;
229         }
230         return "<unknown>";
231 }
232
233 static int strat2int(char *strategy)
234 {
235         int x;
236         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
237                 if (!strcasecmp(strategy, strategies[x].name))
238                         return strategies[x].strategy;
239         }
240         return -1;
241 }
242
243 static int join_queue(char *queuename, struct queue_ent *qe)
244 {
245         struct ast_call_queue *q;
246         struct queue_ent *cur, *prev = NULL;
247         int res = -1;
248         int pos = 0;
249         ast_mutex_lock(&qlock);
250         q = queues;
251         while(q) {
252                 if (!strcasecmp(q->name, queuename)) {
253                         /* This is our one */
254                         ast_mutex_lock(&q->lock);
255                         if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
256                                 /* There's space for us, put us at the end */
257                                 prev = NULL;
258                                 cur = q->head;
259                                 while(cur) {
260                                         cur->pos = ++pos;
261                                         prev = cur;
262                                         cur = cur->next;
263                                 }
264                                 if (prev)
265                                         prev->next = qe;
266                                 else
267                                         q->head = qe;
268                                 /* Fix additional pointers and
269                                   information  */
270                                 qe->next = NULL;
271                                 qe->parent = q;
272                                 qe->pos = ++pos;
273                                 qe->opos = pos;
274                                 strncpy(qe->moh, q->moh, sizeof(qe->moh));
275                                 strncpy(qe->announce, q->announce, sizeof(qe->announce));
276                                 strncpy(qe->context, q->context, sizeof(qe->context));
277                                 q->count++;
278                                 res = 0;
279                                 manager_event(EVENT_FLAG_CALL, "Join", 
280                                                                 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
281                                                                 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count );
282 #if 0
283 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
284 #endif
285                         }
286                         ast_mutex_unlock(&q->lock);
287                         break;
288                 }
289                 q = q->next;
290         }
291         ast_mutex_unlock(&qlock);
292         return res;
293 }
294
295 static void free_members(struct ast_call_queue *q, int all)
296 {
297         /* Free non-dynamic members */
298         struct member *curm, *next, *prev;
299         curm = q->members;
300         prev = NULL;
301         while(curm) {
302                 next = curm->next;
303                 if (all || !curm->dynamic) {
304                         if (prev)
305                                 prev->next = next;
306                         else
307                                 q->members = next;
308                         free(curm);
309                 } else 
310                         prev = curm;
311                 curm = next;
312         }
313 }
314
315 static void destroy_queue(struct ast_call_queue *q)
316 {
317         struct ast_call_queue *cur, *prev = NULL;
318         ast_mutex_lock(&qlock);
319         cur = queues;
320         while(cur) {
321                 if (cur == q) {
322                         if (prev)
323                                 prev->next = cur->next;
324                         else
325                                 queues = cur->next;
326                 } else {
327                         prev = cur;
328                 }
329                 cur = cur->next;
330         }
331         ast_mutex_unlock(&qlock);
332         free_members(q, 1);
333         free(q);
334 }
335
336 static int play_file(struct ast_channel *chan, char *filename)
337 {
338         int res;
339
340         ast_stopstream(chan);
341         res = ast_streamfile(chan, filename, chan->language);
342
343         if (!res)
344                 res = ast_waitstream(chan, "");
345         else
346                 res = 0;
347
348         if (res) {
349                 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
350                 res = 0;
351         }
352         ast_stopstream(chan);
353
354         return res;
355 }
356
357 static int say_position(struct queue_ent *qe)
358 {
359         int res = 0, avgholdmins;
360         time_t now;
361
362         /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
363         time(&now);
364         if ( (now - qe->last_pos) < 15 )
365                 return -1;
366
367         /* If either our position has changed, or we are over the freq timer, say position */
368         if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
369                 return -1;
370
371         ast_moh_stop(qe->chan);
372         /* Say we're next, if we are */
373         if (qe->pos == 1) {
374                 res += play_file(qe->chan, qe->parent->sound_next);
375                 goto posout;
376         } else {
377                 res += play_file(qe->chan, qe->parent->sound_thereare);
378                 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
379                 res += play_file(qe->chan, qe->parent->sound_calls);
380         }
381
382         /* Round hold time to nearest minute */
383         avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60;
384         if (option_verbose > 2)
385                 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins);
386
387         /* If the hold time is >1 min, if it's enabled, and if it's not
388            supposed to be only once and we have already said it, say it */
389         if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
390                 res += play_file(qe->chan, qe->parent->sound_holdtime);
391                 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
392                 res += play_file(qe->chan, qe->parent->sound_minutes);
393         }
394
395         posout:
396         /* Set our last_pos indicators */
397         qe->last_pos = now;
398         qe->last_pos_said = qe->pos;
399
400         if (option_verbose > 2)
401                 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
402         res += play_file(qe->chan, qe->parent->sound_thanks);
403         ast_moh_start(qe->chan, qe->moh);
404
405         return (res>0);
406 }
407
408 static void record_abandoned(struct queue_ent *qe)
409 {
410         ast_mutex_lock(&qe->parent->lock);
411         qe->parent->callsabandoned++;
412         ast_mutex_unlock(&qe->parent->lock);
413 }
414
415 static void recalc_holdtime(struct queue_ent *qe)
416 {
417         int oldvalue, newvalue;
418
419         /* Calculate holdtime using a recursive boxcar filter */
420         /* Thanks to SRT for this contribution */
421         /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
422
423         newvalue = time(NULL) - qe->start;
424
425         ast_mutex_lock(&qe->parent->lock);
426         if (newvalue <= qe->parent->servicelevel)
427                 qe->parent->callscompletedinsl++;
428         oldvalue = qe->parent->holdtime;
429         qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
430         ast_mutex_unlock(&qe->parent->lock);
431 }
432
433
434 static void leave_queue(struct queue_ent *qe)
435 {
436         struct ast_call_queue *q;
437         struct queue_ent *cur, *prev = NULL;
438         int pos = 0;
439         q = qe->parent;
440         if (!q)
441                 return;
442         ast_mutex_lock(&q->lock);
443
444         prev = NULL;
445         cur = q->head;
446         while(cur) {
447                 if (cur == qe) {
448                         q->count--;
449
450                         /* Take us out of the queue */
451                         manager_event(EVENT_FLAG_CALL, "Leave",
452                                  "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
453                                  qe->chan->name, q->name,  q->count);
454 #if 0
455 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
456 #endif
457                         /* Take us out of the queue */
458                         if (prev)
459                                 prev->next = cur->next;
460                         else
461                                 q->head = cur->next;
462                 } else {
463                         /* Renumber the people after us in the queue based on a new count */
464                         cur->pos = ++pos;
465                         prev = cur;
466                 }
467                 cur = cur->next;
468         }
469         ast_mutex_unlock(&q->lock);
470         if (q->dead && !q->count) {     
471                 /* It's dead and nobody is in it, so kill it */
472                 destroy_queue(q);
473         }
474 }
475
476 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
477 {
478         /* Hang up a tree of stuff */
479         struct localuser *oo;
480         while(outgoing) {
481                 /* Hangup any existing lines we have open */
482                 if (outgoing->chan && (outgoing->chan != exception))
483                         ast_hangup(outgoing->chan);
484                 oo = outgoing;
485                 outgoing=outgoing->next;
486                 free(oo);
487         }
488 }
489
490 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
491 {
492         int res;
493         /* Request the peer */
494         tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
495         if (!tmp->chan) {                       /* 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                 tmp->stillgoing = 0;
502                 return 0;
503         }
504         tmp->chan->appl = "AppQueue";
505         tmp->chan->data = "(Outgoing Line)";
506         tmp->chan->whentohangup = 0;
507         if (tmp->chan->callerid)
508                 free(tmp->chan->callerid);
509         if (tmp->chan->ani)
510                 free(tmp->chan->ani);
511         if (qe->chan->callerid)
512                 tmp->chan->callerid = strdup(qe->chan->callerid);
513         else
514                 tmp->chan->callerid = NULL;
515         if (qe->chan->ani)
516                 tmp->chan->ani = strdup(qe->chan->ani);
517         else
518                 tmp->chan->ani = NULL;
519         /* Presense of ADSI CPE on outgoing channel follows ours */
520         tmp->chan->adsicpe = qe->chan->adsicpe;
521         /* Place the call, but don't wait on the answer */
522         res = ast_call(tmp->chan, tmp->numsubst, 0);
523         if (res) {
524                 /* Again, keep going even if there's an error */
525                 if (option_debug)
526                         ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
527                 else if (option_verbose > 2)
528                         ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
529                 ast_hangup(tmp->chan);
530                 tmp->chan = NULL;
531                 tmp->stillgoing = 0;
532                 return 0;
533         } else
534                 if (option_verbose > 2)
535                         ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
536         return 0;
537 }
538
539 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
540 {
541         struct localuser *cur;
542         struct localuser *best;
543         int bestmetric=0;
544         do {
545                 best = NULL;
546                 cur = outgoing;
547                 while(cur) {
548                         if (cur->stillgoing &&                                                  /* Not already done */
549                                 !cur->chan &&                                                           /* Isn't already going */
550                                 (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
551                                         bestmetric = cur->metric;
552                                         best = cur;
553                         }
554                         cur = cur->next;
555                 }
556                 if (best) {
557                         if (!qe->parent->strategy) {
558                                 /* Ring everyone who shares this best metric (for ringall) */
559                                 cur = outgoing;
560                                 while(cur) {
561                                         if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
562                                                 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
563                                                 ring_entry(qe, cur);
564                                         }
565                                         cur = cur->next;
566                                 }
567                         } else {
568                                 /* Ring just the best channel */
569                                 ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
570                                 ring_entry(qe, best);
571                         }
572                 }
573         } while (best && !best->chan);
574         if (!best) {
575                 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
576                 return 0;
577         }
578         return 1;
579 }
580
581 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
582 {
583         struct localuser *cur;
584         struct localuser *best;
585         int bestmetric=0;
586         best = NULL;
587         cur = outgoing;
588         while(cur) {
589                 if (cur->stillgoing &&                                                  /* Not already done */
590                         !cur->chan &&                                                           /* Isn't already going */
591                         (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
592                                 bestmetric = cur->metric;
593                                 best = cur;
594                 }
595                 cur = cur->next;
596         }
597         if (best) {
598                 /* Ring just the best channel */
599                 ast_log(LOG_DEBUG, "Next is '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
600                 qe->parent->rrpos = best->metric % 1000;
601         } else {
602                 /* Just increment rrpos */
603                 if (!qe->parent->wrapped) {
604                         /* No more channels, start over */
605                         qe->parent->rrpos = 0;
606                 } else {
607                         /* Prioritize next entry */
608                         qe->parent->rrpos++;
609                 }
610         }
611         qe->parent->wrapped = 0;
612         return 0;
613 }
614
615 static int valid_exit(struct queue_ent *qe, char digit)
616 {
617         char tmp[2];
618         if (ast_strlen_zero(qe->context))
619                 return 0;
620         tmp[0] = digit;
621         tmp[1] = '\0';
622         if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
623                 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
624                 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
625                 qe->chan->priority = 0;
626                 return 1;
627         }
628         return 0;
629 }
630
631 #define MAX 256
632
633 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *digit)
634 {
635         char *queue = qe->parent->name;
636         struct localuser *o;
637         int found;
638         int numlines;
639         int sentringing = 0;
640         int numbusies = 0;
641         int orig = *to;
642         struct ast_frame *f;
643         struct localuser *peer = NULL;
644         struct ast_channel *watchers[MAX];
645         int pos;
646         struct ast_channel *winner;
647         struct ast_channel *in = qe->chan;
648         
649         while(*to && !peer) {
650                 o = outgoing;
651                 found = -1;
652                 pos = 1;
653                 numlines = 0;
654                 watchers[0] = in;
655                 while(o) {
656                         /* Keep track of important channels */
657                         if (o->stillgoing && o->chan) {
658                                 watchers[pos++] = o->chan;
659                                 found = 1;
660                         }
661                         o = o->next;
662                         numlines++;
663                 }
664                 if (found < 0) {
665                         if (numlines == numbusies) {
666                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
667                         } else {
668                                 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
669                         }
670                         *to = 0;
671                         return NULL;
672                 }
673                 winner = ast_waitfor_n(watchers, pos, to);
674                 o = outgoing;
675                 while(o) {
676                         if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
677                                 if (!peer) {
678                                         if (option_verbose > 2)
679                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
680                                         peer = o;
681                                         *allowredir_in = o->allowredirect_in;
682                                         *allowredir_out = o->allowredirect_out;
683                                         *allowdisconnect = o->allowdisconnect;
684                                 }
685                         } else if (o->chan && (o->chan == winner)) {
686                                 f = ast_read(winner);
687                                 if (f) {
688                                         if (f->frametype == AST_FRAME_CONTROL) {
689                                                 switch(f->subclass) {
690                                             case AST_CONTROL_ANSWER:
691                                                         /* This is our guy if someone answered. */
692                                                         if (!peer) {
693                                                                 if (option_verbose > 2)
694                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
695                                                                 peer = o;
696                                                                 *allowredir_in = o->allowredirect_in;
697                                                                 *allowredir_out = o->allowredirect_out;
698                                                                 *allowdisconnect = o->allowdisconnect;
699                                                         }
700                                                         break;
701                                                 case AST_CONTROL_BUSY:
702                                                         if (option_verbose > 2)
703                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
704                                                         o->stillgoing = 0;
705                                                         if (in->cdr)
706                                                                 ast_cdr_busy(in->cdr);
707                                                         ast_hangup(o->chan);
708                                                         o->chan = NULL;
709                                                         if (qe->parent->strategy)
710                                                                 ring_one(qe, outgoing);
711                                                         numbusies++;
712                                                         break;
713                                                 case AST_CONTROL_CONGESTION:
714                                                         if (option_verbose > 2)
715                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
716                                                         o->stillgoing = 0;
717                                                         if (in->cdr)
718                                                                 ast_cdr_busy(in->cdr);
719                                                         ast_hangup(o->chan);
720                                                         o->chan = NULL;
721                                                         if (qe->parent->strategy)
722                                                                 ring_one(qe, outgoing);
723                                                         numbusies++;
724                                                         break;
725                                                 case AST_CONTROL_RINGING:
726                                                         if (option_verbose > 2)
727                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
728                                                         if (!sentringing) {
729 #if 0
730                                                                 ast_indicate(in, AST_CONTROL_RINGING);
731 #endif                                                          
732                                                                 sentringing++;
733                                                         }
734                                                         break;
735                                                 case AST_CONTROL_OFFHOOK:
736                                                         /* Ignore going off hook */
737                                                         break;
738                                                 default:
739                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
740                                                 }
741                                         }
742                                         ast_frfree(f);
743                                 } else {
744                                         o->stillgoing = 0;
745                                         ast_hangup(o->chan);
746                                         o->chan = NULL;
747                                         if (qe->parent->strategy)
748                                                 ring_one(qe, outgoing);
749                                 }
750                         }
751                         o = o->next;
752                 }
753                 if (winner == in) {
754                         f = ast_read(in);
755 #if 0
756                         if (f && (f->frametype != AST_FRAME_VOICE))
757                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
758                         else if (!f || (f->frametype != AST_FRAME_VOICE))
759                                 printf("Hangup received on %s\n", in->name);
760 #endif
761                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
762                                 /* Got hung up */
763                                 *to=-1;
764                                 return NULL;
765                         }
766                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
767                             if (option_verbose > 3)
768                                         ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
769                                 *to=0;
770                                 return NULL;
771                         }
772                         if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
773                                 if (option_verbose > 3)
774                                         ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
775                                 *to=0;
776                                 *digit=f->subclass;
777                                 return NULL;
778                         }
779                 }
780                 if (!*to && (option_verbose > 2))
781                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
782         }
783
784         return peer;
785         
786 }
787
788 static int wait_our_turn(struct queue_ent *qe, int ringing)
789 {
790         struct queue_ent *ch;
791         int res = 0;
792         time_t now;
793
794         /* This is the holding pen for callers 2 through maxlen */
795         for (;;) {
796                 /* Atomically read the parent head -- does not need a lock */
797                 ch = qe->parent->head;
798
799                 /* If we are now at the top of the head, break out */
800                 if (qe == ch)
801                         break;
802
803                 /* If we have timed out, break out */
804                 if ( qe->queuetimeout ) {
805                         time(&now);
806                         if ( (now - qe->start) >= qe->queuetimeout )
807                         break;
808                 }
809
810                 /* Make a position announcement, if enabled */
811                 if (qe->parent->announcefrequency && !ringing)
812                         say_position(qe);
813
814
815                 /* Wait a second before checking again */
816                 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
817                 if (res)
818                         break;
819         }
820         return res;
821 }
822
823 static int update_queue(struct ast_call_queue *q, struct member *member)
824 {
825         struct member *cur;
826         /* Since a reload could have taken place, we have to traverse the list to
827                 be sure it's still valid */
828         ast_mutex_lock(&q->lock);
829         cur = q->members;
830         while(cur) {
831                 if (member == cur) {
832                         time(&cur->lastcall);
833                         cur->calls++;
834                         break;
835                 }
836                 cur = cur->next;
837         }
838         q->callscompleted++;
839         ast_mutex_unlock(&q->lock);
840         return 0;
841 }
842
843 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
844 {
845         switch (q->strategy) {
846         case QUEUE_STRATEGY_RINGALL:
847                 /* Everyone equal, except for penalty */
848                 tmp->metric = mem->penalty * 1000000;
849                 break;
850         case QUEUE_STRATEGY_ROUNDROBIN:
851                 if (!pos) {
852                         if (!q->wrapped) {
853                                 /* No more channels, start over */
854                                 q->rrpos = 0;
855                         } else {
856                                 /* Prioritize next entry */
857                                 q->rrpos++;
858                         }
859                         q->wrapped = 0;
860                 }
861                 /* Fall through */
862         case QUEUE_STRATEGY_RRMEMORY:
863                 if (pos < q->rrpos) {
864                         tmp->metric = 1000 + pos;
865                 } else {
866                         if (pos > q->rrpos) {
867                                 /* Indicate there is another priority */
868                                 q->wrapped = 1;
869                         }
870                         tmp->metric = pos;
871                 }
872                 tmp->metric += mem->penalty * 1000000;
873                 break;
874         case QUEUE_STRATEGY_RANDOM:
875                 tmp->metric = rand() % 1000;
876                 tmp->metric += mem->penalty * 1000000;
877                 break;
878         case QUEUE_STRATEGY_FEWESTCALLS:
879                 tmp->metric = mem->calls;
880                 tmp->metric += mem->penalty * 1000000;
881                 break;
882         case QUEUE_STRATEGY_LEASTRECENT:
883                 if (!mem->lastcall)
884                         tmp->metric = 0;
885                 else
886                         tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
887                 tmp->metric += mem->penalty * 1000000;
888                 break;
889         default:
890                 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
891                 break;
892         }
893         return 0;
894 }
895
896 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
897 {
898         struct member *cur;
899         struct localuser *outgoing=NULL, *tmp = NULL;
900         int to;
901         int allowredir_in=0;
902         int allowredir_out=0;
903         int allowdisconnect=0;
904         char restofit[AST_MAX_EXTENSION];
905         char oldexten[AST_MAX_EXTENSION]="";
906         char oldcontext[AST_MAX_EXTENSION]="";
907         char queuename[256]="";
908         char *newnum;
909         char *monitorfilename;
910         struct ast_channel *peer;
911         struct localuser *lpeer;
912         struct member *member;
913         int res = 0, bridge = 0;
914         int zapx = 2;
915         int x=0;
916         char *announce = NULL;
917         char digit = 0;
918         time_t callstart;
919         time_t now;
920         struct ast_bridge_config config;
921         /* Hold the lock while we setup the outgoing calls */
922         ast_mutex_lock(&qe->parent->lock);
923         strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
924         time(&now);
925         cur = qe->parent->members;
926         if (!ast_strlen_zero(qe->announce))
927                 announce = qe->announce;
928         if (announceoverride && !ast_strlen_zero(announceoverride))
929                 announce = announceoverride;
930         while(cur) {
931                 /* Get a technology/[device:]number pair */
932                 tmp = malloc(sizeof(struct localuser));
933                 if (!tmp) {
934                         ast_mutex_unlock(&qe->parent->lock);
935                         ast_log(LOG_WARNING, "Out of memory\n");
936                         goto out;
937                 }
938                 memset(tmp, 0, sizeof(struct localuser));
939                 tmp->stillgoing = -1;
940                 if (options) {
941                         if (strchr(options, 't'))
942                                 tmp->allowredirect_in = 1;
943                         if (strchr(options, 'T'))
944                                 tmp->allowredirect_out = 1;
945                         if (strchr(options, 'r'))
946                                 tmp->ringbackonly = 1;
947                         if (strchr(options, 'm'))
948                                 tmp->musiconhold = 1;
949                         if (strchr(options, 'd'))
950                                 tmp->dataquality = 1;
951                         if (strchr(options, 'H'))
952                                 tmp->allowdisconnect = 1;
953                         if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout))
954                                 *go_on = 1;
955                 }
956                 if (url) {
957                         ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
958                 } else 
959                         ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
960
961                 tmp->member = cur;              /* Never directly dereference!  Could change on reload */
962                 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
963                 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
964                 /* If we're dialing by extension, look at the extension to know what to dial */
965                 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
966                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
967                         snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
968                         if (option_debug)
969                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
970                 }
971                 /* Special case: If we ring everyone, go ahead and ring them, otherwise
972                    just calculate their metric for the appropriate strategy */
973                 calc_metric(qe->parent, cur, x++, qe, tmp);
974                 /* Put them in the list of outgoing thingies...  We're ready now. 
975                    XXX If we're forcibly removed, these outgoing calls won't get
976                    hung up XXX */
977                 tmp->next = outgoing;
978                 outgoing = tmp;         
979                 /* If this line is up, don't try anybody else */
980                 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
981                         break;
982
983                 cur = cur->next;
984         }
985         if (qe->parent->timeout)
986                 to = qe->parent->timeout * 1000;
987         else
988                 to = -1;
989         ring_one(qe, outgoing);
990         ast_mutex_unlock(&qe->parent->lock);
991         lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
992         ast_mutex_lock(&qe->parent->lock);
993         if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
994                 store_next(qe, outgoing);
995         }
996         ast_mutex_unlock(&qe->parent->lock);
997         if (lpeer)
998                 peer = lpeer->chan;
999         else
1000                 peer = NULL;
1001         if (!peer) {
1002                 if (to) {
1003                         /* Musta gotten hung up */
1004                         record_abandoned(qe);
1005                         res = -1;
1006                 } else {
1007                         if (digit && valid_exit(qe, digit))
1008                                 res=digit;
1009                         else
1010                                 /* Nobody answered, next please? */
1011                                 res=0;
1012                 }
1013                 goto out;
1014         }
1015         if (peer) {
1016                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
1017                    we will always return with -1 so that it is hung up properly after the 
1018                    conversation.  */
1019                 qe->handled++;
1020                 if (!strcmp(qe->chan->type,"Zap")) {
1021                         if (tmp->dataquality) zapx = 0;
1022                         ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1023                 }                       
1024                 if (!strcmp(peer->type,"Zap")) {
1025                         if (tmp->dataquality) zapx = 0;
1026                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1027                 }
1028                 /* Update parameters for the queue */
1029                 recalc_holdtime(qe);
1030                 member = lpeer->member;
1031                 hanguptree(outgoing, peer);
1032                 outgoing = NULL;
1033                 if (announce) {
1034                         int res2;
1035                         res2 = ast_autoservice_start(qe->chan);
1036                         if (!res2) {
1037                                 res2 = ast_streamfile(peer, announce, peer->language);
1038                                 if (!res2)
1039                                         res2 = ast_waitstream(peer, "");
1040                                 else {
1041                                         ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
1042                                         res2 = 0;
1043                                 }
1044                         }
1045                         res2 |= ast_autoservice_stop(qe->chan);
1046                         if (res2) {
1047                                 /* Agent must have hung up */
1048                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
1049                                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
1050                                 ast_hangup(peer);
1051                                 return -1;
1052                         }
1053                 }
1054                 /* Stop music on hold */
1055                 ast_moh_stop(qe->chan);
1056                 /* If appropriate, log that we have a destination channel */
1057                 if (qe->chan->cdr)
1058                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
1059                 /* Make sure channels are compatible */
1060                 res = ast_channel_make_compatible(qe->chan, peer);
1061                 if (res < 0) {
1062                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1063                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1064                         ast_hangup(peer);
1065                         return -1;
1066                 }
1067                 /* Begin Monitoring */
1068                 if (qe->parent->monfmt && *qe->parent->monfmt) {
1069                         monitorfilename = pbx_builtin_getvar_helper( qe->chan, "MONITOR_FILENAME");
1070                         if(monitorfilename) {
1071                                 ast_monitor_start( peer, qe->parent->monfmt, monitorfilename, 1 );
1072                         } else {
1073                                 ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1074                         }
1075                         if(qe->parent->monjoin) {
1076                                 ast_monitor_setjoinfiles( peer, 1);
1077                         }
1078                 }
1079                 /* Drop out of the queue at this point, to prepare for next caller */
1080                 leave_queue(qe);                        
1081                 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
1082                         ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1083                         ast_channel_sendurl( peer, url );
1084                 }
1085                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1086                 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1087                 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1088                 time(&callstart);
1089
1090                 memset(&config,0,sizeof(struct ast_bridge_config));
1091         config.allowredirect_in = allowredir_in;
1092         config.allowredirect_out = allowredir_out;
1093         config.allowdisconnect = allowdisconnect;
1094         bridge = ast_bridge_call(qe->chan,peer,&config);
1095
1096                 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1097                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1098                 } else if (qe->chan->_softhangup) {
1099                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1100                 } else {
1101                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1102                 }
1103
1104                 if(bridge != AST_PBX_NO_HANGUP_PEER)
1105                         ast_hangup(peer);
1106                 update_queue(qe->parent, member);
1107                 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1108                 else res = bridge; /* bridge error, stay in the queue */
1109         }       
1110 out:
1111         hanguptree(outgoing, NULL);
1112         return res;
1113 }
1114
1115 static int wait_a_bit(struct queue_ent *qe)
1116 {
1117         /* Don't need to hold the lock while we setup the outgoing calls */
1118         int retrywait = qe->parent->retry * 1000;
1119         return ast_waitfordigit(qe->chan, retrywait);
1120 }
1121
1122 // [PHM 06/26/03]
1123
1124 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
1125 {
1126         struct member * ret = NULL ;
1127         struct member *mem;
1128         char buf[500] ;
1129
1130         if( q != NULL )
1131         {
1132                 mem = q->members ;
1133
1134                 while( mem != NULL ) {
1135                         sprintf( buf, "%s/%s", mem->tech, mem->loc);
1136
1137                         if( strcmp( buf, interface ) == 0 ) {
1138                                 ret = mem ;
1139                                 break ;
1140                         }
1141                         else
1142                                 mem = mem->next ;
1143                 }
1144         }
1145
1146         return( ret ) ;
1147 }
1148
1149
1150 static struct member * create_queue_node( char * interface, int penalty )
1151 {
1152         struct member * cur ;
1153         char * tmp ;
1154         
1155         /* Add a new member */
1156
1157         cur = malloc(sizeof(struct member));
1158
1159         if (cur) {
1160                 memset(cur, 0, sizeof(struct member));
1161                 cur->penalty = penalty;
1162                 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
1163                 if ((tmp = strchr(cur->tech, '/')))
1164                         *tmp = '\0';
1165                 if ((tmp = strchr(interface, '/'))) {
1166                         tmp++;
1167                         strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1168                 } else
1169                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1170         }
1171
1172         return( cur ) ;
1173 }
1174
1175
1176 static int rqm_exec(struct ast_channel *chan, void *data)
1177 {
1178         int res=-1;
1179         struct localuser *u;
1180         char *queuename;
1181         struct member * node ;
1182         struct member * look ;
1183         char info[512];
1184         char tmpchan[256]="";
1185         char *interface=NULL;
1186         struct ast_call_queue *q;
1187         int found=0 ;
1188
1189         if (!data) {
1190                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
1191                 return -1;
1192         }
1193         
1194         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1195         
1196         /* Parse our arguments XXX Check for failure XXX */
1197         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1198         queuename = info;
1199         if (queuename) {
1200                 interface = strchr(queuename, '|');
1201                 if (interface) {
1202                         *interface = '\0';
1203                         interface++;
1204                 }
1205                 else {
1206                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1207                         interface = strrchr(tmpchan, '-');
1208                         if (interface)
1209                                 *interface = '\0';
1210                         interface = tmpchan;
1211                 }
1212         }
1213
1214         if( ( q = queues) != NULL )
1215         {
1216                 while( q && ( res != 0 ) && (!found) ) 
1217                 {
1218                         ast_mutex_lock(&q->lock);
1219                         if( strcmp( q->name, queuename) == 0 )
1220                         {
1221                                 // found queue, try to remove  interface
1222                                 found=1 ;
1223
1224                                 if( ( node = interface_exists( q, interface ) ) != NULL )
1225                                 {
1226                                         if( ( look = q->members ) == node )
1227                                         {
1228                                                 // 1st
1229                                                 q->members = node->next;
1230                                         }
1231                                         else
1232                                         {
1233                                                 while( look != NULL )
1234                                                         if( look->next == node )
1235                                                         {
1236                                                                 look->next = node->next ;
1237                                                                 break ;
1238                                                         }
1239                                                         else
1240                                                                 look = look->next ;
1241                                         }
1242
1243                                         free( node ) ;
1244
1245                                         ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", 
1246                                                 interface, queuename);
1247                                         res = 0 ;
1248                                 }
1249                                 else
1250                                 {
1251                                         ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
1252                                                 "Not there\n", interface, queuename);
1253                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1254                                                 {
1255                                                 chan->priority += 100;
1256                                                 res = 0 ;
1257                                                 }
1258                                 }
1259                         }
1260
1261                         ast_mutex_unlock(&q->lock);
1262                         q = q->next;
1263                 }
1264         }
1265
1266         if( ! found )
1267                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1268
1269         LOCAL_USER_REMOVE(u);
1270         return res;
1271 }
1272
1273
1274
1275 static int aqm_exec(struct ast_channel *chan, void *data)
1276 {
1277         int res=-1;
1278         struct localuser *u;
1279         char *queuename;
1280         char info[512];
1281         char tmpchan[512]="";
1282         char *interface=NULL;
1283         char *penaltys=NULL;
1284         int penalty = 0;
1285         struct ast_call_queue *q;
1286         struct member *save;
1287         int found=0 ;
1288
1289         if (!data) {
1290                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface|optional penalty)\n");
1291                 return -1;
1292         }
1293         
1294         LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1295         
1296         /* Parse our arguments XXX Check for failure XXX */
1297         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1298         queuename = info;
1299         if (queuename) {
1300                 interface = strchr(queuename, '|');
1301                 if (interface) {
1302                         *interface = '\0';
1303                         interface++;
1304                 }
1305                 if (interface) {
1306                         penaltys = strchr(interface, '|');
1307                         if (penaltys) {
1308                                 *penaltys = 0;
1309                                 penaltys++;
1310                         }
1311                 }
1312                 if (!interface || !strlen(interface)) {
1313                         strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1314                         interface = strrchr(tmpchan, '-');
1315                         if (interface)
1316                                 *interface = '\0';
1317                         interface = tmpchan;
1318                 }
1319                 if (penaltys && strlen(penaltys)) {
1320                         if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
1321                                 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
1322                                 penalty = 0;
1323                         }
1324                 }
1325         }
1326
1327         if( ( q = queues) != NULL )
1328         {
1329                 while( q && ( res != 0 ) && (!found) ) 
1330                 {
1331                         ast_mutex_lock(&q->lock);
1332                         if( strcmp( q->name, queuename) == 0 )
1333                         {
1334                                 // found queue, try to enable interface
1335                                 found=1 ;
1336
1337                                 if( interface_exists( q, interface ) == NULL )
1338                                 {
1339                                         save = q->members ;
1340                                         q->members = create_queue_node( interface, penalty ) ;
1341
1342                                         if( q->members != NULL ) {
1343                                                 q->members->dynamic = 1;
1344                                                 q->members->next = save ;
1345                                         } else
1346                                                 q->members = save ;
1347
1348                                         ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1349                                         res = 0 ;
1350                                 }
1351                                 else
1352                                 {
1353                                         ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
1354                                                 "Already there\n", interface, queuename);
1355                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1356                                         {
1357                                                 chan->priority += 100;
1358                                                 res = 0 ;
1359                                         }
1360                                 }
1361                         }
1362
1363                         ast_mutex_unlock(&q->lock);
1364                         q = q->next;
1365                 }
1366         }
1367
1368         if( ! found )
1369                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1370
1371         LOCAL_USER_REMOVE(u);
1372         return res;
1373 }
1374
1375
1376 static int queue_exec(struct ast_channel *chan, void *data)
1377 {
1378         int res=-1;
1379         int ringing=0;
1380         struct localuser *u;
1381         char *queuename;
1382         char info[512];
1383         char *options = NULL;
1384         char *url = NULL;
1385         char *announceoverride = NULL;
1386         char *queuetimeoutstr = NULL;
1387         /* whether to exit Queue application after the timeout hits */
1388         int go_on = 0;
1389
1390
1391
1392         /* Our queue entry */
1393         struct queue_ent qe;
1394         
1395         if (!data) {
1396                 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
1397                 return -1;
1398         }
1399         
1400         LOCAL_USER_ADD(u);
1401
1402         /* Setup our queue entry */
1403         memset(&qe, 0, sizeof(qe));
1404         
1405         /* Parse our arguments XXX Check for failure XXX */
1406         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1407         queuename = info;
1408         if (queuename) {
1409                 options = strchr(queuename, '|');
1410                 if (options) {
1411                         *options = '\0';
1412                         options++;
1413                         url = strchr(options, '|');
1414                         if (url) {
1415                                 *url = '\0';
1416                                 url++;
1417                                 announceoverride = strchr(url, '|');
1418                                 if (announceoverride) {
1419                                         *announceoverride = '\0';
1420                                         announceoverride++;
1421                                         queuetimeoutstr = strchr(announceoverride, '|');
1422                                         if (queuetimeoutstr) {
1423                                                 *queuetimeoutstr = '\0';
1424                                                 queuetimeoutstr++;
1425                                                 qe.queuetimeout = atoi(queuetimeoutstr);
1426                                         } else {
1427                                                 qe.queuetimeout = 0;
1428                                 }
1429                         }
1430                 }
1431         }
1432         }
1433
1434         if (options) {
1435                 if (strchr(options, 'r')) {
1436                         ringing = 1;
1437                 }
1438         }
1439
1440         printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
1441                 queuename, options, url, announceoverride, qe.queuetimeout);
1442
1443
1444         qe.chan = chan;
1445         qe.start = time(NULL);
1446         qe.last_pos_said = 0;
1447         qe.last_pos = 0;
1448         if (!join_queue(queuename, &qe)) {
1449                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
1450                 /* Start music on hold */
1451                 if (ringing) {
1452                         ast_indicate(chan, AST_CONTROL_RINGING);
1453                 } else {              
1454                         ast_moh_start(chan, qe.moh);
1455                 }
1456                 for (;;) {
1457                         /* This is the wait loop for callers 2 through maxlen */
1458
1459                         res = wait_our_turn(&qe, ringing);
1460                         /* If they hungup, return immediately */
1461                         if (res < 0) {
1462                                 /* Record this abandoned call */
1463                                 record_abandoned(&qe);
1464                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1465                                 if (option_verbose > 2) {
1466                                         ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1467                                         res = -1;
1468                                 }
1469                                 break;
1470                         }
1471                         if (!res) 
1472                                 break;
1473                         if (valid_exit(&qe, res)) {
1474                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1475                                 break;
1476                         }
1477                 }
1478                 if (!res) {
1479                         for (;;) {
1480                                 /* This is the wait loop for the head caller*/
1481                                 /* To exit, they may get their call answered; */
1482                                 /* they may dial a digit from the queue context; */
1483                                 /* or, they may may timeout. */
1484
1485                                 /* Leave if we have exceeded our queuetimeout */
1486                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1487                                         res = 0;
1488                                         break;
1489                                 }
1490
1491                                 /* Make a position announcement, if enabled */
1492                                 if (qe.parent->announcefrequency && !ringing)
1493                                         say_position(&qe);
1494
1495                                 /* Try calling all queue members for 'timeout' seconds */
1496                                 res = try_calling(&qe, options, announceoverride, url, &go_on);
1497                                 if (res) {
1498                                         if (res < 0) {
1499                                                 if (!qe.handled)
1500                                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1501                                         } else if (res > 0)
1502                                                 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1503                                         break;
1504                                 }
1505
1506                                 /* Leave if we have exceeded our queuetimeout */
1507                                 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1508                                         res = 0;
1509                                         break;
1510                                 }
1511
1512                                 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
1513                                 res = wait_a_bit(&qe);
1514                                 if (res < 0) {
1515                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1516                                         if (option_verbose > 2) {
1517                                                 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1518                                                 res = -1;
1519                                         }
1520                                         break;
1521                                 }
1522                                 if (res && valid_exit(&qe, res)) {
1523                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1524                                         break;
1525                                 }
1526                                 /* exit after 'timeout' cycle if 'n' option enabled */
1527                                 if (go_on) {
1528                                         if (option_verbose > 2) {
1529                                                 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
1530                                                 res = -1;
1531                                         }
1532                                         ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1533                                         res = 0;
1534                                         break;
1535                                 }
1536
1537                         }
1538                 }
1539                 /* Don't allow return code > 0 */
1540                 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1541                         res = 0;        
1542                         if (ringing) {
1543                                 ast_indicate(chan, -1);
1544                         } else {
1545                                 ast_moh_stop(chan);
1546                         }                       
1547                         ast_stopstream(chan);
1548                 }
1549                 leave_queue(&qe);
1550         } else {
1551                 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1552                 res =  0;
1553         }
1554         LOCAL_USER_REMOVE(u);
1555         return res;
1556 }
1557
1558 static void reload_queues(void)
1559 {
1560         struct ast_call_queue *q, *ql, *qn;
1561         struct ast_config *cfg;
1562         char *cat, *tmp;
1563         struct ast_variable *var;
1564         struct member *prev, *cur;
1565         int new;
1566         cfg = ast_load("queues.conf");
1567         if (!cfg) {
1568                 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1569                 return;
1570         }
1571         ast_mutex_lock(&qlock);
1572         /* Mark all queues as dead for the moment */
1573         q = queues;
1574         while(q) {
1575                 q->dead = 1;
1576                 q = q->next;
1577         }
1578         /* Chug through config file */
1579         cat = ast_category_browse(cfg, NULL);
1580         while(cat) {
1581                 if (strcasecmp(cat, "general")) {
1582                         /* Look for an existing one */
1583                         q = queues;
1584                         while(q) {
1585                                 if (!strcmp(q->name, cat))
1586                                         break;
1587                                 q = q->next;
1588                         }
1589                         if (!q) {
1590                                 /* Make one then */
1591                                 q = malloc(sizeof(struct ast_call_queue));
1592                                 if (q) {
1593                                         /* Initialize it */
1594                                         memset(q, 0, sizeof(struct ast_call_queue));
1595                                         ast_mutex_init(&q->lock);
1596                                         strncpy(q->name, cat, sizeof(q->name));
1597                                         new = 1;
1598                                 } else new = 0;
1599                         } else
1600                                         new = 0;
1601                         if (q) {
1602                                 if (!new) 
1603                                         ast_mutex_lock(&q->lock);
1604                                 /* Re-initialize the queue */
1605                                 q->dead = 0;
1606                                 q->retry = 0;
1607                                 q->timeout = -1;
1608                                 q->maxlen = 0;
1609                                 q->announcefrequency = 0;
1610                                 q->announceholdtime = 0;
1611                                 q->holdtime = 0;
1612                                 q->callscompleted = 0;
1613                                 q->callsabandoned = 0;
1614                                 q->callscompletedinsl = 0;
1615                                 q->servicelevel = 0;
1616                                 free_members(q, 0);
1617                                 strcpy(q->moh, "");
1618                                 strcpy(q->announce, "");
1619                                 strcpy(q->context, "");
1620                                 strcpy(q->monfmt, "");
1621                                 strcpy(q->sound_next, "queue-youarenext");
1622                                 strcpy(q->sound_thereare, "queue-thereare");
1623                                 strcpy(q->sound_calls, "queue-callswaiting");
1624                                 strcpy(q->sound_holdtime, "queue-holdtime");
1625                                 strcpy(q->sound_minutes, "queue-minutes");
1626                                 strcpy(q->sound_thanks, "queue-thankyou");
1627                                 prev = q->members;
1628                                 if (prev) {
1629                                         /* find the end of any dynamic members */
1630                                         while(prev->next)
1631                                                 prev = prev->next;
1632                                 }
1633                                 var = ast_variable_browse(cfg, cat);
1634                                 while(var) {
1635                                         if (!strcasecmp(var->name, "member")) {
1636                                                 /* Add a new member */
1637                                                 cur = malloc(sizeof(struct member));
1638                                                 if (cur) {
1639                                                         memset(cur, 0, sizeof(struct member));
1640                                                         strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1641                                                         if ((tmp = strchr(cur->tech, ','))) {
1642                                                                 *tmp = '\0';
1643                                                                 tmp++;
1644                                                                 cur->penalty = atoi(tmp);
1645                                                                 if (cur->penalty < 0)
1646                                                                         cur->penalty = 0;
1647                                                         }
1648                                                         if ((tmp = strchr(cur->tech, '/')))
1649                                                                 *tmp = '\0';
1650                                                         if ((tmp = strchr(var->value, '/'))) {
1651                                                                 tmp++;
1652                                                                 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1653                                                                 if ((tmp = strchr(cur->loc, ',')))
1654                                                                         *tmp = '\0';
1655                                                         } else
1656                                                                 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1657                                                         if (prev)
1658                                                                 prev->next = cur;
1659                                                         else
1660                                                                 q->members = cur;
1661                                                         prev = cur;
1662                                                 }
1663                                         } else if (!strcasecmp(var->name, "music")) {
1664                                                 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1665                                         } else if (!strcasecmp(var->name, "announce")) {
1666                                                 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1667                                         } else if (!strcasecmp(var->name, "context")) {
1668                                                 strncpy(q->context, var->value, sizeof(q->context) - 1);
1669                                         } else if (!strcasecmp(var->name, "timeout")) {
1670                                                 q->timeout = atoi(var->value);
1671                                         } else if (!strcasecmp(var->name, "monitor-join")) {
1672                                                 q->monjoin = ast_true(var->value);
1673                                         } else if (!strcasecmp(var->name, "monitor-format")) {
1674                                                 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
1675                                         } else if (!strcasecmp(var->name, "queue-youarenext")) {
1676                                                 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
1677                                         } else if (!strcasecmp(var->name, "queue-thereare")) {
1678                                                 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
1679                                         } else if (!strcasecmp(var->name, "queue-callswaiting")) {
1680                                                 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
1681                                         } else if (!strcasecmp(var->name, "queue-holdtime")) {
1682                                                 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
1683                                         } else if (!strcasecmp(var->name, "queue-minutes")) {
1684                                                 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
1685                                         } else if (!strcasecmp(var->name, "queue-thankyou")) {
1686                                                 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
1687                                         } else if (!strcasecmp(var->name, "announce-frequency")) {
1688                                                 q->announcefrequency = atoi(var->value);
1689                                         } else if (!strcasecmp(var->name, "announce-holdtime")) {
1690                                                 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
1691                                         } else if (!strcasecmp(var->name, "retry")) {
1692                                                 q->retry = atoi(var->value);
1693                                         } else if (!strcasecmp(var->name, "maxlen")) {
1694                                                 q->maxlen = atoi(var->value);
1695                                         } else if (!strcasecmp(var->name, "servicelevel")) {
1696                                                 q->servicelevel= atoi(var->value);
1697                                         } else if (!strcasecmp(var->name, "strategy")) {
1698                                                 q->strategy = strat2int(var->value);
1699                                                 if (q->strategy < 0) {
1700                                                         ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1701                                                         q->strategy = 0;
1702                                                 }
1703                                         } else {
1704                                                 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1705                                         }
1706                                         var = var->next;
1707                                 }
1708                                 if (q->retry < 1)
1709                                         q->retry = DEFAULT_RETRY;
1710                                 if (q->timeout < 0)
1711                                         q->timeout = DEFAULT_TIMEOUT;
1712                                 if (q->maxlen < 0)
1713                                         q->maxlen = 0;
1714                                 if (!new) 
1715                                         ast_mutex_unlock(&q->lock);
1716                                 if (new) {
1717                                         q->next = queues;
1718                                         queues = q;
1719                                 }
1720                         }
1721                 }
1722                 cat = ast_category_browse(cfg, cat);
1723         }
1724         ast_destroy(cfg);
1725         q = queues;
1726         ql = NULL;
1727         while(q) {
1728                 qn = q->next;
1729                 if (q->dead) {
1730                         if (ql)
1731                                 ql->next = q->next;
1732                         else
1733                                 queues = q->next;
1734                         if (!q->count) {
1735                                 free(q);
1736                         } else
1737                                 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1738                 } else
1739                         ql = q;
1740                 q = qn;
1741         }
1742         ast_mutex_unlock(&qlock);
1743 }
1744
1745 static int __queues_show(int fd, int argc, char **argv, int queue_show)
1746 {
1747         struct ast_call_queue *q;
1748         struct queue_ent *qe;
1749         struct member *mem;
1750         int pos;
1751         time_t now;
1752         char max[80];
1753         char calls[80];
1754         float sl = 0;
1755
1756         time(&now);
1757         if ((!queue_show && argc != 2) || (queue_show && argc != 3))
1758                 return RESULT_SHOWUSAGE;
1759         ast_mutex_lock(&qlock);
1760         q = queues;
1761         if (!q) {       
1762                 ast_mutex_unlock(&qlock);
1763                 if (queue_show)
1764                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
1765                 else
1766                         ast_cli(fd, "No queues.\n");
1767                 return RESULT_SUCCESS;
1768         }
1769         while(q) {
1770                 ast_mutex_lock(&q->lock);
1771                 if (queue_show) {
1772                         if (strcasecmp(q->name, argv[2]) != 0) {
1773                                 ast_mutex_unlock(&q->lock);
1774                                 q = q->next;
1775                                 if (!q) {
1776                                         ast_cli(fd, "No such queue: %s.\n",argv[2]);
1777                                         break;
1778                                 }
1779                                 continue;
1780                         }
1781                 }
1782                 if (q->maxlen)
1783                         snprintf(max, sizeof(max), "%d", q->maxlen);
1784                 else
1785                         strcpy(max, "unlimited");
1786                 sl = 0;
1787                 if(q->callscompleted > 0)
1788                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1789                 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
1790                         q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
1791                 if (q->members) {
1792                         ast_cli(fd, "   Members: \n");
1793                         for (mem = q->members; mem; mem = mem->next) {
1794                                 if (mem->penalty)
1795                                         snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
1796                                 else
1797                                         strcpy(max, "");
1798                                 if (mem->dynamic)
1799                                         strcat(max, " (dynamic)");
1800                                 if (mem->calls) {
1801                                         snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
1802                                                         mem->calls, (long)(time(NULL) - mem->lastcall));
1803                                 } else
1804                                         strcpy(calls, " has taken no calls yet");
1805                                 ast_cli(fd, "      %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
1806                         }
1807                 } else
1808                         ast_cli(fd, "   No Members\n");
1809                 if (q->head) {
1810                         pos = 1;
1811                         ast_cli(fd, "   Callers: \n");
1812                         for (qe = q->head; qe; qe = qe->next) 
1813                                 ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name,
1814                                                                 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60);
1815                 } else
1816                         ast_cli(fd, "   No Callers\n");
1817                 ast_cli(fd, "\n");
1818                 ast_mutex_unlock(&q->lock);
1819                 q = q->next;
1820                 if (queue_show)
1821                         break;
1822         }
1823         ast_mutex_unlock(&qlock);
1824         return RESULT_SUCCESS;
1825 }
1826
1827 static int queues_show(int fd, int argc, char **argv)
1828 {
1829         return __queues_show(fd, argc, argv, 0);
1830 }
1831
1832 static int queue_show(int fd, int argc, char **argv)
1833 {
1834         return __queues_show(fd, argc, argv, 1);
1835 }
1836
1837 static char *complete_queue(char *line, char *word, int pos, int state)
1838 {
1839         struct ast_call_queue *q;
1840         int which=0;
1841         
1842         ast_mutex_lock(&qlock);
1843         q = queues;
1844         while(q) {
1845                 if (!strncasecmp(word, q->name, strlen(word))) {
1846                         if (++which > state)
1847                                 break;
1848                 }
1849                 q = q->next;
1850         }
1851         ast_mutex_unlock(&qlock);
1852         return q ? strdup(q->name) : NULL;
1853 }
1854
1855 /* JDG: callback to display queues status in manager */
1856 static int manager_queues_show( struct mansession *s, struct message *m )
1857 {
1858         char *a[] = { "show", "queues" };
1859         return queues_show( s->fd, 2, a );
1860 } /* /JDG */
1861
1862
1863 /* Dump queue status */
1864 static int manager_queues_status( struct mansession *s, struct message *m )
1865 {
1866         time_t now;
1867         int pos;
1868         char *id = astman_get_header(m,"ActionID");
1869         char idText[256] = "";
1870         struct ast_call_queue *q;
1871         struct queue_ent *qe;
1872         float sl = 0;
1873         struct member *mem;
1874         astman_send_ack(s, m, "Queue status will follow");
1875         time(&now);
1876         ast_mutex_lock(&qlock);
1877         q = queues;
1878         if (id && !ast_strlen_zero(id)) {
1879                 snprintf(idText,256,"ActionID: %s\r\n",id);
1880         }
1881         while(q) {
1882                 ast_mutex_lock(&q->lock);
1883
1884                 /* List queue properties */
1885                 if(q->callscompleted > 0)
1886                         sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1887                 ast_cli(s->fd, "Event: QueueParams\r\n"
1888                                         "Queue: %s\r\n"
1889                                         "Max: %d\r\n"
1890                                         "Calls: %d\r\n"
1891                                         "Holdtime: %d\r\n"
1892                                         "Completed: %d\r\n"
1893                                         "Abandoned: %d\r\n"
1894                                         "ServiceLevel: %d\r\n"
1895                                         "ServicelevelPerf: %2.1f\r\n"
1896                                         "%s"
1897                                         "\r\n",
1898                                                 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
1899                                                 q->callsabandoned, q->servicelevel, sl, idText);
1900
1901                 /* List Queue Members */
1902                 for (mem = q->members; mem; mem = mem->next) 
1903                         ast_cli(s->fd, "Event: QueueMember\r\n"
1904                                 "Queue: %s\r\n"
1905                                 "Location: %s/%s\r\n"
1906                                 "Membership: %s\r\n"
1907                                 "Penalty: %d\r\n"
1908                                 "CallsTaken: %d\r\n"
1909                                 "LastCall: %ld\r\n"
1910                                 "%s"
1911                                 "\r\n",
1912                                         q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
1913                                         mem->penalty, mem->calls, mem->lastcall, idText);
1914
1915                 /* List Queue Entries */
1916
1917                 pos = 1;
1918                 for (qe = q->head; qe; qe = qe->next) 
1919                         ast_cli(s->fd, "Event: QueueEntry\r\n"
1920                                 "Queue: %s\r\n"
1921                                 "Position: %d\r\n"
1922                                 "Channel: %s\r\n"
1923                                 "CallerID: %s\r\n"
1924                                 "Wait: %ld\r\n"
1925                                 "%s"
1926                                 "\r\n", 
1927                                         q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
1928                 ast_mutex_unlock(&q->lock);
1929                 q = q->next;
1930         }
1931         ast_mutex_unlock(&qlock);
1932         return RESULT_SUCCESS;
1933 }
1934
1935 static char show_queues_usage[] = 
1936 "Usage: show queues\n"
1937 "       Provides summary information on call queues.\n";
1938
1939 static struct ast_cli_entry cli_show_queues = {
1940         { "show", "queues", NULL }, queues_show, 
1941         "Show status of queues", show_queues_usage, NULL };
1942
1943 static char show_queue_usage[] = 
1944 "Usage: show queue\n"
1945 "       Provides summary information on a specified queue.\n";
1946
1947 static struct ast_cli_entry cli_show_queue = {
1948         { "show", "queue", NULL }, queue_show, 
1949         "Show status of a specified queue", show_queue_usage, complete_queue };
1950
1951 int unload_module(void)
1952 {
1953         STANDARD_HANGUP_LOCALUSERS;
1954         ast_cli_unregister(&cli_show_queue);
1955         ast_cli_unregister(&cli_show_queues);
1956         ast_manager_unregister( "Queues" );
1957         ast_manager_unregister( "QueueStatus" );
1958         ast_unregister_application(app_aqm);
1959         ast_unregister_application(app_rqm);
1960         return ast_unregister_application(app);
1961 }
1962
1963 int load_module(void)
1964 {
1965         int res;
1966         res = ast_register_application(app, queue_exec, synopsis, descrip);
1967         if (!res) {
1968                 ast_cli_register(&cli_show_queue);
1969                 ast_cli_register(&cli_show_queues);
1970                 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1971                 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1972
1973                 // [PHM 06/26/03]
1974                 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1975                 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1976         }
1977         reload_queues();
1978         return res;
1979 }
1980
1981
1982 int reload(void)
1983 {
1984         reload_queues();
1985         return 0;
1986 }
1987
1988 char *description(void)
1989 {
1990         return tdesc;
1991 }
1992
1993 int usecount(void)
1994 {
1995         int res;
1996         STANDARD_USECOUNT(res);
1997         return res;
1998 }
1999
2000 char *key()
2001 {
2002         return ASTERISK_GPL_KEY;
2003 }