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