major dialplan functions update
[asterisk/asterisk.git] / apps / app_queue.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief True call queues with optional send URL on answer
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \arg Config in \ref Config_qu queues.conf
26  * 
27  * \par Development notes
28  * \note 2004-11-25: Persistent Dynamic Members added by:
29  *             NetNation Communications (www.netnation.com)
30  *             Kevin Lindsay <kevinl@netnation.com>
31  * 
32  *             Each dynamic agent in each queue is now stored in the astdb.
33  *             When asterisk is restarted, each agent will be automatically
34  *             readded into their recorded queues. This feature can be
35  *             configured with the 'persistent_members=<1|0>' setting in the
36  *             '[general]' category in queues.conf. The default is on.
37  * 
38  * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
39  *
40  * \note These features added by David C. Troy <dave@toad.net>:
41  *    - Per-queue holdtime calculation
42  *    - Estimated holdtime announcement
43  *    - Position announcement
44  *    - Abandoned/completed call counters
45  *    - Failout timer passed as optional app parameter
46  *    - Optional monitoring of calls, started when call is answered
47  *
48  * Patch Version 1.07 2003-12-24 01
49  *
50  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
51  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
52  *
53  * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
54  * by Matthew Enger <m.enger@xi.com.au>
55  *
56  * \ingroup applications
57  */
58
59 #include <stdlib.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <sys/time.h>
66 #include <sys/signal.h>
67 #include <netinet/in.h>
68
69 #include "asterisk.h"
70
71 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
72
73 #include "asterisk/lock.h"
74 #include "asterisk/file.h"
75 #include "asterisk/logger.h"
76 #include "asterisk/channel.h"
77 #include "asterisk/pbx.h"
78 #include "asterisk/options.h"
79 #include "asterisk/app.h"
80 #include "asterisk/linkedlists.h"
81 #include "asterisk/module.h"
82 #include "asterisk/translate.h"
83 #include "asterisk/say.h"
84 #include "asterisk/features.h"
85 #include "asterisk/musiconhold.h"
86 #include "asterisk/cli.h"
87 #include "asterisk/manager.h"
88 #include "asterisk/config.h"
89 #include "asterisk/monitor.h"
90 #include "asterisk/utils.h"
91 #include "asterisk/causes.h"
92 #include "asterisk/astdb.h"
93 #include "asterisk/devicestate.h"
94 #include "asterisk/stringfields.h"
95
96 #define QUEUE_STRATEGY_RINGALL          0
97 #define QUEUE_STRATEGY_ROUNDROBIN       1
98 #define QUEUE_STRATEGY_LEASTRECENT      2
99 #define QUEUE_STRATEGY_FEWESTCALLS      3
100 #define QUEUE_STRATEGY_RANDOM           4
101 #define QUEUE_STRATEGY_RRMEMORY         5
102
103 static struct strategy {
104         int strategy;
105         char *name;
106 } strategies[] = {
107         { QUEUE_STRATEGY_RINGALL, "ringall" },
108         { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
109         { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
110         { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
111         { QUEUE_STRATEGY_RANDOM, "random" },
112         { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
113 };
114
115 #define DEFAULT_RETRY           5
116 #define DEFAULT_TIMEOUT         15
117 #define RECHECK                 1               /* Recheck every second to see we we're at the top yet */
118 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
119
120 #define RES_OKAY        0               /* Action completed */
121 #define RES_EXISTS      (-1)            /* Entry already exists */
122 #define RES_OUTOFMEMORY (-2)            /* Out of memory */
123 #define RES_NOSUCHQUEUE (-3)            /* No such queue */
124
125 static char *tdesc = "True Call Queueing";
126
127 static char *app = "Queue";
128
129 static char *synopsis = "Queue a call for a call queue";
130
131 static char *descrip =
132 "  Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
133 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
134 "This application will return to the dialplan if the queue does not exist, or\n"
135 "any of the join options cause the caller to not enter the queue.\n"
136 "The option string may contain zero or more of the following characters:\n"
137 "      'd' -- data-quality (modem) call (minimum delay).\n"
138 "      'h' -- allow callee to hang up by hitting *.\n"
139 "      'H' -- allow caller to hang up by hitting *.\n"
140 "      'n' -- no retries on the timeout; will exit this application and \n"
141 "             go to the next step.\n"
142 "      'r' -- ring instead of playing MOH\n"
143 "      't' -- allow the called user transfer the calling user\n"
144 "      'T' -- to allow the calling user to transfer the call.\n"
145 "      'w' -- allow the called user to write the conversation to disk via Monitor\n"
146 "      'W' -- allow the calling user to write the conversation to disk via Monitor\n"
147 "  In addition to transferring the call, a call may be parked and then picked\n"
148 "up by another user.\n"
149 "  The optional URL will be sent to the called party if the channel supports\n"
150 "it.\n"
151 "  The timeout will cause the queue to fail out after a specified number of\n"
152 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
153 "  This application sets the following channel variable upon completion:\n"
154 "      QUEUESTATUS    The status of the call as a text string, one of\n"
155 "             TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
156
157 static char *app_aqm = "AddQueueMember" ;
158 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
159 static char *app_aqm_descrip =
160 "   AddQueueMember(queuename[|interface[|penalty[|options]]]):\n"
161 "Dynamically adds interface to an existing queue.\n"
162 "If the interface is already in the queue and there exists an n+101 priority\n"
163 "then it will then jump to this priority.  Otherwise it will return an error\n"
164 "The option string may contain zero or more of the following characters:\n"
165 "       'j' -- jump to +101 priority when appropriate.\n"
166 "  This application sets the following channel variable upon completion:\n"
167 "     AQMSTATUS    The status of the attempt to add a queue member as a \n"
168 "                     text string, one of\n"
169 "           ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
170 "Example: AddQueueMember(techsupport|SIP/3000)\n"
171 "";
172
173 static char *app_rqm = "RemoveQueueMember" ;
174 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
175 static char *app_rqm_descrip =
176 "   RemoveQueueMember(queuename[|interface[|options]]):\n"
177 "Dynamically removes interface to an existing queue\n"
178 "If the interface is NOT in the queue and there exists an n+101 priority\n"
179 "then it will then jump to this priority.  Otherwise it will return an error\n"
180 "The option string may contain zero or more of the following characters:\n"
181 "       'j' -- jump to +101 priority when appropriate.\n"
182 "  This application sets the following channel variable upon completion:\n"
183 "     RQMSTATUS      The status of the attempt to remove a queue member as a\n"
184 "                     text string, one of\n"
185 "           REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
186 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
187 "";
188
189 static char *app_pqm = "PauseQueueMember" ;
190 static char *app_pqm_synopsis = "Pauses a queue member" ;
191 static char *app_pqm_descrip =
192 "   PauseQueueMember([queuename]|interface[|options]):\n"
193 "Pauses (blocks calls for) a queue member.\n"
194 "The given interface will be paused in the given queue.  This prevents\n"
195 "any calls from being sent from the queue to the interface until it is\n"
196 "unpaused with UnpauseQueueMember or the manager interface.  If no\n"
197 "queuename is given, the interface is paused in every queue it is a\n"
198 "member of.  If the interface is not in the named queue, or if no queue\n"
199 "is given and the interface is not in any queue, it will jump to\n"
200 "priority n+101, if it exists and the appropriate options are set.\n"
201 "The application will fail if the interface is not found and no extension\n"
202 "to jump to exists.\n"
203 "The option string may contain zero or more of the following characters:\n"
204 "       'j' -- jump to +101 priority when appropriate.\n"
205 "  This application sets the following channel variable upon completion:\n"
206 "     PQMSTATUS      The status of the attempt to pause a queue member as a\n"
207 "                     text string, one of\n"
208 "           PAUSED | NOTFOUND\n"
209 "Example: PauseQueueMember(|SIP/3000)\n";
210
211 static char *app_upqm = "UnpauseQueueMember" ;
212 static char *app_upqm_synopsis = "Unpauses a queue member" ;
213 static char *app_upqm_descrip =
214 "   UnpauseQueueMember([queuename]|interface[|options]):\n"
215 "Unpauses (resumes calls to) a queue member.\n"
216 "This is the counterpart to PauseQueueMember and operates exactly the\n"
217 "same way, except it unpauses instead of pausing the given interface.\n"
218 "The option string may contain zero or more of the following characters:\n"
219 "       'j' -- jump to +101 priority when appropriate.\n"
220 "  This application sets the following channel variable upon completion:\n"
221 "     UPQMSTATUS       The status of the attempt to unpause a queue \n"
222 "                      member as a text string, one of\n"
223 "            UNPAUSED | NOTFOUND\n"
224 "Example: UnpauseQueueMember(|SIP/3000)\n";
225
226 /*! \brief Persistent Members astdb family */
227 static const char *pm_family = "/Queue/PersistentMembers";
228 /* The maximum length of each persistent member queue database entry */
229 #define PM_MAX_LEN 2048
230
231 /*! \brief queues.conf [general] option */
232 static int queue_persistent_members = 0;
233
234 /*! \brief queues.conf per-queue weight option */
235 static int use_weight = 0;
236
237 enum queue_result {
238         QUEUE_UNKNOWN = 0,
239         QUEUE_TIMEOUT = 1,
240         QUEUE_JOINEMPTY = 2,
241         QUEUE_LEAVEEMPTY = 3,
242         QUEUE_JOINUNAVAIL = 4,
243         QUEUE_LEAVEUNAVAIL = 5,
244         QUEUE_FULL = 6,
245 };
246
247 const struct { 
248         enum queue_result id;
249         char *text;
250 } queue_results[] = {
251         { QUEUE_UNKNOWN, "UNKNOWN" },
252         { QUEUE_TIMEOUT, "TIMEOUT" },
253         { QUEUE_JOINEMPTY,"JOINEMPTY" },
254         { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
255         { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
256         { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
257         { QUEUE_FULL, "FULL" },
258 };
259
260 /*! \brief We define a custom "local user" structure because we
261    use it not only for keeping track of what is in use but
262    also for keeping track of who we're dialing. */
263
264 struct localuser {
265         struct ast_channel *chan;
266         char interface[256];
267         int stillgoing;
268         int metric;
269         int oldstatus;
270         time_t lastcall;
271         struct member *member;
272         struct localuser *next;
273 };
274
275 LOCAL_USER_DECL;
276
277
278 struct queue_ent {
279         struct ast_call_queue *parent;  /*!< What queue is our parent */
280         char moh[80];                   /*!< Name of musiconhold to be used */
281         char announce[80];              /*!< Announcement to play for member when call is answered */
282         char context[AST_MAX_CONTEXT];  /*!< Context when user exits queue */
283         char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
284         int pos;                        /*!< Where we are in the queue */
285         int prio;                       /*!< Our priority */
286         int last_pos_said;              /*!< Last position we told the user */
287         time_t last_periodic_announce_time;     /*!< The last time we played a periodic announcement */
288         int last_periodic_announce_sound;       /* The last periodic announcement we made */
289         time_t last_pos;                /*!< Last time we told the user their position */
290         int opos;                       /*!< Where we started in the queue */
291         int handled;                    /*!< Whether our call was handled */
292         int max_penalty;                /*!< Limit the members that can take this call to this penalty or lower */
293         time_t start;                   /*!< When we started holding */
294         time_t expire;                  /*!< When this entry should expire (time out of queue) */
295         struct ast_channel *chan;       /*!< Our channel */
296         struct queue_ent *next;         /*!< The next queue entry */
297 };
298
299 struct member {
300         char interface[80];             /*!< Technology/Location */
301         int penalty;                    /*!< Are we a last resort? */
302         int calls;                      /*!< Number of calls serviced by this member */
303         int dynamic;                    /*!< Are we dynamically added? */
304         int status;                     /*!< Status of queue member */
305         int paused;                     /*!< Are we paused (not accepting calls)? */
306         time_t lastcall;                /*!< When last successful call was hungup */
307         int dead;                       /*!< Used to detect members deleted in realtime */
308         struct member *next;            /*!< Next member */
309 };
310
311 /* values used in multi-bit flags in ast_call_queue */
312 #define QUEUE_EMPTY_NORMAL 1
313 #define QUEUE_EMPTY_STRICT 2
314 #define ANNOUNCEHOLDTIME_ALWAYS 1
315 #define ANNOUNCEHOLDTIME_ONCE 2
316
317 struct ast_call_queue {
318         ast_mutex_t lock;       
319         char name[80];                  /*!< Name */
320         char moh[80];                   /*!< Music On Hold class to be used */
321         char announce[80];              /*!< Announcement to play when call is answered */
322         char context[AST_MAX_CONTEXT];  /*!< Exit context */
323         unsigned int monjoin:1;
324         unsigned int dead:1;
325         unsigned int joinempty:2;
326         unsigned int eventwhencalled:1;
327         unsigned int leavewhenempty:2;
328         unsigned int reportholdtime:1;
329         unsigned int wrapped:1;
330         unsigned int timeoutrestart:1;
331         unsigned int announceholdtime:2;
332         unsigned int strategy:3;
333         unsigned int maskmemberstatus:1;
334         unsigned int realtime:1;
335         int announcefrequency;          /*!< How often to announce their position */
336         int periodicannouncefrequency;  /*!< How often to play periodic announcement */
337         int roundingseconds;            /*!< How many seconds do we round to? */
338         int holdtime;                   /*!< Current avg holdtime, based on recursive boxcar filter */
339         int callscompleted;             /*!< Number of queue calls completed */
340         int callsabandoned;             /*!< Number of queue calls abandoned */
341         int servicelevel;               /*!< seconds setting for servicelevel*/
342         int callscompletedinsl;         /*!< Number of calls answered with servicelevel*/
343         char monfmt[8];                 /*!< Format to use when recording calls */
344         char sound_next[80];            /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
345         char sound_thereare[80];        /*!< Sound file: "There are currently" (def. queue-thereare) */
346         char sound_calls[80];           /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
347         char sound_holdtime[80];        /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
348         char sound_minutes[80];         /*!< Sound file: "minutes." (def. queue-minutes) */
349         char sound_lessthan[80];        /*!< Sound file: "less-than" (def. queue-lessthan) */
350         char sound_seconds[80];         /*!< Sound file: "seconds." (def. queue-seconds) */
351         char sound_thanks[80];          /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
352         char sound_reporthold[80];      /*!< Sound file: "Hold time" (def. queue-reporthold) */
353         char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/* Sound files: Custom announce, no default */
354
355         int count;                      /*!< How many entries */
356         int maxlen;                     /*!< Max number of entries */
357         int wrapuptime;                 /*!< Wrapup Time */
358
359         int retry;                      /*!< Retry calling everyone after this amount of time */
360         int timeout;                    /*!< How long to wait for an answer */
361         int weight;                     /*!< Respective weight */
362         int autopause;                  /*!< Auto pause queue members if they fail to answer */
363
364         /* Queue strategy things */
365         int rrpos;                      /*!< Round Robin - position */
366         int memberdelay;                /*!< Seconds to delay connecting member to caller */
367         int autofill;                   /*!< Ignore the head call status and ring an available agent */
368         
369         struct member *members;         /*!< Head of the list of members */
370         struct queue_ent *head;         /*!< Head of the list of callers */
371         AST_LIST_ENTRY(ast_call_queue) list;    /*!< Next call queue */
372 };
373
374 static AST_LIST_HEAD_STATIC(queues, ast_call_queue);
375
376 static int set_member_paused(char *queuename, char *interface, int paused);
377
378 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
379 {
380         int i;
381
382         for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
383                 if (queue_results[i].id == res) {
384                         pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
385                         return;
386                 }
387         }
388 }
389
390 static char *int2strat(int strategy)
391 {
392         int x;
393         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
394                 if (strategy == strategies[x].strategy)
395                         return strategies[x].name;
396         }
397         return "<unknown>";
398 }
399
400 static int strat2int(const char *strategy)
401 {
402         int x;
403         for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
404                 if (!strcasecmp(strategy, strategies[x].name))
405                         return strategies[x].strategy;
406         }
407         return -1;
408 }
409
410 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
411 static inline void insert_entry(struct ast_call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
412 {
413         struct queue_ent *cur;
414
415         if (!q || !new)
416                 return;
417         if (prev) {
418                 cur = prev->next;
419                 prev->next = new;
420         } else {
421                 cur = q->head;
422                 q->head = new;
423         }
424         new->next = cur;
425         new->parent = q;
426         new->pos = ++(*pos);
427         new->opos = *pos;
428 }
429
430 enum queue_member_status {
431         QUEUE_NO_MEMBERS,
432         QUEUE_NO_REACHABLE_MEMBERS,
433         QUEUE_NORMAL
434 };
435
436 static enum queue_member_status get_member_status(const struct ast_call_queue *q, int max_penalty)
437 {
438         struct member *member;
439         enum queue_member_status result = QUEUE_NO_MEMBERS;
440
441         for (member = q->members; member; member = member->next) {
442                 if (max_penalty && (member->penalty > max_penalty))
443                         continue;
444
445                 switch (member->status) {
446                 case AST_DEVICE_INVALID:
447                         /* nothing to do */
448                         break;
449                 case AST_DEVICE_UNAVAILABLE:
450                         result = QUEUE_NO_REACHABLE_MEMBERS;
451                         break;
452                 default:
453                         return QUEUE_NORMAL;
454                 }
455         }
456         
457         return result;
458 }
459
460 struct statechange {
461         int state;
462         char dev[0];
463 };
464
465 static void *changethread(void *data)
466 {
467         struct ast_call_queue *q;
468         struct statechange *sc = data;
469         struct member *cur;
470         char *loc;
471         char *technology;
472
473         technology = ast_strdupa(sc->dev);
474         loc = strchr(technology, '/');
475         if (loc) {
476                 *loc = '\0';
477                 loc++;
478         } else {
479                 free(sc);
480                 return NULL;
481         }
482         if (option_debug)
483                 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
484         AST_LIST_LOCK(&queues);
485         AST_LIST_TRAVERSE(&queues, q, list) {
486                 ast_mutex_lock(&q->lock);
487                 cur = q->members;
488                 while(cur) {
489                         if (!strcasecmp(sc->dev, cur->interface)) {
490                                 if (cur->status != sc->state) {
491                                         cur->status = sc->state;
492                                         if (!q->maskmemberstatus) {
493                                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
494                                                         "Queue: %s\r\n"
495                                                         "Location: %s\r\n"
496                                                         "Membership: %s\r\n"
497                                                         "Penalty: %d\r\n"
498                                                         "CallsTaken: %d\r\n"
499                                                         "LastCall: %d\r\n"
500                                                         "Status: %d\r\n"
501                                                         "Paused: %d\r\n",
502                                                 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
503                                                 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
504                                         }
505                                 }
506                         }
507                         cur = cur->next;
508                 }
509                 ast_mutex_unlock(&q->lock);
510         }
511         AST_LIST_UNLOCK(&queues);
512         free(sc);
513         return NULL;
514 }
515
516 static int statechange_queue(const char *dev, int state, void *ign)
517 {
518         /* Avoid potential for deadlocks by spawning a new thread to handle
519            the event */
520         struct statechange *sc;
521         pthread_t t;
522         pthread_attr_t attr;
523         
524         if ((sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1))) {
525                 sc->state = state;
526                 strcpy(sc->dev, dev);
527                 pthread_attr_init(&attr);
528                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
529                 if (ast_pthread_create(&t, &attr, changethread, sc)) {
530                         ast_log(LOG_WARNING, "Failed to create update thread!\n");
531                         free(sc);
532                 }
533         }
534         return 0;
535 }
536
537 static struct member *create_queue_member(char *interface, int penalty, int paused)
538 {
539         struct member *cur;
540         
541         /* Add a new member */
542
543         if ((cur = ast_calloc(1, sizeof(*cur)))) {
544                 cur->penalty = penalty;
545                 cur->paused = paused;
546                 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
547                 if (!strchr(cur->interface, '/'))
548                         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
549                 cur->status = ast_device_state(interface);
550         }
551
552         return cur;
553 }
554
555 static struct ast_call_queue *alloc_queue(const char *queuename)
556 {
557         struct ast_call_queue *q;
558
559         if ((q = ast_calloc(1, sizeof(*q)))) {
560                 ast_mutex_init(&q->lock);
561                 ast_copy_string(q->name, queuename, sizeof(q->name));
562         }
563         return q;
564 }
565
566 static void init_queue(struct ast_call_queue *q)
567 {
568         int i;
569         q->dead = 0;
570         q->retry = DEFAULT_RETRY;
571         q->timeout = -1;
572         q->maxlen = 0;
573         q->announcefrequency = 0;
574         q->announceholdtime = 0;
575         q->roundingseconds = 0; /* Default - don't announce seconds */
576         q->servicelevel = 0;
577         q->moh[0] = '\0';
578         q->announce[0] = '\0';
579         q->context[0] = '\0';
580         q->monfmt[0] = '\0';
581         q->periodicannouncefrequency = 0;
582         ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
583         ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
584         ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
585         ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
586         ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
587         ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
588         ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
589         ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
590         ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
591         ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
592         for (i=1;i<MAX_PERIODIC_ANNOUNCEMENTS;i++) {
593                 q->sound_periodicannounce[i][0]='\0';
594         }
595 }
596
597 static void clear_queue(struct ast_call_queue *q)
598 {
599         q->holdtime = 0;
600         q->callscompleted = 0;
601         q->callsabandoned = 0;
602         q->callscompletedinsl = 0;
603         q->wrapuptime = 0;
604 }
605
606 /*! \brief Configure a queue parameter.
607 \par
608    For error reporting, line number is passed for .conf static configuration.
609    For Realtime queues, linenum is -1.
610    The failunknown flag is set for config files (and static realtime) to show
611    errors for unknown parameters. It is cleared for dynamic realtime to allow
612    extra fields in the tables. */
613 static void queue_set_param(struct ast_call_queue *q, const char *param, const char *val, int linenum, int failunknown)
614 {
615         int i = 0;
616         char *c, *lastc;
617         char buff[80];
618         if (!strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
619                 ast_copy_string(q->moh, val, sizeof(q->moh));
620         } else if (!strcasecmp(param, "announce")) {
621                 ast_copy_string(q->announce, val, sizeof(q->announce));
622         } else if (!strcasecmp(param, "context")) {
623                 ast_copy_string(q->context, val, sizeof(q->context));
624         } else if (!strcasecmp(param, "timeout")) {
625                 q->timeout = atoi(val);
626                 if (q->timeout < 0)
627                         q->timeout = DEFAULT_TIMEOUT;
628         } else if (!strcasecmp(param, "monitor-join")) {
629                 q->monjoin = ast_true(val);
630         } else if (!strcasecmp(param, "monitor-format")) {
631                 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
632         } else if (!strcasecmp(param, "queue-youarenext")) {
633                 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
634         } else if (!strcasecmp(param, "queue-thereare")) {
635                 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
636         } else if (!strcasecmp(param, "queue-callswaiting")) {
637                 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
638         } else if (!strcasecmp(param, "queue-holdtime")) {
639                 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
640         } else if (!strcasecmp(param, "queue-minutes")) {
641                 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
642         } else if (!strcasecmp(param, "queue-seconds")) {
643                 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
644         } else if (!strcasecmp(param, "queue-lessthan")) {
645                 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
646         } else if (!strcasecmp(param, "queue-thankyou")) {
647                 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
648         } else if (!strcasecmp(param, "queue-reporthold")) {
649                 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
650         } else if (!strcasecmp(param, "announce-frequency")) {
651                 q->announcefrequency = atoi(val);
652         } else if (!strcasecmp(param, "announce-round-seconds")) {
653                 q->roundingseconds = atoi(val);
654                 if (q->roundingseconds>60 || q->roundingseconds<0) {
655                         if (linenum >= 0) {
656                                 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
657                                         "using 0 instead for queue '%s' at line %d of queues.conf\n",
658                                         val, param, q->name, linenum);
659                         } else {
660                                 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
661                                         "using 0 instead for queue '%s'\n", val, param, q->name);
662                         }
663                         q->roundingseconds=0;
664                 }
665         } else if (!strcasecmp(param, "announce-holdtime")) {
666                 if (!strcasecmp(val, "once"))
667                         q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
668                 else if (ast_true(val))
669                         q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
670                 else
671                         q->announceholdtime = 0;
672         } else if (!strcasecmp(param, "periodic-announce")) {
673                 if (strchr(val,'|')) {
674                         lastc = (char *)val;
675                         while ((c = strchr(lastc,'|'))) {
676                                 if (i > MAX_PERIODIC_ANNOUNCEMENTS)
677                                         break;
678                                 strncpy(buff, lastc, abs(lastc - c));
679                                 buff[abs(lastc - c)] = '\0';
680                                 ast_copy_string(q->sound_periodicannounce[i], buff, sizeof(q->sound_periodicannounce[i]));
681                                 lastc = (c + 1);
682                                 i++;
683                         }
684                         if (strlen(lastc)) {
685                                 ast_copy_string(q->sound_periodicannounce[i], lastc, sizeof(q->sound_periodicannounce[i]));
686                         }
687                 } else {
688                         ast_copy_string(q->sound_periodicannounce[i], val, sizeof(q->sound_periodicannounce[i]));
689                 }
690         } else if (!strcasecmp(param, "periodic-announce-frequency")) {
691                 q->periodicannouncefrequency = atoi(val);
692         } else if (!strcasecmp(param, "retry")) {
693                 q->retry = atoi(val);
694                 if (q->retry < 0)
695                         q->retry = DEFAULT_RETRY;
696         } else if (!strcasecmp(param, "wrapuptime")) {
697                 q->wrapuptime = atoi(val);
698         } else if (!strcasecmp(param, "autofill")) {
699                 q->autofill = ast_true(val);
700         } else if (!strcasecmp(param, "autopause")) {
701                 q->autopause = ast_true(val);
702         } else if (!strcasecmp(param, "maxlen")) {
703                 q->maxlen = atoi(val);
704                 if (q->maxlen < 0)
705                         q->maxlen = 0;
706         } else if (!strcasecmp(param, "servicelevel")) {
707                 q->servicelevel= atoi(val);
708         } else if (!strcasecmp(param, "strategy")) {
709                 q->strategy = strat2int(val);
710                 if (q->strategy < 0) {
711                         ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
712                                 val, q->name);
713                         q->strategy = 0;
714                 }
715         } else if (!strcasecmp(param, "joinempty")) {
716                 if (!strcasecmp(val, "strict"))
717                         q->joinempty = QUEUE_EMPTY_STRICT;
718                 else if (ast_true(val))
719                         q->joinempty = QUEUE_EMPTY_NORMAL;
720                 else
721                         q->joinempty = 0;
722         } else if (!strcasecmp(param, "leavewhenempty")) {
723                 if (!strcasecmp(val, "strict"))
724                         q->leavewhenempty = QUEUE_EMPTY_STRICT;
725                 else if (ast_true(val))
726                         q->leavewhenempty = QUEUE_EMPTY_NORMAL;
727                 else
728                         q->leavewhenempty = 0;
729         } else if (!strcasecmp(param, "eventmemberstatus")) {
730                 q->maskmemberstatus = !ast_true(val);
731         } else if (!strcasecmp(param, "eventwhencalled")) {
732                 q->eventwhencalled = ast_true(val);
733         } else if (!strcasecmp(param, "reportholdtime")) {
734                 q->reportholdtime = ast_true(val);
735         } else if (!strcasecmp(param, "memberdelay")) {
736                 q->memberdelay = atoi(val);
737         } else if (!strcasecmp(param, "weight")) {
738                 q->weight = atoi(val);
739                 if (q->weight)
740                         use_weight++;
741                 /* With Realtime queues, if the last queue using weights is deleted in realtime,
742                    we will not see any effect on use_weight until next reload. */
743         } else if (!strcasecmp(param, "timeoutrestart")) {
744                 q->timeoutrestart = ast_true(val);
745         } else if(failunknown) {
746                 if (linenum >= 0) {
747                         ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
748                                 q->name, param, linenum);
749                 } else {
750                         ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
751                 }
752         }
753 }
754
755 static void rt_handle_member_record(struct ast_call_queue *q, char *interface, const char *penalty_str)
756 {
757         struct member *m, *prev_m;
758         int penalty = 0;
759
760         if(penalty_str) {
761                 penalty = atoi(penalty_str);
762                 if(penalty < 0)
763                         penalty = 0;
764         }
765
766         /* Find the member, or the place to put a new one. */
767         prev_m = NULL;
768         m = q->members;
769         while (m && strcmp(m->interface, interface)) {
770                 prev_m = m;
771                 m = m->next;
772         }
773
774         /* Create a new one if not found, else update penalty */
775         if (!m) {
776                 m = create_queue_member(interface, penalty, 0);
777                 if (m) {
778                         m->dead = 0;
779                         if (prev_m) {
780                                 prev_m->next = m;
781                         } else {
782                                 q->members = m;
783                         }
784                 }
785         } else {
786                 m->dead = 0;    /* Do not delete this one. */
787                 m->penalty = penalty;
788         }
789 }
790
791 static void free_members(struct ast_call_queue *q, int all)
792 {
793         /* Free non-dynamic members */
794         struct member *curm, *next, *prev = NULL;
795
796         for (curm = q->members; curm; curm = next) {
797                 next = curm->next;
798                 if (all || !curm->dynamic) {
799                         if (prev)
800                                 prev->next = next;
801                         else
802                                 q->members = next;
803                         free(curm);
804                 } else 
805                         prev = curm;
806         }
807 }
808
809 static void destroy_queue(struct ast_call_queue *q)
810 {
811         free_members(q, 1);
812         ast_mutex_destroy(&q->lock);
813         free(q);
814 }
815
816 /*!\brief Reload a single queue via realtime.
817    \return Return the queue, or NULL if it doesn't exist.
818    \note Should be called with the global qlock locked. */
819 static struct ast_call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
820 {
821         struct ast_variable *v;
822         struct ast_call_queue *q;
823         struct member *m, *prev_m, *next_m;
824         char *interface;
825         char *tmp, *tmp_name;
826         char tmpbuf[64];        /* Must be longer than the longest queue param name. */
827
828         /* Find the queue in the in-core list (we will create a new one if not found). */
829         AST_LIST_TRAVERSE(&queues, q, list) {
830                 if (!strcasecmp(q->name, queuename)) {
831                         break;
832                 }
833         }
834
835         /* Static queues override realtime. */
836         if (q) {
837                 ast_mutex_lock(&q->lock);
838                 if (!q->realtime) {
839                         if (q->dead) {
840                                 ast_mutex_unlock(&q->lock);
841                                 return NULL;
842                         } else {
843                                 ast_mutex_unlock(&q->lock);
844                                 return q;
845                         }
846                 }
847         } else if (!member_config)
848                 /* Not found in the list, and it's not realtime ... */
849                 return NULL;
850
851         /* Check if queue is defined in realtime. */
852         if (!queue_vars) {
853                 /* Delete queue from in-core list if it has been deleted in realtime. */
854                 if (q) {
855                         /*! \note Hmm, can't seem to distinguish a DB failure from a not
856                            found condition... So we might delete an in-core queue
857                            in case of DB failure. */
858                         ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
859
860                         q->dead = 1;
861                         /* Delete if unused (else will be deleted when last caller leaves). */
862                         if (!q->count) {
863                                 /* Delete. */
864                                 AST_LIST_REMOVE(&queues, q, list);
865                                 ast_mutex_unlock(&q->lock);
866                                 destroy_queue(q);
867                         } else
868                                 ast_mutex_unlock(&q->lock);
869                 }
870                 return NULL;
871         }
872
873         /* Create a new queue if an in-core entry does not exist yet. */
874         if (!q) {
875                 if (!(q = alloc_queue(queuename)))
876                         return NULL;
877                 ast_mutex_lock(&q->lock);
878                 clear_queue(q);
879                 q->realtime = 1;
880                 AST_LIST_INSERT_HEAD(&queues, q, list);
881         }
882         init_queue(q);          /* Ensure defaults for all parameters not set explicitly. */
883
884         v = queue_vars;
885         memset(tmpbuf, 0, sizeof(tmpbuf));
886         while(v) {
887                 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
888                 if((tmp = strchr(v->name, '_')) != NULL) {
889                         ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
890                         tmp_name = tmpbuf;
891                         tmp = tmp_name;
892                         while((tmp = strchr(tmp, '_')) != NULL)
893                                 *tmp++ = '-';
894                 } else
895                         tmp_name = v->name;
896                 queue_set_param(q, tmp_name, v->value, -1, 0);
897                 v = v->next;
898         }
899
900         /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
901         m = q->members;
902         while (m) {
903                 if (!m->dynamic)
904                         m->dead = 1;
905                 m = m->next;
906         }
907
908         interface = ast_category_browse(member_config, NULL);
909         while (interface) {
910                 rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty"));
911                 interface = ast_category_browse(member_config, interface);
912         }
913
914         /* Delete all realtime members that have been deleted in DB. */
915         m = q->members;
916         prev_m = NULL;
917         while (m) {
918                 next_m = m->next;
919                 if (m->dead) {
920                         if (prev_m) {
921                                 prev_m->next = next_m;
922                         } else {
923                                 q->members = next_m;
924                         }
925                         free(m);
926                 } else {
927                         prev_m = m;
928                 }
929                 m = next_m;
930         }
931
932         ast_mutex_unlock(&q->lock);
933
934         return q;
935 }
936
937 static struct ast_call_queue *load_realtime_queue(char *queuename)
938 {
939         struct ast_variable *queue_vars = NULL;
940         struct ast_config *member_config = NULL;
941         struct ast_call_queue *q;
942
943         /* Find the queue in the in-core list first. */
944         AST_LIST_LOCK(&queues);
945         AST_LIST_TRAVERSE(&queues, q, list) {
946                 if (!strcasecmp(q->name, queuename)) {
947                         break;
948                 }
949         }
950         AST_LIST_UNLOCK(&queues);
951
952         if (!q) {
953                 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
954                    queue operations while waiting for the DB.
955
956                    This will be two separate database transactions, so we might
957                    see queue parameters as they were before another process
958                    changed the queue and member list as it was after the change.
959                    Thus we might see an empty member list when a queue is
960                    deleted. In practise, this is unlikely to cause a problem. */
961
962                 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
963                 if (queue_vars) {
964                         member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
965                         if (!member_config) {
966                                 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
967                                 return NULL;
968                         }
969                 }
970
971                 AST_LIST_LOCK(&queues);
972
973                 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
974                 if (member_config)
975                         ast_config_destroy(member_config);
976                 if (queue_vars)
977                         ast_variables_destroy(queue_vars);
978
979                 AST_LIST_UNLOCK(&queues);
980         }
981         return q;
982 }
983
984 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
985 {
986         struct ast_call_queue *q;
987         struct queue_ent *cur, *prev = NULL;
988         int res = -1;
989         int pos = 0;
990         int inserted = 0;
991         enum queue_member_status stat;
992
993         q = load_realtime_queue(queuename);
994         if (!q)
995                 return res;
996
997         AST_LIST_LOCK(&queues);
998         ast_mutex_lock(&q->lock);
999
1000         /* This is our one */
1001         stat = get_member_status(q, qe->max_penalty);
1002         if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1003                 *reason = QUEUE_JOINEMPTY;
1004         else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
1005                 *reason = QUEUE_JOINUNAVAIL;
1006         else if (q->maxlen && (q->count >= q->maxlen))
1007                 *reason = QUEUE_FULL;
1008         else {
1009                 /* There's space for us, put us at the right position inside
1010                  * the queue. 
1011                  * Take into account the priority of the calling user */
1012                 inserted = 0;
1013                 prev = NULL;
1014                 cur = q->head;
1015                 while(cur) {
1016                         /* We have higher priority than the current user, enter
1017                          * before him, after all the other users with priority
1018                          * higher or equal to our priority. */
1019                         if ((!inserted) && (qe->prio > cur->prio)) {
1020                                 insert_entry(q, prev, qe, &pos);
1021                                 inserted = 1;
1022                         }
1023                         cur->pos = ++pos;
1024                         prev = cur;
1025                         cur = cur->next;
1026                 }
1027                 /* No luck, join at the end of the queue */
1028                 if (!inserted)
1029                         insert_entry(q, prev, qe, &pos);
1030                 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1031                 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1032                 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1033                 q->count++;
1034                 res = 0;
1035                 manager_event(EVENT_FLAG_CALL, "Join", 
1036                               "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
1037                               qe->chan->name, 
1038                               qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
1039                               qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
1040                               q->name, qe->pos, q->count );
1041 #if 0
1042 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1043 #endif
1044         }
1045         ast_mutex_unlock(&q->lock);
1046         AST_LIST_UNLOCK(&queues);
1047         return res;
1048 }
1049
1050 static int play_file(struct ast_channel *chan, char *filename)
1051 {
1052         int res;
1053
1054         ast_stopstream(chan);
1055         res = ast_streamfile(chan, filename, chan->language);
1056
1057         if (!res)
1058                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1059         else
1060                 res = 0;
1061
1062         ast_stopstream(chan);
1063
1064         return res;
1065 }
1066
1067 static int valid_exit(struct queue_ent *qe, char digit)
1068 {
1069         int digitlen = strlen(qe->digits);
1070
1071         /* Prevent possible buffer overflow */
1072         if (digitlen < sizeof(qe->digits) - 2) {
1073                 qe->digits[digitlen] = digit;
1074                 qe->digits[digitlen + 1] = '\0';
1075         } else {
1076                 qe->digits[0] = '\0';
1077                 return 0;
1078         }
1079
1080         /* If there's no context to goto, short-circuit */
1081         if (ast_strlen_zero(qe->context))
1082                 return 0;
1083
1084         /* If the extension is bad, then reset the digits to blank */
1085         if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1086                 qe->digits[0] = '\0';
1087                 return 0;
1088         }
1089
1090         /* We have an exact match */
1091         if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1092                 /* Return 1 on a successful goto */
1093                 return 1;
1094         }
1095         return 0;
1096 }
1097
1098 static int say_position(struct queue_ent *qe)
1099 {
1100         int res = 0, avgholdmins, avgholdsecs;
1101         time_t now;
1102
1103         /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1104         time(&now);
1105         if ( (now - qe->last_pos) < 15 )
1106                 return 0;
1107
1108         /* If either our position has changed, or we are over the freq timer, say position */
1109         if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
1110                 return 0;
1111
1112         ast_moh_stop(qe->chan);
1113         /* Say we're next, if we are */
1114         if (qe->pos == 1) {
1115                 res = play_file(qe->chan, qe->parent->sound_next);
1116                 if (res && valid_exit(qe, res))
1117                         goto playout;
1118                 else
1119                         goto posout;
1120         } else {
1121                 res = play_file(qe->chan, qe->parent->sound_thereare);
1122                 if (res && valid_exit(qe, res))
1123                         goto playout;
1124                 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
1125                 if (res && valid_exit(qe, res))
1126                         goto playout;
1127                 res = play_file(qe->chan, qe->parent->sound_calls);
1128                 if (res && valid_exit(qe, res))
1129                         goto playout;
1130         }
1131         /* Round hold time to nearest minute */
1132         avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
1133
1134         /* If they have specified a rounding then round the seconds as well */
1135         if(qe->parent->roundingseconds) {
1136                 avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
1137                 avgholdsecs*= qe->parent->roundingseconds;
1138         } else {
1139                 avgholdsecs=0;
1140         }
1141
1142         if (option_verbose > 2)
1143                 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1144
1145         /* If the hold time is >1 min, if it's enabled, and if it's not
1146            supposed to be only once and we have already said it, say it */
1147         if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1148             (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1149                 res = play_file(qe->chan, qe->parent->sound_holdtime);
1150                 if (res && valid_exit(qe, res))
1151                         goto playout;
1152
1153                 if (avgholdmins>0) {
1154                         if (avgholdmins < 2) {
1155                                 res = play_file(qe->chan, qe->parent->sound_lessthan);
1156                                 if (res && valid_exit(qe, res))
1157                                         goto playout;
1158
1159                                 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
1160                                 if (res && valid_exit(qe, res))
1161                                         goto playout;
1162                         } else {
1163                                 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
1164                                 if (res && valid_exit(qe, res))
1165                                         goto playout;
1166                         }
1167                         
1168                         res = play_file(qe->chan, qe->parent->sound_minutes);
1169                         if (res && valid_exit(qe, res))
1170                                 goto playout;
1171                 }
1172                 if (avgholdsecs>0) {
1173                         res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
1174                         if (res && valid_exit(qe, res))
1175                                 goto playout;
1176
1177                         res = play_file(qe->chan, qe->parent->sound_seconds);
1178                         if (res && valid_exit(qe, res))
1179                                 goto playout;
1180                 }
1181
1182         }
1183
1184  posout:
1185         if (option_verbose > 2)
1186                 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
1187                             qe->chan->name, qe->parent->name, qe->pos);
1188         res = play_file(qe->chan, qe->parent->sound_thanks);
1189
1190  playout:
1191         /* Set our last_pos indicators */
1192         qe->last_pos = now;
1193         qe->last_pos_said = qe->pos;
1194         ast_moh_start(qe->chan, qe->moh);
1195
1196         return res;
1197 }
1198
1199 static void recalc_holdtime(struct queue_ent *qe)
1200 {
1201         int oldvalue, newvalue;
1202
1203         /* Calculate holdtime using a recursive boxcar filter */
1204         /* Thanks to SRT for this contribution */
1205         /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1206
1207         newvalue = time(NULL) - qe->start;
1208
1209         ast_mutex_lock(&qe->parent->lock);
1210         if (newvalue <= qe->parent->servicelevel)
1211                 qe->parent->callscompletedinsl++;
1212         oldvalue = qe->parent->holdtime;
1213         qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
1214         ast_mutex_unlock(&qe->parent->lock);
1215 }
1216
1217
1218 static void leave_queue(struct queue_ent *qe)
1219 {
1220         struct ast_call_queue *q;
1221         struct queue_ent *cur, *prev = NULL;
1222         int pos = 0;
1223
1224         q = qe->parent;
1225         if (!q)
1226                 return;
1227         ast_mutex_lock(&q->lock);
1228
1229         prev = NULL;
1230         cur = q->head;
1231         while(cur) {
1232                 if (cur == qe) {
1233                         q->count--;
1234
1235                         /* Take us out of the queue */
1236                         manager_event(EVENT_FLAG_CALL, "Leave",
1237                                 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
1238                                 qe->chan->name, q->name,  q->count);
1239 #if 0
1240 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1241 #endif
1242                         /* Take us out of the queue */
1243                         if (prev)
1244                                 prev->next = cur->next;
1245                         else
1246                                 q->head = cur->next;
1247                 } else {
1248                         /* Renumber the people after us in the queue based on a new count */
1249                         cur->pos = ++pos;
1250                         prev = cur;
1251                 }
1252                 cur = cur->next;
1253         }
1254         ast_mutex_unlock(&q->lock);
1255         if (q->dead && !q->count) {     
1256                 /* It's dead and nobody is in it, so kill it */
1257                 AST_LIST_LOCK(&queues);
1258                 AST_LIST_REMOVE(&queues, q, list);
1259                 AST_LIST_UNLOCK(&queues);
1260                 destroy_queue(q);
1261         }
1262 }
1263
1264 /* Hang up a list of outgoing calls */
1265 static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
1266 {
1267         struct localuser *oo;
1268
1269         while(outgoing) {
1270                 /* Hangup any existing lines we have open */
1271                 if (outgoing->chan && (outgoing->chan != exception))
1272                         ast_hangup(outgoing->chan);
1273                 oo = outgoing;
1274                 outgoing=outgoing->next;
1275                 free(oo);
1276         }
1277 }
1278
1279 static int update_status(struct ast_call_queue *q, struct member *member, int status)
1280 {
1281         struct member *cur;
1282
1283         /* Since a reload could have taken place, we have to traverse the list to
1284                 be sure it's still valid */
1285         ast_mutex_lock(&q->lock);
1286         cur = q->members;
1287         while(cur) {
1288                 if (member == cur) {
1289                         cur->status = status;
1290                         if (!q->maskmemberstatus) {
1291                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
1292                                         "Queue: %s\r\n"
1293                                         "Location: %s\r\n"
1294                                         "Membership: %s\r\n"
1295                                         "Penalty: %d\r\n"
1296                                         "CallsTaken: %d\r\n"
1297                                         "LastCall: %d\r\n"
1298                                         "Status: %d\r\n"
1299                                         "Paused: %d\r\n",
1300                                 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
1301                                 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
1302                         }
1303                         break;
1304                 }
1305                 cur = cur->next;
1306         }
1307         ast_mutex_unlock(&q->lock);
1308         return 0;
1309 }
1310
1311 static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
1312 {
1313         if (status == AST_CAUSE_BUSY)
1314                 status = AST_DEVICE_BUSY;
1315         else if (status == AST_CAUSE_UNREGISTERED)
1316                 status = AST_DEVICE_UNAVAILABLE;
1317         else if (status == AST_CAUSE_NOSUCHDRIVER)
1318                 status = AST_DEVICE_INVALID;
1319         else
1320                 status = AST_DEVICE_UNKNOWN;
1321         return update_status(q, member, status);
1322 }
1323
1324 /* traverse all defined queues which have calls waiting and contain this member
1325    return 0 if no other queue has precedence (higher weight) or 1 if found  */
1326 static int compare_weight(struct ast_call_queue *rq, struct member *member)
1327 {
1328         struct ast_call_queue *q;
1329         struct member *mem;
1330         int found = 0;
1331         
1332         /* &qlock and &rq->lock already set by try_calling()
1333          * to solve deadlock */
1334         AST_LIST_TRAVERSE(&queues, q, list) {
1335                 if (q == rq) /* don't check myself, could deadlock */
1336                         continue; 
1337                 ast_mutex_lock(&q->lock);
1338                 if (q->count && q->members) {
1339                         for (mem = q->members; mem; mem = mem->next) {
1340                                 if (!strcmp(mem->interface, member->interface)) {
1341                                         ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
1342                                         if (q->weight > rq->weight) {
1343                                                 ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
1344                                                 found = 1;
1345                                                 break;
1346                                         }
1347                                 }
1348                         }
1349                 }
1350                 ast_mutex_unlock(&q->lock);
1351                 if (found) 
1352                         break;
1353         }
1354         return found;
1355 }
1356
1357 static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
1358 {
1359         int res;
1360         int status;
1361         char tech[256];
1362         char *location;
1363
1364         if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
1365                 if (option_debug)
1366                         ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
1367                 if (qe->chan->cdr)
1368                         ast_cdr_busy(qe->chan->cdr);
1369                 tmp->stillgoing = 0;
1370                 (*busies)++;
1371                 return 0;
1372         }
1373         
1374         if (tmp->member->paused) {
1375                 if (option_debug)
1376                         ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
1377                 if (qe->chan->cdr)
1378                         ast_cdr_busy(qe->chan->cdr);
1379                 tmp->stillgoing = 0;
1380                 return 0;
1381         }
1382         if (use_weight && compare_weight(qe->parent,tmp->member)) {
1383                 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
1384                 if (qe->chan->cdr)
1385                         ast_cdr_busy(qe->chan->cdr);
1386                 tmp->stillgoing = 0;
1387                 (*busies)++;
1388                 return 0;
1389         }
1390
1391         ast_copy_string(tech, tmp->interface, sizeof(tech));
1392         if ((location = strchr(tech, '/')))
1393                 *location++ = '\0';
1394         else
1395                 location = "";
1396
1397         /* Request the peer */
1398         tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
1399         if (!tmp->chan) {                       /* If we can't, just go on to the next call */
1400 #if 0
1401                 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
1402 #endif                  
1403                 if (qe->chan->cdr)
1404                         ast_cdr_busy(qe->chan->cdr);
1405                 tmp->stillgoing = 0;
1406                 update_dial_status(qe->parent, tmp->member, status);
1407                 (*busies)++;
1408                 return 0;
1409         } else if (status != tmp->oldstatus) 
1410                 update_dial_status(qe->parent, tmp->member, status);
1411         
1412         tmp->chan->appl = "AppQueue";
1413         tmp->chan->data = "(Outgoing Line)";
1414         tmp->chan->whentohangup = 0;
1415         if (tmp->chan->cid.cid_num)
1416                 free(tmp->chan->cid.cid_num);
1417         tmp->chan->cid.cid_num = NULL;
1418         if (tmp->chan->cid.cid_name)
1419                 free(tmp->chan->cid.cid_name);
1420         tmp->chan->cid.cid_name = NULL;
1421         if (tmp->chan->cid.cid_ani)
1422                 free(tmp->chan->cid.cid_ani);
1423         tmp->chan->cid.cid_ani = NULL;
1424         if (qe->chan->cid.cid_num)
1425                 tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
1426         if (qe->chan->cid.cid_name)
1427                 tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
1428         if (qe->chan->cid.cid_ani)
1429                 tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
1430
1431         /* Inherit specially named variables from parent channel */
1432         ast_channel_inherit_variables(qe->chan, tmp->chan);
1433
1434         /* Presense of ADSI CPE on outgoing channel follows ours */
1435         tmp->chan->adsicpe = qe->chan->adsicpe;
1436
1437         /* Place the call, but don't wait on the answer */
1438         res = ast_call(tmp->chan, location, 0);
1439         if (res) {
1440                 /* Again, keep going even if there's an error */
1441                 if (option_debug)
1442                         ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
1443                 else if (option_verbose > 2)
1444                         ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
1445                 ast_hangup(tmp->chan);
1446                 tmp->chan = NULL;
1447                 tmp->stillgoing = 0;
1448                 (*busies)++;
1449                 return 0;
1450         } else {
1451                 if (qe->parent->eventwhencalled) {
1452                         manager_event(EVENT_FLAG_AGENT, "AgentCalled",
1453                                                 "AgentCalled: %s\r\n"
1454                                                 "ChannelCalling: %s\r\n"
1455                                                 "CallerID: %s\r\n"
1456                                                 "CallerIDName: %s\r\n"
1457                                                 "Context: %s\r\n"
1458                                                 "Extension: %s\r\n"
1459                                                 "Priority: %d\r\n",
1460                                                 tmp->interface, qe->chan->name,
1461                                                 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
1462                                                 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
1463                                                 qe->chan->context, qe->chan->exten, qe->chan->priority);
1464                 }
1465                 if (option_verbose > 2)
1466                         ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
1467         }
1468         return 1;
1469 }
1470
1471 static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
1472 {
1473         struct localuser *cur;
1474         struct localuser *best;
1475         int bestmetric=0;
1476
1477         do {
1478                 best = NULL;
1479                 cur = outgoing;
1480                 while(cur) {
1481                         if (cur->stillgoing &&                                  /* Not already done */
1482                                 !cur->chan &&                                   /* Isn't already going */
1483                                 (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
1484                                         bestmetric = cur->metric;
1485                                         best = cur;
1486                         }
1487                         cur = cur->next;
1488                 }
1489                 if (best) {
1490                         if (!qe->parent->strategy) {
1491                                 /* Ring everyone who shares this best metric (for ringall) */
1492                                 cur = outgoing;
1493                                 while(cur) {
1494                                         if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
1495                                                 if (option_debug)
1496                                                         ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
1497                                                 ring_entry(qe, cur, busies);
1498                                         }
1499                                         cur = cur->next;
1500                                 }
1501                         } else {
1502                                 /* Ring just the best channel */
1503                                 if (option_debug)
1504                                         ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
1505                                 ring_entry(qe, best, busies);
1506                         }
1507                 }
1508         } while (best && !best->chan);
1509         if (!best) {
1510                 if (option_debug)
1511                         ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
1512                 return 0;
1513         }
1514         return 1;
1515 }
1516
1517 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
1518 {
1519         struct localuser *cur;
1520         struct localuser *best;
1521         int bestmetric=0;
1522
1523         best = NULL;
1524         cur = outgoing;
1525         while(cur) {
1526                 if (cur->stillgoing &&                                  /* Not already done */
1527                         !cur->chan &&                                   /* Isn't already going */
1528                         (!best || (cur->metric < bestmetric))) {        /* We haven't found one yet, or it's better */
1529                                 bestmetric = cur->metric;
1530                                 best = cur;
1531                 }
1532                 cur = cur->next;
1533         }
1534         if (best) {
1535                 /* Ring just the best channel */
1536                 if (option_debug)
1537                         ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
1538                 qe->parent->rrpos = best->metric % 1000;
1539         } else {
1540                 /* Just increment rrpos */
1541                 if (qe->parent->wrapped) {
1542                         /* No more channels, start over */
1543                         qe->parent->rrpos = 0;
1544                 } else {
1545                         /* Prioritize next entry */
1546                         qe->parent->rrpos++;
1547                 }
1548         }
1549         qe->parent->wrapped = 0;
1550         return 0;
1551 }
1552
1553 static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
1554 {
1555         int res;
1556
1557         ast_stopstream(chan);
1558         res = ast_streamfile(chan, filename, chan->language);
1559
1560         if (!res) {
1561                 /* Wait for a keypress */
1562                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1563                 if (res <= 0 || !valid_exit(qe, res))
1564                         res = 0;
1565
1566                 /* Stop playback */
1567                 ast_stopstream(chan);
1568         } else {
1569                 res = 0;
1570         }
1571         
1572         /*if (res) {
1573                 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
1574                 res = 0;
1575         }*/
1576
1577         return res;
1578 }
1579
1580 static int say_periodic_announcement(struct queue_ent *qe)
1581 {
1582         int res = 0;
1583         time_t now;
1584
1585         /* Get the current time */
1586         time(&now);
1587
1588         /* Check to see if it is time to announce */
1589         if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
1590                 return 0;
1591
1592         /* Stop the music on hold so we can play our own file */
1593         ast_moh_stop(qe->chan);
1594
1595         if (option_verbose > 2)
1596                 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
1597
1598         /* Check to make sure we have a sound file. If not, reset to the first sound file */
1599         if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
1600                 qe->last_periodic_announce_sound = 0;
1601         }
1602         
1603         /* play the announcement */
1604         res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
1605
1606         /* Resume Music on Hold */
1607         ast_moh_start(qe->chan, qe->moh);
1608
1609         /* update last_periodic_announce_time */
1610         qe->last_periodic_announce_time = now;
1611
1612         /* Update the current periodic announcement to the next announcement */
1613         qe->last_periodic_announce_sound++;
1614         
1615         return res;
1616 }
1617
1618 static void record_abandoned(struct queue_ent *qe)
1619 {
1620         ast_mutex_lock(&qe->parent->lock);
1621         qe->parent->callsabandoned++;
1622         ast_mutex_unlock(&qe->parent->lock);
1623 }
1624
1625
1626 #define AST_MAX_WATCHERS 256
1627
1628 #define BUILD_WATCHERS do { \
1629                 o = outgoing; \
1630                 found = -1; \
1631                 pos = 1; \
1632                 numlines = 0; \
1633                 watchers[0] = in; \
1634                 while(o) { \
1635                         /* Keep track of important channels */ \
1636                         if (o->stillgoing) { \
1637                                 stillgoing = 1; \
1638                                 if (o->chan) { \
1639                                         watchers[pos++] = o->chan; \
1640                                         found = 1; \
1641                                 } \
1642                         } \
1643                         o = o->next; \
1644                         numlines++; \
1645                 } \
1646         } while(0)
1647         
1648 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
1649 {
1650         char *queue = qe->parent->name;
1651         char on[256] = "";
1652         struct localuser *o;
1653         int found;
1654         int numlines;
1655         int status;
1656         int sentringing = 0;
1657         int numbusies = prebusies;
1658         int numnochan = 0;
1659         int stillgoing = 0;
1660         int orig = *to;
1661         struct ast_frame *f;
1662         struct localuser *peer = NULL;
1663         struct ast_channel *watchers[AST_MAX_WATCHERS];
1664         int pos;
1665         struct ast_channel *winner;
1666         struct ast_channel *in = qe->chan;
1667         
1668         while(*to && !peer) {
1669                 BUILD_WATCHERS;
1670                 if ((found < 0) && stillgoing && !qe->parent->strategy) {
1671                         /* On "ringall" strategy we only move to the next penalty level
1672                            when *all* ringing phones are done in the current penalty level */
1673                         ring_one(qe, outgoing, &numbusies);
1674                         BUILD_WATCHERS;
1675                 }
1676                 if (found < 0) {
1677                         if (numlines == (numbusies + numnochan)) {
1678                                 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
1679                         } else {
1680                                 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
1681                         }
1682                         *to = 0;
1683                         return NULL;
1684                 }
1685                 winner = ast_waitfor_n(watchers, pos, to);
1686                 o = outgoing;
1687                 while(o) {
1688                         if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
1689                                 if (!peer) {
1690                                         if (option_verbose > 2)
1691                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1692                                         peer = o;
1693                                 }
1694                         } else if (o->chan && (o->chan == winner)) {
1695                                 ast_copy_string(on, o->member->interface, sizeof(on));
1696                                 if (!ast_strlen_zero(o->chan->call_forward)) {
1697                                         char tmpchan[256]="";
1698                                         char *stuff;
1699                                         char *tech;
1700                                         ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
1701                                         if ((stuff = strchr(tmpchan, '/'))) {
1702                                                 *stuff = '\0';
1703                                                 stuff++;
1704                                                 tech = tmpchan;
1705                                         } else {
1706                                                 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
1707                                                 stuff = tmpchan;
1708                                                 tech = "Local";
1709                                         }
1710                                         /* Before processing channel, go ahead and check for forwarding */
1711                                         if (option_verbose > 2)
1712                                                 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
1713                                         /* Setup parameters */
1714                                         o->chan = ast_request(tech, in->nativeformats, stuff, &status);
1715                                         if (status != o->oldstatus) 
1716                                                 update_dial_status(qe->parent, o->member, status);                                              
1717                                         if (!o->chan) {
1718                                                 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
1719                                                 o->stillgoing = 0;
1720                                                 numnochan++;
1721                                         } else {
1722                                                 if (o->chan->cid.cid_num)
1723                                                         free(o->chan->cid.cid_num);
1724                                                 o->chan->cid.cid_num = NULL;
1725                                                 if (o->chan->cid.cid_name)
1726                                                         free(o->chan->cid.cid_name);
1727                                                 o->chan->cid.cid_name = NULL;
1728
1729                                                 if (in->cid.cid_num) {
1730                                                         o->chan->cid.cid_num = strdup(in->cid.cid_num);
1731                                                         if (!o->chan->cid.cid_num)
1732                                                                 ast_log(LOG_WARNING, "Out of memory\n");        
1733                                                 }
1734                                                 if (in->cid.cid_name) {
1735                                                         o->chan->cid.cid_name = strdup(in->cid.cid_name);
1736                                                         if (!o->chan->cid.cid_name)
1737                                                                 ast_log(LOG_WARNING, "Out of memory\n");        
1738                                                 }
1739                                                 ast_string_field_set(o->chan, accountcode, in->accountcode);
1740                                                 o->chan->cdrflags = in->cdrflags;
1741
1742                                                 if (in->cid.cid_ani) {
1743                                                         if (o->chan->cid.cid_ani)
1744                                                                 free(o->chan->cid.cid_ani);
1745                                                         o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
1746                                                 }
1747                                                 if (o->chan->cid.cid_rdnis) 
1748                                                         free(o->chan->cid.cid_rdnis);
1749                                                 if (!ast_strlen_zero(in->macroexten))
1750                                                         o->chan->cid.cid_rdnis = strdup(in->macroexten);
1751                                                 else
1752                                                         o->chan->cid.cid_rdnis = strdup(in->exten);
1753                                                 if (ast_call(o->chan, tmpchan, 0)) {
1754                                                         ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
1755                                                         o->stillgoing = 0;
1756                                                         ast_hangup(o->chan);
1757                                                         o->chan = NULL;
1758                                                         numnochan++;
1759                                                 }
1760                                         }
1761                                         /* Hangup the original channel now, in case we needed it */
1762                                         ast_hangup(winner);
1763                                         continue;
1764                                 }
1765                                 f = ast_read(winner);
1766                                 if (f) {
1767                                         if (f->frametype == AST_FRAME_CONTROL) {
1768                                                 switch(f->subclass) {
1769                                         case AST_CONTROL_ANSWER:
1770                                                         /* This is our guy if someone answered. */
1771                                                         if (!peer) {
1772                                                                 if (option_verbose > 2)
1773                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1774                                                                 peer = o;
1775                                                         }
1776                                                         break;
1777                                                 case AST_CONTROL_BUSY:
1778                                                         if (option_verbose > 2)
1779                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
1780                                                         o->stillgoing = 0;
1781                                                         if (in->cdr)
1782                                                                 ast_cdr_busy(in->cdr);
1783                                                         ast_hangup(o->chan);
1784                                                         o->chan = NULL;
1785                                                         if (qe->parent->strategy) {
1786                                                                 if (qe->parent->timeoutrestart)
1787                                                                         *to = orig;
1788                                                                 ring_one(qe, outgoing, &numbusies);
1789                                                         }
1790                                                         numbusies++;
1791                                                         break;
1792                                                 case AST_CONTROL_CONGESTION:
1793                                                         if (option_verbose > 2)
1794                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
1795                                                         o->stillgoing = 0;
1796                                                         if (in->cdr)
1797                                                                 ast_cdr_busy(in->cdr);
1798                                                         ast_hangup(o->chan);
1799                                                         o->chan = NULL;
1800                                                         if (qe->parent->strategy) {
1801                                                                 if (qe->parent->timeoutrestart)
1802                                                                         *to = orig;
1803                                                                 ring_one(qe, outgoing, &numbusies);
1804                                                         }
1805                                                         numbusies++;
1806                                                         break;
1807                                                 case AST_CONTROL_RINGING:
1808                                                         if (option_verbose > 2)
1809                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
1810                                                         if (!sentringing) {
1811 #if 0
1812                                                                 ast_indicate(in, AST_CONTROL_RINGING);
1813 #endif                                                          
1814                                                                 sentringing++;
1815                                                         }
1816                                                         break;
1817                                                 case AST_CONTROL_OFFHOOK:
1818                                                         /* Ignore going off hook */
1819                                                         break;
1820                                                 default:
1821                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
1822                                                 }
1823                                         }
1824                                         ast_frfree(f);
1825                                 } else {
1826                                         o->stillgoing = 0;
1827                                         ast_hangup(o->chan);
1828                                         o->chan = NULL;
1829                                         if (qe->parent->strategy) {
1830                                                 if (qe->parent->timeoutrestart)
1831                                                         *to = orig;
1832                                                 ring_one(qe, outgoing, &numbusies);
1833                                         }
1834                                 }
1835                         }
1836                         o = o->next;
1837                 }
1838                 if (winner == in) {
1839                         f = ast_read(in);
1840 #if 0
1841                         if (f && (f->frametype != AST_FRAME_VOICE))
1842                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
1843                         else if (!f || (f->frametype != AST_FRAME_VOICE))
1844                                 printf("Hangup received on %s\n", in->name);
1845 #endif
1846                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
1847                                 /* Got hung up */
1848                                 *to=-1;
1849                                 if (f)
1850                                         ast_frfree(f);
1851                                 return NULL;
1852                         }
1853                         if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
1854                                 if (option_verbose > 3)
1855                                         ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
1856                                 *to=0;
1857                                 ast_frfree(f);
1858                                 return NULL;
1859                         }
1860                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
1861                                 if (option_verbose > 3)
1862                                         ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
1863                                 *to=0;
1864                                 *digit=f->subclass;
1865                                 ast_frfree(f);
1866                                 return NULL;
1867                         }
1868                         ast_frfree(f);
1869                 }
1870                 if (!*to) {
1871                         if (option_verbose > 2)
1872                                 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
1873                         if (qe->parent->autopause) {
1874                                 if (!set_member_paused(qe->parent->name, on, 1)) {
1875                                         if (option_verbose > 2)
1876                                                 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", on, qe->parent->name);
1877                                 } else {
1878                                         if (option_verbose > 2)
1879                                                 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", on, qe->parent->name);
1880                                 }
1881                         }
1882                 }
1883         }
1884
1885         return peer;
1886         
1887 }
1888
1889 static int is_our_turn(struct queue_ent *qe)
1890 {
1891         struct queue_ent *ch;
1892         int res;
1893
1894         /* Atomically read the parent head -- does not need a lock */
1895         ch = qe->parent->head;
1896         /* If we are now at the top of the head, break out */
1897         if ((ch == qe) || (qe->parent->autofill)) {
1898                 if (option_debug)
1899                         ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
1900                 res = 1;
1901         } else {
1902                 if (option_debug)
1903                         ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
1904                 res = 0;
1905         }
1906         return res;
1907 }
1908
1909 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
1910 {
1911         int res = 0;
1912
1913         /* This is the holding pen for callers 2 through maxlen */
1914         for (;;) {
1915                 enum queue_member_status stat;
1916
1917                 if (is_our_turn(qe))
1918                         break;
1919
1920                 /* If we have timed out, break out */
1921                 if (qe->expire && (time(NULL) > qe->expire)) {
1922                         *reason = QUEUE_TIMEOUT;
1923                         ast_queue_log(qe->parent->name, qe->chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe->pos);
1924                         break;
1925                 }
1926
1927                 stat = get_member_status(qe->parent, qe->max_penalty);
1928
1929                 /* leave the queue if no agents, if enabled */
1930                 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
1931                         *reason = QUEUE_LEAVEEMPTY;
1932                         leave_queue(qe);
1933                         break;
1934                 }
1935
1936                 /* leave the queue if no reachable agents, if enabled */
1937                 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
1938                         *reason = QUEUE_LEAVEUNAVAIL;
1939                         leave_queue(qe);
1940                         break;
1941                 }
1942
1943                 /* Make a position announcement, if enabled */
1944                 if (qe->parent->announcefrequency && !ringing)
1945                         res = say_position(qe);
1946                 if (res)
1947                         break;
1948
1949                 /* Make a periodic announcement, if enabled */
1950                 if (qe->parent->periodicannouncefrequency && !ringing)
1951                         res = say_periodic_announcement(qe);
1952
1953                 /* Wait a second before checking again */
1954                 if (!res) res = ast_waitfordigit(qe->chan, RECHECK * 1000);
1955                 if (res)
1956                         break;
1957         }
1958         return res;
1959 }
1960
1961 static int update_queue(struct ast_call_queue *q, struct member *member)
1962 {
1963         struct member *cur;
1964
1965         /* Since a reload could have taken place, we have to traverse the list to
1966                 be sure it's still valid */
1967         ast_mutex_lock(&q->lock);
1968         cur = q->members;
1969         while(cur) {
1970                 if (member == cur) {
1971                         time(&cur->lastcall);
1972                         cur->calls++;
1973                         break;
1974                 }
1975                 cur = cur->next;
1976         }
1977         q->callscompleted++;
1978         ast_mutex_unlock(&q->lock);
1979         return 0;
1980 }
1981
1982 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
1983 {
1984         if (mem->penalty > qe->max_penalty)
1985                 return -1;
1986
1987         switch (q->strategy) {
1988         case QUEUE_STRATEGY_RINGALL:
1989                 /* Everyone equal, except for penalty */
1990                 tmp->metric = mem->penalty * 1000000;
1991                 break;
1992         case QUEUE_STRATEGY_ROUNDROBIN:
1993                 if (!pos) {
1994                         if (!q->wrapped) {
1995                                 /* No more channels, start over */
1996                                 q->rrpos = 0;
1997                         } else {
1998                                 /* Prioritize next entry */
1999                                 q->rrpos++;
2000                         }
2001                         q->wrapped = 0;
2002                 }
2003                 /* Fall through */
2004         case QUEUE_STRATEGY_RRMEMORY:
2005                 if (pos < q->rrpos) {
2006                         tmp->metric = 1000 + pos;
2007                 } else {
2008                         if (pos > q->rrpos)
2009                                 /* Indicate there is another priority */
2010                                 q->wrapped = 1;
2011                         tmp->metric = pos;
2012                 }
2013                 tmp->metric += mem->penalty * 1000000;
2014                 break;
2015         case QUEUE_STRATEGY_RANDOM:
2016                 tmp->metric = rand() % 1000;
2017                 tmp->metric += mem->penalty * 1000000;
2018                 break;
2019         case QUEUE_STRATEGY_FEWESTCALLS:
2020                 tmp->metric = mem->calls;
2021                 tmp->metric += mem->penalty * 1000000;
2022                 break;
2023         case QUEUE_STRATEGY_LEASTRECENT:
2024                 if (!mem->lastcall)
2025                         tmp->metric = 0;
2026                 else
2027                         tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2028                 tmp->metric += mem->penalty * 1000000;
2029                 break;
2030         default:
2031                 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2032                 break;
2033         }
2034         return 0;
2035 }
2036
2037 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
2038 {
2039         struct member *cur;
2040         struct localuser *outgoing=NULL, *tmp = NULL;
2041         int to;
2042         char restofit[AST_MAX_EXTENSION];
2043         char oldexten[AST_MAX_EXTENSION]="";
2044         char oldcontext[AST_MAX_CONTEXT]="";
2045         char queuename[256]="";
2046         char *newnum;
2047         struct ast_channel *peer;
2048         struct ast_channel *which;
2049         struct localuser *lpeer;
2050         struct member *member;
2051         int res = 0, bridge = 0;
2052         int numbusies = 0;
2053         int x=0;
2054         char *announce = NULL;
2055         char digit = 0;
2056         time_t callstart;
2057         time_t now = time(NULL);
2058         struct ast_bridge_config bridge_config;
2059         char nondataquality = 1;
2060
2061         memset(&bridge_config, 0, sizeof(bridge_config));
2062         time(&now);
2063                 
2064         for (; options && *options; options++)
2065                 switch (*options) {
2066                 case 't':
2067                         ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
2068                         break;
2069                 case 'T':
2070                         ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
2071                         break;
2072                 case 'w':
2073                         ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
2074                         break;
2075                 case 'W':
2076                         ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
2077                         break;
2078                 case 'd':
2079                         nondataquality = 0;
2080                         break;
2081                 case 'h':
2082                         ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
2083                         break;
2084                 case 'H':
2085                         ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
2086                         break;
2087                 case 'n':
2088                         if ((now - qe->start >= qe->parent->timeout))
2089                                 *go_on = 1;
2090                         break;
2091                 }
2092
2093         /* Hold the lock while we setup the outgoing calls */
2094         if (use_weight) 
2095                 AST_LIST_LOCK(&queues);
2096         ast_mutex_lock(&qe->parent->lock);
2097         if (option_debug)
2098                 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 
2099                                                         qe->chan->name);
2100         ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
2101         cur = qe->parent->members;
2102         if (!ast_strlen_zero(qe->announce))
2103                 announce = qe->announce;
2104         if (!ast_strlen_zero(announceoverride))
2105                 announce = announceoverride;
2106
2107         while(cur) {
2108                 if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
2109                         ast_mutex_unlock(&qe->parent->lock);
2110                         if (use_weight) 
2111                                 AST_LIST_UNLOCK(&queues);
2112                         goto out;
2113                 }
2114                 tmp->stillgoing = -1;
2115                 if (option_debug) {
2116                         if (url)
2117                                 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
2118                         else 
2119                                 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
2120                 }
2121
2122                 tmp->member = cur;              /* Never directly dereference!  Could change on reload */
2123                 tmp->oldstatus = cur->status;
2124                 tmp->lastcall = cur->lastcall;
2125                 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
2126                 /* If we're dialing by extension, look at the extension to know what to dial */
2127                 if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
2128                         newnum++;
2129                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
2130                         snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
2131                         if (option_debug)
2132                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
2133                 }
2134                 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2135                    just calculate their metric for the appropriate strategy */
2136                 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
2137                         /* Put them in the list of outgoing thingies...  We're ready now. 
2138                            XXX If we're forcibly removed, these outgoing calls won't get
2139                            hung up XXX */
2140                         tmp->next = outgoing;
2141                         outgoing = tmp;         
2142                         /* If this line is up, don't try anybody else */
2143                         if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
2144                                 break;
2145                 } else {
2146                         free(tmp);
2147                 }
2148
2149                 cur = cur->next;
2150         }
2151         if (qe->parent->timeout)
2152                 to = qe->parent->timeout * 1000;
2153         else
2154                 to = -1;
2155         ring_one(qe, outgoing, &numbusies);
2156         ast_mutex_unlock(&qe->parent->lock);
2157         if (use_weight) 
2158                 AST_LIST_UNLOCK(&queues);
2159         lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
2160         ast_mutex_lock(&qe->parent->lock);
2161         if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2162                 store_next(qe, outgoing);
2163         }
2164         ast_mutex_unlock(&qe->parent->lock);
2165         if (lpeer)
2166                 peer = lpeer->chan;
2167         else
2168                 peer = NULL;
2169         if (!peer) {
2170                 if (to) {
2171                         /* Must gotten hung up */
2172                         record_abandoned(qe);
2173                         res = -1;
2174                 } else {
2175                         res = digit;
2176                 }
2177                 if (option_debug)
2178                         ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
2179                 goto out;
2180         }
2181         if (peer) {
2182                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
2183                    we will always return with -1 so that it is hung up properly after the 
2184                    conversation.  */
2185                 qe->handled++;
2186                 if (!strcmp(qe->chan->tech->type, "Zap"))
2187                         ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2188                 if (!strcmp(peer->tech->type, "Zap"))
2189                         ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2190                 /* Update parameters for the queue */
2191                 recalc_holdtime(qe);
2192                 member = lpeer->member;
2193                 hangupcalls(outgoing, peer);
2194                 outgoing = NULL;
2195                 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
2196                         int res2;
2197                         res2 = ast_autoservice_start(qe->chan);
2198                         if (!res2) {
2199                                 if (qe->parent->memberdelay) {
2200                                         ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
2201                                         res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
2202                                 }
2203                                 if (!res2 && announce) {
2204                                         if (play_file(peer, announce))
2205                                                 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
2206                                 }
2207                                 if (!res2 && qe->parent->reportholdtime) {
2208                                         if (!play_file(peer, qe->parent->sound_reporthold)) {
2209                                                 int holdtime;
2210
2211                                                 time(&now);
2212                                                 holdtime = abs((now - qe->start) / 60);
2213                                                 if (holdtime < 2) {
2214                                                         play_file(peer, qe->parent->sound_lessthan);
2215                                                         ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
2216                                                 } else 
2217                                                         ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
2218                                                 play_file(peer, qe->parent->sound_minutes);
2219                                         }
2220                                 }
2221                         }
2222                         res2 |= ast_autoservice_stop(qe->chan);
2223                         if (peer->_softhangup) {
2224                                 /* Agent must have hung up */
2225                                 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
2226                                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
2227                                 record_abandoned(qe);
2228                                 if (qe->parent->eventwhencalled) {
2229                                         manager_event(EVENT_FLAG_AGENT, "AgentDump",
2230                                                       "Queue: %s\r\n"
2231                                                       "Uniqueid: %s\r\n"
2232                                                       "Channel: %s\r\n"
2233                                                       "Member: %s\r\n",
2234                                                       queuename, qe->chan->uniqueid, peer->name, member->interface);
2235                                 }
2236                                 ast_hangup(peer);
2237                                 goto out;
2238                         } else if (res2) {
2239                                 /* Caller must have hung up just before being connected*/
2240                                 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
2241                                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2242                                 record_abandoned(qe);
2243                                 ast_hangup(peer);
2244                                 return -1;
2245                         }
2246                 }
2247                 /* Stop music on hold */
2248                 ast_moh_stop(qe->chan);
2249                 /* If appropriate, log that we have a destination channel */
2250                 if (qe->chan->cdr)
2251                         ast_cdr_setdestchan(qe->chan->cdr, peer->name);
2252                 /* Make sure channels are compatible */
2253                 res = ast_channel_make_compatible(qe->chan, peer);
2254                 if (res < 0) {
2255                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
2256                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
2257                         record_abandoned(qe);
2258                         ast_hangup(peer);
2259                         return -1;
2260                 }
2261                 /* Begin Monitoring */
2262                 if (qe->parent->monfmt && *qe->parent->monfmt) {
2263                         const char *monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2264                         if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
2265                                 which = qe->chan;
2266                         else
2267                                 which = peer;
2268                         if (monitorfilename)
2269                                 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
2270                         else if (qe->chan->cdr) 
2271                                 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
2272                         else {
2273                                 /* Last ditch effort -- no CDR, make up something */
2274                                 char tmpid[256];
2275                                 snprintf(tmpid, sizeof(tmpid), "chan-%x", rand());
2276                                 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
2277                         }
2278                         if (qe->parent->monjoin)
2279                                 ast_monitor_setjoinfiles(which, 1);
2280                 }
2281                 /* Drop out of the queue at this point, to prepare for next caller */
2282                 leave_queue(qe);                        
2283                 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
2284                         if (option_debug)
2285                                 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
2286                         ast_channel_sendurl(peer, url);
2287                 }
2288                 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
2289                 if (qe->parent->eventwhencalled)
2290                         manager_event(EVENT_FLAG_AGENT, "AgentConnect",
2291                                       "Queue: %s\r\n"
2292                                       "Uniqueid: %s\r\n"
2293                                       "Channel: %s\r\n"
2294                                       "Member: %s\r\n"
2295                                       "Holdtime: %ld\r\n",
2296                                       queuename, qe->chan->uniqueid, peer->name, member->interface,
2297                                       (long)time(NULL) - qe->start);
2298                 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
2299                 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
2300                 time(&callstart);
2301
2302                 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
2303
2304                 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
2305                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
2306                 } else if (qe->chan->_softhangup) {
2307                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
2308                                       (long)(callstart - qe->start), (long)(time(NULL) - callstart));
2309                         if (qe->parent->eventwhencalled)
2310                                 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2311                                               "Queue: %s\r\n"
2312                                               "Uniqueid: %s\r\n"
2313                                               "Channel: %s\r\n"
2314                                               "Member: %s\r\n"
2315                                               "HoldTime: %ld\r\n"
2316                                               "TalkTime: %ld\r\n"
2317                                               "Reason: caller\r\n",
2318                                               queuename, qe->chan->uniqueid, peer->name, member->interface,
2319                                               (long)(callstart - qe->start), (long)(time(NULL) - callstart));
2320                 } else {
2321                         ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
2322                         if (qe->parent->eventwhencalled)
2323                                 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2324                                               "Queue: %s\r\n"
2325                                               "Uniqueid: %s\r\n"
2326                                               "Channel: %s\r\n"
2327                                               "HoldTime: %ld\r\n"
2328                                               "TalkTime: %ld\r\n"
2329                                               "Reason: agent\r\n",
2330                                               queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
2331                                               (long)(time(NULL) - callstart));
2332                 }
2333
2334                 if(bridge != AST_PBX_NO_HANGUP_PEER)
2335                         ast_hangup(peer);
2336                 update_queue(qe->parent, member);
2337                 if (bridge == 0) 
2338                         res = 1; /* JDG: bridge successfully, leave app_queue */
2339                 else 
2340                         res = bridge; /* bridge error, stay in the queue */
2341         }       
2342 out:
2343         hangupcalls(outgoing, NULL);
2344         return res;
2345 }
2346
2347 static int wait_a_bit(struct queue_ent *qe)
2348 {
2349         /* Don't need to hold the lock while we setup the outgoing calls */
2350         int retrywait = qe->parent->retry * 1000;
2351
2352         return ast_waitfordigit(qe->chan, retrywait);
2353 }
2354
2355 static struct member * interface_exists(struct ast_call_queue *q, char *interface)
2356 {
2357         struct member *mem;
2358
2359         if (q)
2360                 for (mem = q->members; mem; mem = mem->next)
2361                         if (!strcasecmp(interface, mem->interface))
2362                                 return mem;
2363
2364         return NULL;
2365 }
2366
2367
2368 /* Dump all members in a specific queue to the database
2369  *
2370  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
2371  *
2372  */
2373 static void dump_queue_members(struct ast_call_queue *pm_queue)
2374 {
2375         struct member *cur_member;
2376         char value[PM_MAX_LEN];
2377         int value_len = 0;
2378         int res;
2379
2380         memset(value, 0, sizeof(value));
2381
2382         if (!pm_queue)
2383                 return;
2384
2385         for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
2386                 if (!cur_member->dynamic)
2387                         continue;
2388
2389                 res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
2390                                cur_member->interface, cur_member->penalty, cur_member->paused,
2391                                cur_member->next ? "|" : "");
2392                 if (res != strlen(value + value_len)) {
2393                         ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
2394                         break;
2395                 }
2396                 value_len += res;
2397         }
2398         
2399         if (value_len && !cur_member) {
2400                 if (ast_db_put(pm_family, pm_queue->name, value))
2401                         ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
2402         } else
2403                 /* Delete the entry if the queue is empty or there is an error */
2404                 ast_db_del(pm_family, pm_queue->name);
2405 }
2406
2407 static int remove_from_queue(char *queuename, char *interface)
2408 {
2409         struct ast_call_queue *q;
2410         struct member *last_member, *look;
2411         int res = RES_NOSUCHQUEUE;
2412
2413         AST_LIST_LOCK(&queues);
2414         AST_LIST_TRAVERSE(&queues, q, list) {
2415                 ast_mutex_lock(&q->lock);
2416                 if (!strcmp(q->name, queuename)) {
2417                         if ((last_member = interface_exists(q, interface))) {
2418                                 if ((look = q->members) == last_member) {
2419                                         q->members = last_member->next;
2420                                 } else {
2421                                         while (look != NULL) {
2422                                                 if (look->next == last_member) {
2423                                                         look->next = last_member->next;
2424                                                         break;
2425                                                 } else {
2426                                                          look = look->next;
2427                                                 }
2428                                         }
2429                                 }
2430                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
2431                                                 "Queue: %s\r\n"
2432                                                 "Location: %s\r\n",
2433                                         q->name, last_member->interface);
2434                                 free(last_member);
2435
2436                                 if (queue_persistent_members)
2437                                     dump_queue_members(q);
2438
2439                                 res = RES_OKAY;
2440                         } else {
2441                                 res = RES_EXISTS;
2442                         }
2443                         ast_mutex_unlock(&q->lock);
2444                         break;
2445                 }
2446                 ast_mutex_unlock(&q->lock);
2447         }
2448         AST_LIST_UNLOCK(&queues);
2449         return res;
2450 }
2451
2452 static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
2453 {
2454         struct ast_call_queue *q;
2455         struct member *new_member;
2456         int res = RES_NOSUCHQUEUE;
2457
2458         /* \note Ensure the appropriate realtime queue is loaded.  Note that this
2459          * short-circuits if the queue is already in memory. */
2460         q = load_realtime_queue(queuename);
2461
2462         AST_LIST_LOCK(&queues);
2463
2464         if (q) {
2465                 ast_mutex_lock(&q->lock);
2466                 if (interface_exists(q, interface) == NULL) {
2467                         new_member = create_queue_member(interface, penalty, paused);
2468
2469                         if (new_member != NULL) {
2470                                 new_member->dynamic = 1;
2471                                 new_member->next = q->members;
2472                                 q->members = new_member;
2473                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
2474                                         "Queue: %s\r\n"
2475                                         "Location: %s\r\n"
2476                                         "Membership: %s\r\n"
2477                                         "Penalty: %d\r\n"
2478                                         "CallsTaken: %d\r\n"
2479                                         "LastCall: %d\r\n"
2480                                         "Status: %d\r\n"
2481                                         "Paused: %d\r\n",
2482                                 q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
2483                                 new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused);
2484                                         
2485                                 if (dump)
2486                                         dump_queue_members(q);
2487
2488                                 res = RES_OKAY;
2489                         } else {
2490                                 res = RES_OUTOFMEMORY;
2491                         }
2492                 } else {
2493                         res = RES_EXISTS;
2494                 }
2495                 ast_mutex_unlock(&q->lock);
2496         }
2497         AST_LIST_UNLOCK(&queues);
2498         return res;
2499 }
2500
2501 static int set_member_paused(char *queuename, char *interface, int paused)
2502 {
2503         int found = 0;
2504         struct ast_call_queue *q;
2505         struct member *mem;
2506
2507         /* Special event for when all queues are paused - individual events still generated */
2508
2509         if (ast_strlen_zero(queuename))
2510                 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
2511
2512         AST_LIST_LOCK(&queues);
2513         AST_LIST_TRAVERSE(&queues, q, list) {
2514                 ast_mutex_lock(&q->lock);
2515                 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
2516                         if ((mem = interface_exists(q, interface))) {
2517                                 found++;
2518                                 if (mem->paused == paused)
2519                                         ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
2520                                 mem->paused = paused;
2521
2522                                 if (queue_persistent_members)
2523                                     dump_queue_members(q);
2524
2525                                 ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
2526
2527                                 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
2528                                         "Queue: %s\r\n"
2529                                         "Location: %s\r\n"
2530                                         "Paused: %d\r\n",
2531                                                 q->name, mem->interface, paused);
2532                         }
2533                 }
2534                 ast_mutex_unlock(&q->lock);
2535         }
2536         AST_LIST_UNLOCK(&queues);
2537
2538         if (found)
2539                 return RESULT_SUCCESS;
2540         else
2541                 return RESULT_FAILURE;
2542 }
2543
2544 /* Reload dynamic queue members persisted into the astdb */
2545 static void reload_queue_members(void)
2546 {
2547         char *cur_ptr;  
2548         char *queue_name;
2549         char *member;
2550         char *interface;
2551         char *penalty_tok;
2552         int penalty = 0;
2553         char *paused_tok;
2554         int paused = 0;
2555         struct ast_db_entry *db_tree;
2556         struct ast_db_entry *entry;
2557         struct ast_call_queue *cur_queue;
2558         char queue_data[PM_MAX_LEN];
2559
2560         AST_LIST_LOCK(&queues);
2561
2562         /* Each key in 'pm_family' is the name of a queue */
2563         db_tree = ast_db_gettree(pm_family, NULL);
2564         for (entry = db_tree; entry; entry = entry->next) {
2565
2566                 queue_name = entry->key + strlen(pm_family) + 2;
2567
2568                 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
2569                         ast_mutex_lock(&cur_queue->lock);
2570                         if (!strcmp(queue_name, cur_queue->name))
2571                                 break;
2572                         ast_mutex_unlock(&cur_queue->lock);
2573                 }
2574
2575                 if (!cur_queue) {
2576                         /* If the queue no longer exists, remove it from the
2577                          * database */
2578                         ast_db_del(pm_family, queue_name);
2579                         continue;
2580                 } else
2581                         ast_mutex_unlock(&cur_queue->lock);
2582
2583                 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
2584                         continue;
2585
2586                 cur_ptr = queue_data;
2587                 while ((member = strsep(&cur_ptr, "|"))) {
2588                         if (ast_strlen_zero(member))
2589                                 continue;
2590
2591                         interface = strsep(&member, ";");
2592                         penalty_tok = strsep(&member, ";");
2593                         paused_tok = strsep(&member, ";");
2594
2595                         if (!penalty_tok) {
2596                                 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
2597                                 break;
2598                         }
2599                         penalty = strtol(penalty_tok, NULL, 10);
2600                         if (errno == ERANGE) {
2601                                 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
2602                                 break;
2603                         }
2604                         
2605                         if (!paused_tok) {
2606                                 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
2607                                 break;
2608                         }
2609                         paused = strtol(paused_tok, NULL, 10);
2610                         if ((errno == ERANGE) || paused < 0 || paused > 1) {
2611                                 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
2612                                 break;
2613                         }
2614
2615                         if (option_debug)
2616                                 ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Penalty: %d  Paused: %d\n", queue_name, interface, penalty, paused);
2617                         
2618                         if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
2619                                 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
2620                                 break;
2621                         }
2622                 }
2623         }
2624
2625         AST_LIST_UNLOCK(&queues);
2626         if (db_tree) {
2627                 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
2628                 ast_db_freetree(db_tree);
2629         }
2630 }
2631
2632 static int pqm_exec(struct ast_channel *chan, void *data)
2633 {
2634         struct localuser *u;
2635         char *parse;
2636         int priority_jump = 0;
2637         AST_DECLARE_APP_ARGS(args,
2638                 AST_APP_ARG(queuename);
2639                 AST_APP_ARG(interface);
2640                 AST_APP_ARG(options);
2641         );
2642
2643         if (ast_strlen_zero(data)) {
2644                 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
2645                 return -1;
2646         }
2647
2648         if (!(parse = ast_strdupa(data)))
2649                 return -1;
2650
2651         AST_STANDARD_APP_ARGS(args, parse);
2652
2653         LOCAL_USER_ADD(u);
2654
2655         if (args.options) {
2656                 if (strchr(args.options, 'j'))
2657                         priority_jump = 1;
2658         }
2659
2660         if (ast_strlen_zero(args.interface)) {
2661                 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
2662                 LOCAL_USER_REMOVE(u);
2663                 return -1;
2664         }
2665
2666         if (set_member_paused(args.queuename, args.interface, 1)) {
2667                 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
2668                 if (priority_jump || ast_opt_priority_jumping) {
2669                         if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
2670                                 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
2671                                 LOCAL_USER_REMOVE(u);
2672                                 return 0;
2673                         }
2674                 }
2675                 LOCAL_USER_REMOVE(u);
2676                 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
2677                 return -1;
2678         }
2679
2680         LOCAL_USER_REMOVE(u);
2681         pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
2682         return 0;
2683 }
2684
2685 static int upqm_exec(struct ast_channel *chan, void *data)
2686 {
2687         struct localuser *u;
2688         char *parse;
2689         int priority_jump = 0;
2690         AST_DECLARE_APP_ARGS(args,
2691                 AST_APP_ARG(queuename);
2692                 AST_APP_ARG(interface);
2693                 AST_APP_ARG(options);
2694         );
2695
2696         if (ast_strlen_zero(data)) {
2697                 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
2698                 return -1;
2699         }
2700
2701         if (!(parse = ast_strdupa(data))) 
2702                 return -1;      
2703
2704         AST_STANDARD_APP_ARGS(args, parse);
2705
2706         LOCAL_USER_ADD(u);
2707
2708         if (args.options) {
2709                 if (strchr(args.options, 'j'))
2710                         priority_jump = 1;
2711         }
2712
2713         if (ast_strlen_zero(args.interface)) {
2714                 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
2715                 LOCAL_USER_REMOVE(u);
2716                 return -1;
2717         }
2718
2719         if (set_member_paused(args.queuename, args.interface, 0)) {
2720                 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
2721                 if (priority_jump || ast_opt_priority_jumping) {
2722                         if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
2723                                 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
2724                                 LOCAL_USER_REMOVE(u);
2725                                 return 0;
2726                         }
2727                 }
2728                 LOCAL_USER_REMOVE(u);
2729                 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
2730                 return -1;
2731         }
2732
2733         LOCAL_USER_REMOVE(u);
2734         pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
2735         return 0;
2736 }
2737
2738 static int rqm_exec(struct ast_channel *chan, void *data)
2739 {
2740         int res=-1;
2741         struct localuser *u;
2742         char *parse, *temppos = NULL;
2743         int priority_jump = 0;
2744         AST_DECLARE_APP_ARGS(args,
2745                 AST_APP_ARG(queuename);
2746                 AST_APP_ARG(interface);
2747                 AST_APP_ARG(options);
2748         );
2749
2750
2751         if (ast_strlen_zero(data)) {
2752                 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
2753                 return -1;
2754         }
2755
2756         if (!(parse = ast_strdupa(data)))
2757                 return -1;
2758
2759         AST_STANDARD_APP_ARGS(args, parse);
2760
2761         LOCAL_USER_ADD(u);
2762
2763         if (ast_strlen_zero(args.interface)) {
2764                 args.interface = ast_strdupa(chan->name);
2765                 temppos = strrchr(args.interface, '-');
2766                 if (temppos)
2767                         *temppos = '\0';
2768         }
2769
2770         if (args.options) {
2771                 if (strchr(args.options, 'j'))
2772                         priority_jump = 1;
2773         }
2774
2775         switch (remove_from_queue(args.queuename, args.interface)) {
2776         case RES_OKAY:
2777                 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
2778                 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
2779                 res = 0;
2780                 break;
2781         case RES_EXISTS:
2782                 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
2783                 if (priority_jump || ast_opt_priority_jumping) 
2784                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2785                 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
2786                 res = 0;
2787                 break;
2788         case RES_NOSUCHQUEUE:
2789                 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
2790                 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
2791                 res = 0;
2792                 break;
2793         case RES_OUTOFMEMORY:
2794                 ast_log(LOG_ERROR, "Out of memory\n");
2795                 break;
2796         }
2797
2798         LOCAL_USER_REMOVE(u);
2799         return res;
2800 }
2801
2802 static int aqm_exec(struct ast_channel *chan, void *data)
2803 {
2804         int res=-1;
2805         struct localuser *u;
2806         char *parse, *temppos = NULL;
2807         int priority_jump = 0;
2808         AST_DECLARE_APP_ARGS(args,
2809                 AST_APP_ARG(queuename);
2810                 AST_APP_ARG(interface);
2811                 AST_APP_ARG(penalty);
2812                 AST_APP_ARG(options);
2813         );
2814         int penalty = 0;
2815
2816         if (ast_strlen_zero(data)) {
2817                 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n");
2818                 return -1;
2819         }
2820
2821         if (!(parse = ast_strdupa(data)))
2822                 return -1;
2823
2824         AST_STANDARD_APP_ARGS(args, parse);
2825
2826         LOCAL_USER_ADD(u);
2827
2828         if (ast_strlen_zero(args.interface)) {
2829                 args.interface = ast_strdupa(chan->name);
2830                 temppos = strrchr(args.interface, '-');
2831                 if (temppos)
2832                         *temppos = '\0';
2833         }
2834
2835         if (!ast_strlen_zero(args.penalty)) {
2836                 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
2837                         ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
2838                         penalty = 0;
2839                 }
2840         }
2841         
2842         if (args.options) {
2843                 if (strchr(args.options, 'j'))
2844                         priority_jump = 1;
2845         }
2846
2847
2848         switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) {
2849         case RES_OKAY:
2850                 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
2851                 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
2852                 res = 0;
2853                 break;
2854         case RES_EXISTS:
2855                 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
2856                 if (priority_jump || ast_opt_priority_jumping) 
2857                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2858                 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
2859                 res = 0;
2860                 break;
2861         case RES_NOSUCHQUEUE:
2862                 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
2863                 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
2864                 res = 0;
2865                 break;
2866         case RES_OUTOFMEMORY:
2867                 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
2868                 break;
2869         }
2870
2871         LOCAL_USER_REMOVE(u);
2872         return res;
2873 }
2874
2875 static int queue_exec(struct ast_channel *chan, void *data)
2876 {
2877         int res=-1;
2878         int ringing=0;
2879         struct localuser *u;
2880         const char *user_priority;
2881         const char *max_penalty_str;
2882         int prio;
2883         int max_penalty;
2884         enum queue_result reason = QUEUE_UNKNOWN;
2885
2886         /* whether to exit Queue application after the timeout hits */
2887         int go_on = 0;
2888
2889         char *parse;
2890         AST_DECLARE_APP_ARGS(args,
2891                  AST_APP_ARG(queuename);
2892                  AST_APP_ARG(options);
2893                  AST_APP_ARG(url);
2894                  AST_APP_ARG(announceoverride);
2895                  AST_APP_ARG(queuetimeoutstr);
2896         );
2897         
2898         /* Our queue entry */
2899         struct queue_ent qe;
2900         
2901         if (ast_strlen_zero(data)) {
2902                 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
2903                 return -1;
2904         }
2905         
2906         parse = ast_strdupa(data);
2907         if (!parse) {
2908                 ast_log(LOG_ERROR, "Out of memory!\n");
2909                 return -1;
2910         }
2911         AST_STANDARD_APP_ARGS(args, parse);
2912
2913         LOCAL_USER_ADD(u);
2914
2915         /* Setup our queue entry */
2916         memset(&qe, 0, sizeof(qe));
2917         qe.start = time(NULL);
2918
2919         /* set the expire time based on the supplied timeout; */
2920         if (args.queuetimeoutstr)
2921                &nb