Wed Aug 15 01:24:14 2007

Asterisk developer's documentation


app_queue.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief True call queues with optional send URL on answer
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \arg Config in \ref Config_qu queues.conf
00026  *
00027  * \par Development notes
00028  * \note 2004-11-25: Persistent Dynamic Members added by:
00029  *             NetNation Communications (www.netnation.com)
00030  *             Kevin Lindsay <kevinl@netnation.com>
00031  *
00032  *             Each dynamic agent in each queue is now stored in the astdb.
00033  *             When asterisk is restarted, each agent will be automatically
00034  *             readded into their recorded queues. This feature can be
00035  *             configured with the 'persistent_members=<1|0>' setting in the
00036  *             '[general]' category in queues.conf. The default is on.
00037  *
00038  * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
00039  *
00040  * \note These features added by David C. Troy <dave@toad.net>:
00041  *    - Per-queue holdtime calculation
00042  *    - Estimated holdtime announcement
00043  *    - Position announcement
00044  *    - Abandoned/completed call counters
00045  *    - Failout timer passed as optional app parameter
00046  *    - Optional monitoring of calls, started when call is answered
00047  *
00048  * Patch Version 1.07 2003-12-24 01
00049  *
00050  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
00051  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
00052  *
00053  * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
00054  * by Matthew Enger <m.enger@xi.com.au>
00055  *
00056  * \ingroup applications
00057  */
00058 
00059 #include "asterisk.h"
00060 
00061 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 77854 $")
00062 
00063 #include <stdlib.h>
00064 #include <errno.h>
00065 #include <unistd.h>
00066 #include <string.h>
00067 #include <stdlib.h>
00068 #include <stdio.h>
00069 #include <sys/time.h>
00070 #include <sys/signal.h>
00071 #include <netinet/in.h>
00072 
00073 #include "asterisk/lock.h"
00074 #include "asterisk/file.h"
00075 #include "asterisk/logger.h"
00076 #include "asterisk/channel.h"
00077 #include "asterisk/pbx.h"
00078 #include "asterisk/options.h"
00079 #include "asterisk/app.h"
00080 #include "asterisk/linkedlists.h"
00081 #include "asterisk/module.h"
00082 #include "asterisk/translate.h"
00083 #include "asterisk/say.h"
00084 #include "asterisk/features.h"
00085 #include "asterisk/musiconhold.h"
00086 #include "asterisk/cli.h"
00087 #include "asterisk/manager.h"
00088 #include "asterisk/config.h"
00089 #include "asterisk/monitor.h"
00090 #include "asterisk/utils.h"
00091 #include "asterisk/causes.h"
00092 #include "asterisk/astdb.h"
00093 #include "asterisk/devicestate.h"
00094 #include "asterisk/stringfields.h"
00095 
00096 enum {
00097    QUEUE_STRATEGY_RINGALL = 0,
00098    QUEUE_STRATEGY_ROUNDROBIN,
00099    QUEUE_STRATEGY_LEASTRECENT,
00100    QUEUE_STRATEGY_FEWESTCALLS,
00101    QUEUE_STRATEGY_RANDOM,
00102    QUEUE_STRATEGY_RRMEMORY
00103 };
00104 
00105 static struct strategy {
00106    int strategy;
00107    char *name;
00108 } strategies[] = {
00109    { QUEUE_STRATEGY_RINGALL, "ringall" },
00110    { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00111    { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00112    { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00113    { QUEUE_STRATEGY_RANDOM, "random" },
00114    { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00115 };
00116 
00117 #define DEFAULT_RETRY      5
00118 #define DEFAULT_TIMEOUT    15
00119 #define RECHECK         1     /* Recheck every second to see we we're at the top yet */
00120 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
00121 
00122 #define  RES_OKAY 0     /* Action completed */
00123 #define  RES_EXISTS  (-1)     /* Entry already exists */
00124 #define  RES_OUTOFMEMORY   (-2)     /* Out of memory */
00125 #define  RES_NOSUCHQUEUE   (-3)     /* No such queue */
00126 
00127 static char *app = "Queue";
00128 
00129 static char *synopsis = "Queue a call for a call queue";
00130 
00131 static char *descrip =
00132 "  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
00133 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00134 "This application will return to the dialplan if the queue does not exist, or\n"
00135 "any of the join options cause the caller to not enter the queue.\n"
00136 "The option string may contain zero or more of the following characters:\n"
00137 "      'd' -- data-quality (modem) call (minimum delay).\n"
00138 "      'h' -- allow callee to hang up by hitting *.\n"
00139 "      'H' -- allow caller to hang up by hitting *.\n"
00140 "      'n' -- no retries on the timeout; will exit this application and \n"
00141 "             go to the next step.\n"
00142 "      'i' -- ignore call forward requests from queue members and do nothing\n"
00143 "             when they are requested.\n"
00144 "      'r' -- ring instead of playing MOH\n"
00145 "      't' -- allow the called user transfer the calling user\n"
00146 "      'T' -- to allow the calling user to transfer the call.\n"
00147 "      'w' -- allow the called user to write the conversation to disk via Monitor\n"
00148 "      'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00149 "  In addition to transferring the call, a call may be parked and then picked\n"
00150 "up by another user.\n"
00151 "  The optional URL will be sent to the called party if the channel supports\n"
00152 "it.\n"
00153 "  The optional AGI parameter will setup an AGI script to be executed on the \n"
00154 "calling party's channel once they are connected to a queue member.\n"
00155 "  The timeout will cause the queue to fail out after a specified number of\n"
00156 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00157 "  This application sets the following channel variable upon completion:\n"
00158 "      QUEUESTATUS    The status of the call as a text string, one of\n"
00159 "             TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00160 
00161 static char *app_aqm = "AddQueueMember" ;
00162 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00163 static char *app_aqm_descrip =
00164 "   AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
00165 "Dynamically adds interface to an existing queue.\n"
00166 "If the interface is already in the queue and there exists an n+101 priority\n"
00167 "then it will then jump to this priority.  Otherwise it will return an error\n"
00168 "The option string may contain zero or more of the following characters:\n"
00169 "       'j' -- jump to +101 priority when appropriate.\n"
00170 "  This application sets the following channel variable upon completion:\n"
00171 "     AQMSTATUS    The status of the attempt to add a queue member as a \n"
00172 "                     text string, one of\n"
00173 "           ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00174 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00175 "";
00176 
00177 static char *app_rqm = "RemoveQueueMember" ;
00178 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00179 static char *app_rqm_descrip =
00180 "   RemoveQueueMember(queuename[|interface[|options]]):\n"
00181 "Dynamically removes interface to an existing queue\n"
00182 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00183 "then it will then jump to this priority.  Otherwise it will return an error\n"
00184 "The option string may contain zero or more of the following characters:\n"
00185 "       'j' -- jump to +101 priority when appropriate.\n"
00186 "  This application sets the following channel variable upon completion:\n"
00187 "     RQMSTATUS      The status of the attempt to remove a queue member as a\n"
00188 "                     text string, one of\n"
00189 "           REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00190 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00191 "";
00192 
00193 static char *app_pqm = "PauseQueueMember" ;
00194 static char *app_pqm_synopsis = "Pauses a queue member" ;
00195 static char *app_pqm_descrip =
00196 "   PauseQueueMember([queuename]|interface[|options]):\n"
00197 "Pauses (blocks calls for) a queue member.\n"
00198 "The given interface will be paused in the given queue.  This prevents\n"
00199 "any calls from being sent from the queue to the interface until it is\n"
00200 "unpaused with UnpauseQueueMember or the manager interface.  If no\n"
00201 "queuename is given, the interface is paused in every queue it is a\n"
00202 "member of.  If the interface is not in the named queue, or if no queue\n"
00203 "is given and the interface is not in any queue, it will jump to\n"
00204 "priority n+101, if it exists and the appropriate options are set.\n"
00205 "The application will fail if the interface is not found and no extension\n"
00206 "to jump to exists.\n"
00207 "The option string may contain zero or more of the following characters:\n"
00208 "       'j' -- jump to +101 priority when appropriate.\n"
00209 "  This application sets the following channel variable upon completion:\n"
00210 "     PQMSTATUS      The status of the attempt to pause a queue member as a\n"
00211 "                     text string, one of\n"
00212 "           PAUSED | NOTFOUND\n"
00213 "Example: PauseQueueMember(|SIP/3000)\n";
00214 
00215 static char *app_upqm = "UnpauseQueueMember" ;
00216 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00217 static char *app_upqm_descrip =
00218 "   UnpauseQueueMember([queuename]|interface[|options]):\n"
00219 "Unpauses (resumes calls to) a queue member.\n"
00220 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00221 "same way, except it unpauses instead of pausing the given interface.\n"
00222 "The option string may contain zero or more of the following characters:\n"
00223 "       'j' -- jump to +101 priority when appropriate.\n"
00224 "  This application sets the following channel variable upon completion:\n"
00225 "     UPQMSTATUS       The status of the attempt to unpause a queue \n"
00226 "                      member as a text string, one of\n"
00227 "            UNPAUSED | NOTFOUND\n"
00228 "Example: UnpauseQueueMember(|SIP/3000)\n";
00229 
00230 static char *app_ql = "QueueLog" ;
00231 static char *app_ql_synopsis = "Writes to the queue_log" ;
00232 static char *app_ql_descrip =
00233 "   QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
00234 "Allows you to write your own events into the queue log\n"
00235 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
00236 
00237 /*! \brief Persistent Members astdb family */
00238 static const char *pm_family = "Queue/PersistentMembers";
00239 /* The maximum length of each persistent member queue database entry */
00240 #define PM_MAX_LEN 8192
00241 
00242 /*! \brief queues.conf [general] option */
00243 static int queue_persistent_members = 0;
00244 
00245 /*! \brief queues.conf per-queue weight option */
00246 static int use_weight = 0;
00247 
00248 /*! \brief queues.conf [general] option */
00249 static int autofill_default = 0;
00250 
00251 /*! \brief queues.conf [general] option */
00252 static int montype_default = 0;
00253 
00254 enum queue_result {
00255    QUEUE_UNKNOWN = 0,
00256    QUEUE_TIMEOUT = 1,
00257    QUEUE_JOINEMPTY = 2,
00258    QUEUE_LEAVEEMPTY = 3,
00259    QUEUE_JOINUNAVAIL = 4,
00260    QUEUE_LEAVEUNAVAIL = 5,
00261    QUEUE_FULL = 6,
00262 };
00263 
00264 const struct {
00265    enum queue_result id;
00266    char *text;
00267 } queue_results[] = {
00268    { QUEUE_UNKNOWN, "UNKNOWN" },
00269    { QUEUE_TIMEOUT, "TIMEOUT" },
00270    { QUEUE_JOINEMPTY,"JOINEMPTY" },
00271    { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00272    { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00273    { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00274    { QUEUE_FULL, "FULL" },
00275 };
00276 
00277 /*! \brief We define a custom "local user" structure because we
00278    use it not only for keeping track of what is in use but
00279    also for keeping track of who we're dialing. */
00280 
00281 struct callattempt {
00282    struct callattempt *q_next;
00283    struct ast_channel *chan;
00284    char interface[256];
00285    int stillgoing;
00286    int metric;
00287    int oldstatus;
00288    time_t lastcall;
00289    struct member *member;
00290 };
00291 
00292 
00293 struct queue_ent {
00294    struct call_queue *parent;          /*!< What queue is our parent */
00295    char moh[80];                       /*!< Name of musiconhold to be used */
00296    char announce[80];                  /*!< Announcement to play for member when call is answered */
00297    char context[AST_MAX_CONTEXT];      /*!< Context when user exits queue */
00298    char digits[AST_MAX_EXTENSION];     /*!< Digits entered while in queue */
00299    int valid_digits;        /*!< Digits entered correspond to valid extension. Exited */
00300    int pos;                            /*!< Where we are in the queue */
00301    int prio;                           /*!< Our priority */
00302    int last_pos_said;                  /*!< Last position we told the user */
00303    time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
00304    int last_periodic_announce_sound;   /*!< The last periodic announcement we made */
00305    time_t last_pos;                    /*!< Last time we told the user their position */
00306    int opos;                           /*!< Where we started in the queue */
00307    int handled;                        /*!< Whether our call was handled */
00308    int max_penalty;                    /*!< Limit the members that can take this call to this penalty or lower */
00309    time_t start;                       /*!< When we started holding */
00310    time_t expire;                      /*!< When this entry should expire (time out of queue) */
00311    struct ast_channel *chan;           /*!< Our channel */
00312    struct queue_ent *next;             /*!< The next queue entry */
00313 };
00314 
00315 struct member {
00316    char interface[80];                 /*!< Technology/Location */
00317    char membername[80];                /*!< Member name to use in queue logs */
00318    int penalty;                        /*!< Are we a last resort? */
00319    int calls;                          /*!< Number of calls serviced by this member */
00320    int dynamic;                        /*!< Are we dynamically added? */
00321    int status;                         /*!< Status of queue member */
00322    int paused;                         /*!< Are we paused (not accepting calls)? */
00323    time_t lastcall;                    /*!< When last successful call was hungup */
00324    unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
00325    unsigned int delme:1;               /*!< Flag to delete entry on reload */
00326    struct member *next;                /*!< Next member */
00327 };
00328 
00329 struct member_interface {
00330    char interface[80];
00331    AST_LIST_ENTRY(member_interface) list;    /*!< Next call queue */
00332 };
00333 
00334 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00335 
00336 /* values used in multi-bit flags in call_queue */
00337 #define QUEUE_EMPTY_NORMAL 1
00338 #define QUEUE_EMPTY_STRICT 2
00339 #define ANNOUNCEHOLDTIME_ALWAYS 1
00340 #define ANNOUNCEHOLDTIME_ONCE 2
00341 #define QUEUE_EVENT_VARIABLES 3
00342 
00343 struct call_queue {
00344    ast_mutex_t lock; 
00345    char name[80];                      /*!< Name */
00346    char moh[80];                       /*!< Music On Hold class to be used */
00347    char announce[80];                  /*!< Announcement to play when call is answered */
00348    char context[AST_MAX_CONTEXT];      /*!< Exit context */
00349    unsigned int monjoin:1;
00350    unsigned int dead:1;
00351    unsigned int joinempty:2;
00352    unsigned int eventwhencalled:2;
00353    unsigned int leavewhenempty:2;
00354    unsigned int ringinuse:1;
00355    unsigned int setinterfacevar:1;
00356    unsigned int reportholdtime:1;
00357    unsigned int wrapped:1;
00358    unsigned int timeoutrestart:1;
00359    unsigned int announceholdtime:2;
00360    unsigned int strategy:3;
00361    unsigned int maskmemberstatus:1;
00362    unsigned int realtime:1;
00363    int announcefrequency;              /*!< How often to announce their position */
00364    int periodicannouncefrequency;      /*!< How often to play periodic announcement */
00365    int roundingseconds;                /*!< How many seconds do we round to? */
00366    int holdtime;                       /*!< Current avg holdtime, based on recursive boxcar filter */
00367    int callscompleted;                 /*!< Number of queue calls completed */
00368    int callsabandoned;                 /*!< Number of queue calls abandoned */
00369    int servicelevel;                   /*!< seconds setting for servicelevel*/
00370    int callscompletedinsl;             /*!< Number of calls answered with servicelevel*/
00371    char monfmt[8];                     /*!< Format to use when recording calls */
00372    int montype;                        /*!< Monitor type  Monitor vs. MixMonitor */
00373    char sound_next[80];                /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
00374    char sound_thereare[80];            /*!< Sound file: "There are currently" (def. queue-thereare) */
00375    char sound_calls[80];               /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
00376    char sound_holdtime[80];            /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
00377    char sound_minutes[80];             /*!< Sound file: "minutes." (def. queue-minutes) */
00378    char sound_lessthan[80];            /*!< Sound file: "less-than" (def. queue-lessthan) */
00379    char sound_seconds[80];             /*!< Sound file: "seconds." (def. queue-seconds) */
00380    char sound_thanks[80];              /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
00381    char sound_reporthold[80];          /*!< Sound file: "Hold time" (def. queue-reporthold) */
00382    char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
00383 
00384    int count;                          /*!< How many entries */
00385    int maxlen;                         /*!< Max number of entries */
00386    int wrapuptime;                     /*!< Wrapup Time */
00387 
00388    int retry;                          /*!< Retry calling everyone after this amount of time */
00389    int timeout;                        /*!< How long to wait for an answer */
00390    int weight;                         /*!< Respective weight */
00391    int autopause;                      /*!< Auto pause queue members if they fail to answer */
00392 
00393    /* Queue strategy things */
00394    int rrpos;                          /*!< Round Robin - position */
00395    int memberdelay;                    /*!< Seconds to delay connecting member to caller */
00396    int autofill;                       /*!< Ignore the head call status and ring an available agent */
00397    
00398    struct member *members;             /*!< Head of the list of members */
00399    int membercount;              /*!< Number of members in queue */
00400    struct queue_ent *head;             /*!< Head of the list of callers */
00401    AST_LIST_ENTRY(call_queue) list;    /*!< Next call queue */
00402 };
00403 
00404 static AST_LIST_HEAD_STATIC(queues, call_queue);
00405 
00406 static int set_member_paused(const char *queuename, const char *interface, int paused);
00407 
00408 static void rr_dep_warning(void)
00409 {
00410    static unsigned int warned = 0;
00411 
00412    if (!warned) {
00413       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00414       warned = 1;
00415    }
00416 }
00417 
00418 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00419 {
00420    int i;
00421 
00422    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00423       if (queue_results[i].id == res) {
00424          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00425          return;
00426       }
00427    }
00428 }
00429 
00430 static char *int2strat(int strategy)
00431 {
00432    int x;
00433 
00434    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00435       if (strategy == strategies[x].strategy)
00436          return strategies[x].name;
00437    }
00438 
00439    return "<unknown>";
00440 }
00441 
00442 static int strat2int(const char *strategy)
00443 {
00444    int x;
00445 
00446    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00447       if (!strcasecmp(strategy, strategies[x].name))
00448          return strategies[x].strategy;
00449    }
00450 
00451    return -1;
00452 }
00453 
00454 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
00455 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00456 {
00457    struct queue_ent *cur;
00458 
00459    if (!q || !new)
00460       return;
00461    if (prev) {
00462       cur = prev->next;
00463       prev->next = new;
00464    } else {
00465       cur = q->head;
00466       q->head = new;
00467    }
00468    new->next = cur;
00469    new->parent = q;
00470    new->pos = ++(*pos);
00471    new->opos = *pos;
00472 }
00473 
00474 enum queue_member_status {
00475    QUEUE_NO_MEMBERS,
00476    QUEUE_NO_REACHABLE_MEMBERS,
00477    QUEUE_NORMAL
00478 };
00479 
00480 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
00481 {
00482    struct member *member;
00483    enum queue_member_status result = QUEUE_NO_MEMBERS;
00484 
00485    ast_mutex_lock(&q->lock);
00486    for (member = q->members; member; member = member->next) {
00487       if (max_penalty && (member->penalty > max_penalty))
00488          continue;
00489 
00490       if (member->paused) continue;
00491 
00492       switch (member->status) {
00493       case AST_DEVICE_INVALID:
00494          /* nothing to do */
00495          break;
00496       case AST_DEVICE_UNAVAILABLE:
00497          result = QUEUE_NO_REACHABLE_MEMBERS;
00498          break;
00499       default:
00500          ast_mutex_unlock(&q->lock);
00501          return QUEUE_NORMAL;
00502       }
00503    }
00504    
00505    ast_mutex_unlock(&q->lock);
00506    return result;
00507 }
00508 
00509 struct statechange {
00510    int state;
00511    char dev[0];
00512 };
00513 
00514 static void *changethread(void *data)
00515 {
00516    struct call_queue *q;
00517    struct statechange *sc = data;
00518    struct member *cur;
00519    struct member_interface *curint;
00520    char *loc;
00521    char *technology;
00522 
00523    technology = ast_strdupa(sc->dev);
00524    loc = strchr(technology, '/');
00525    if (loc) {
00526       *loc++ = '\0';
00527    } else {
00528       free(sc);
00529       return NULL;
00530    }
00531 
00532    AST_LIST_LOCK(&interfaces);
00533    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00534       char *interface;
00535       char *slash_pos;
00536       interface = ast_strdupa(curint->interface);
00537       if ((slash_pos = strchr(interface, '/')))
00538          if ((slash_pos = strchr(slash_pos + 1, '/')))
00539             *slash_pos = '\0';
00540 
00541       if (!strcasecmp(interface, sc->dev))
00542          break;
00543    }
00544    AST_LIST_UNLOCK(&interfaces);
00545 
00546    if (!curint) {
00547       if (option_debug > 2)
00548          ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
00549       free(sc);
00550       return NULL;
00551    }
00552 
00553    if (option_debug)
00554       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00555    AST_LIST_LOCK(&queues);
00556    AST_LIST_TRAVERSE(&queues, q, list) {
00557       ast_mutex_lock(&q->lock);
00558       for (cur = q->members; cur; cur = cur->next) {
00559          char *interface;
00560          char *slash_pos;
00561          interface = ast_strdupa(cur->interface);
00562          if ((slash_pos = strchr(interface, '/')))
00563             if ((slash_pos = strchr(slash_pos + 1, '/')))
00564                *slash_pos = '\0';
00565 
00566          if (strcasecmp(sc->dev, interface))
00567             continue;
00568 
00569          if (cur->status != sc->state) {
00570             cur->status = sc->state;
00571             if (q->maskmemberstatus)
00572                continue;
00573 
00574             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00575                "Queue: %s\r\n"
00576                "Location: %s\r\n"
00577                "MemberName: %s\r\n"
00578                "Membership: %s\r\n"
00579                "Penalty: %d\r\n"
00580                "CallsTaken: %d\r\n"
00581                "LastCall: %d\r\n"
00582                "Status: %d\r\n"
00583                "Paused: %d\r\n",
00584                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
00585                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00586          }
00587       }
00588       ast_mutex_unlock(&q->lock);
00589    }
00590    AST_LIST_UNLOCK(&queues);
00591 
00592    free(sc);
00593 
00594    return NULL;
00595 }
00596 
00597 static int statechange_queue(const char *dev, int state, void *ign)
00598 {
00599    /* Avoid potential for deadlocks by spawning a new thread to handle
00600       the event */
00601    struct statechange *sc;
00602    pthread_t t;
00603    pthread_attr_t attr;
00604 
00605    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00606       return 0;
00607 
00608    sc->state = state;
00609    strcpy(sc->dev, dev);
00610    pthread_attr_init(&attr);
00611    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00612    if (ast_pthread_create_background(&t, &attr, changethread, sc)) {
00613       ast_log(LOG_WARNING, "Failed to create update thread!\n");
00614       free(sc);
00615    }
00616    pthread_attr_destroy(&attr);
00617 
00618    return 0;
00619 }
00620 
00621 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
00622 {
00623    struct member *cur;
00624    
00625    if ((cur = ast_calloc(1, sizeof(*cur)))) {
00626       cur->penalty = penalty;
00627       cur->paused = paused;
00628       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00629       ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00630       if (!strchr(cur->interface, '/'))
00631          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00632       cur->status = ast_device_state(interface);
00633    }
00634 
00635    return cur;
00636 }
00637 
00638 static struct call_queue *alloc_queue(const char *queuename)
00639 {
00640    struct call_queue *q;
00641 
00642    if ((q = ast_calloc(1, sizeof(*q)))) {
00643       ast_mutex_init(&q->lock);
00644       ast_copy_string(q->name, queuename, sizeof(q->name));
00645    }
00646    return q;
00647 }
00648 
00649 static void init_queue(struct call_queue *q)
00650 {
00651    int i;
00652 
00653    q->dead = 0;
00654    q->retry = DEFAULT_RETRY;
00655    q->timeout = -1;
00656    q->maxlen = 0;
00657    q->announcefrequency = 0;
00658    q->announceholdtime = 0;
00659    q->roundingseconds = 0; /* Default - don't announce seconds */
00660    q->servicelevel = 0;
00661    q->ringinuse = 1;
00662    q->setinterfacevar = 0;
00663    q->autofill = autofill_default;
00664    q->montype = montype_default;
00665    q->moh[0] = '\0';
00666    q->announce[0] = '\0';
00667    q->context[0] = '\0';
00668    q->monfmt[0] = '\0';
00669    q->periodicannouncefrequency = 0;
00670    q->membercount = 0;
00671    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00672    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00673    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00674    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00675    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00676    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00677    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00678    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00679    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00680    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00681    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00682       q->sound_periodicannounce[i][0]='\0';
00683    }
00684 }
00685 
00686 static void clear_queue(struct call_queue *q)
00687 {
00688    q->holdtime = 0;
00689    q->callscompleted = 0;
00690    q->callsabandoned = 0;
00691    q->callscompletedinsl = 0;
00692    q->wrapuptime = 0;
00693 }
00694 
00695 static int add_to_interfaces(const char *interface)
00696 {
00697    struct member_interface *curint;
00698 
00699    AST_LIST_LOCK(&interfaces);
00700    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00701       if (!strcasecmp(curint->interface, interface))
00702          break;
00703    }
00704 
00705    if (curint) {
00706       AST_LIST_UNLOCK(&interfaces);
00707       return 0;
00708    }
00709 
00710    if (option_debug)
00711       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00712    
00713    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00714       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00715       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00716    }
00717    AST_LIST_UNLOCK(&interfaces);
00718 
00719    return 0;
00720 }
00721 
00722 static int interface_exists_global(const char *interface)
00723 {
00724    struct call_queue *q;
00725    struct member *mem;
00726    int ret = 0;
00727 
00728    AST_LIST_LOCK(&queues);
00729    AST_LIST_TRAVERSE(&queues, q, list) {
00730       ast_mutex_lock(&q->lock);
00731       for (mem = q->members; mem && !ret; mem = mem->next) {
00732          if (!strcasecmp(interface, mem->interface))
00733             ret = 1;
00734       }
00735       ast_mutex_unlock(&q->lock);
00736       if (ret)
00737          break;
00738    }
00739    AST_LIST_UNLOCK(&queues);
00740 
00741    return ret;
00742 }
00743 
00744 static int remove_from_interfaces(const char *interface)
00745 {
00746    struct member_interface *curint;
00747 
00748    AST_LIST_LOCK(&interfaces);
00749    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00750       if (!strcasecmp(curint->interface, interface)) {
00751          if (!interface_exists_global(interface)) {
00752             if (option_debug)
00753                ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00754             AST_LIST_REMOVE_CURRENT(&interfaces, list);
00755             free(curint);
00756          }
00757          break;
00758       }
00759    }
00760    AST_LIST_TRAVERSE_SAFE_END;
00761    AST_LIST_UNLOCK(&interfaces);
00762 
00763    return 0;
00764 }
00765 
00766 static void clear_and_free_interfaces(void)
00767 {
00768    struct member_interface *curint;
00769 
00770    AST_LIST_LOCK(&interfaces);
00771    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00772       free(curint);
00773    AST_LIST_UNLOCK(&interfaces);
00774 }
00775 
00776 /*! \brief Configure a queue parameter.
00777 \par
00778    For error reporting, line number is passed for .conf static configuration.
00779    For Realtime queues, linenum is -1.
00780    The failunknown flag is set for config files (and static realtime) to show
00781    errors for unknown parameters. It is cleared for dynamic realtime to allow
00782    extra fields in the tables. */
00783 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
00784 {
00785    if (!strcasecmp(param, "musicclass") || 
00786       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00787       ast_copy_string(q->moh, val, sizeof(q->moh));
00788    } else if (!strcasecmp(param, "announce")) {
00789       ast_copy_string(q->announce, val, sizeof(q->announce));
00790    } else if (!strcasecmp(param, "context")) {
00791       ast_copy_string(q->context, val, sizeof(q->context));
00792    } else if (!strcasecmp(param, "timeout")) {
00793       q->timeout = atoi(val);
00794       if (q->timeout < 0)
00795          q->timeout = DEFAULT_TIMEOUT;
00796    } else if (!strcasecmp(param, "ringinuse")) {
00797       q->ringinuse = ast_true(val);
00798    } else if (!strcasecmp(param, "setinterfacevar")) {
00799       q->setinterfacevar = ast_true(val);
00800    } else if (!strcasecmp(param, "monitor-join")) {
00801       q->monjoin = ast_true(val);
00802    } else if (!strcasecmp(param, "monitor-format")) {
00803       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00804    } else if (!strcasecmp(param, "queue-youarenext")) {
00805       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00806    } else if (!strcasecmp(param, "queue-thereare")) {
00807       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00808    } else if (!strcasecmp(param, "queue-callswaiting")) {
00809       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00810    } else if (!strcasecmp(param, "queue-holdtime")) {
00811       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00812    } else if (!strcasecmp(param, "queue-minutes")) {
00813       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00814    } else if (!strcasecmp(param, "queue-seconds")) {
00815       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00816    } else if (!strcasecmp(param, "queue-lessthan")) {
00817       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00818    } else if (!strcasecmp(param, "queue-thankyou")) {
00819       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00820    } else if (!strcasecmp(param, "queue-reporthold")) {
00821       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00822    } else if (!strcasecmp(param, "announce-frequency")) {
00823       q->announcefrequency = atoi(val);
00824    } else if (!strcasecmp(param, "announce-round-seconds")) {
00825       q->roundingseconds = atoi(val);
00826       if (q->roundingseconds>60 || q->roundingseconds<0) {
00827          if (linenum >= 0) {
00828             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00829                "using 0 instead for queue '%s' at line %d of queues.conf\n",
00830                val, param, q->name, linenum);
00831          } else {
00832             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00833                "using 0 instead for queue '%s'\n", val, param, q->name);
00834          }
00835          q->roundingseconds=0;
00836       }
00837    } else if (!strcasecmp(param, "announce-holdtime")) {
00838       if (!strcasecmp(val, "once"))
00839          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00840       else if (ast_true(val))
00841          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00842       else
00843          q->announceholdtime = 0;
00844    } else if (!strcasecmp(param, "periodic-announce")) {
00845       if (strchr(val, '|')) {
00846          char *s, *buf = ast_strdupa(val);
00847          unsigned int i = 0;
00848 
00849          while ((s = strsep(&buf, "|"))) {
00850             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
00851             i++;
00852             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
00853                break;
00854          }
00855       } else {
00856          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
00857       }
00858    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00859       q->periodicannouncefrequency = atoi(val);
00860    } else if (!strcasecmp(param, "retry")) {
00861       q->retry = atoi(val);
00862       if (q->retry <= 0)
00863          q->retry = DEFAULT_RETRY;
00864    } else if (!strcasecmp(param, "wrapuptime")) {
00865       q->wrapuptime = atoi(val);
00866    } else if (!strcasecmp(param, "autofill")) {
00867       q->autofill = ast_true(val);
00868    } else if (!strcasecmp(param, "monitor-type")) {
00869       if (!strcasecmp(val, "mixmonitor"))
00870          q->montype = 1;
00871    } else if (!strcasecmp(param, "autopause")) {
00872       q->autopause = ast_true(val);
00873    } else if (!strcasecmp(param, "maxlen")) {
00874       q->maxlen = atoi(val);
00875       if (q->maxlen < 0)
00876          q->maxlen = 0;
00877    } else if (!strcasecmp(param, "servicelevel")) {
00878       q->servicelevel= atoi(val);
00879    } else if (!strcasecmp(param, "strategy")) {
00880       q->strategy = strat2int(val);
00881       if (q->strategy < 0) {
00882          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
00883             val, q->name);
00884          q->strategy = QUEUE_STRATEGY_RINGALL;
00885       }
00886    } else if (!strcasecmp(param, "joinempty")) {
00887       if (!strcasecmp(val, "strict"))
00888          q->joinempty = QUEUE_EMPTY_STRICT;
00889       else if (ast_true(val))
00890          q->joinempty = QUEUE_EMPTY_NORMAL;
00891       else
00892          q->joinempty = 0;
00893    } else if (!strcasecmp(param, "leavewhenempty")) {
00894       if (!strcasecmp(val, "strict"))
00895          q->leavewhenempty = QUEUE_EMPTY_STRICT;
00896       else if (ast_true(val))
00897          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
00898       else
00899          q->leavewhenempty = 0;
00900    } else if (!strcasecmp(param, "eventmemberstatus")) {
00901       q->maskmemberstatus = !ast_true(val);
00902    } else if (!strcasecmp(param, "eventwhencalled")) {
00903       if (!strcasecmp(val, "vars")) {
00904          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
00905       } else {
00906          q->eventwhencalled = ast_true(val);
00907       }
00908    } else if (!strcasecmp(param, "reportholdtime")) {
00909       q->reportholdtime = ast_true(val);
00910    } else if (!strcasecmp(param, "memberdelay")) {
00911       q->memberdelay = atoi(val);
00912    } else if (!strcasecmp(param, "weight")) {
00913       q->weight = atoi(val);
00914       if (q->weight)
00915          use_weight++;
00916       /* With Realtime queues, if the last queue using weights is deleted in realtime,
00917          we will not see any effect on use_weight until next reload. */
00918    } else if (!strcasecmp(param, "timeoutrestart")) {
00919       q->timeoutrestart = ast_true(val);
00920    } else if (failunknown) {
00921       if (linenum >= 0) {
00922          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
00923             q->name, param, linenum);
00924       } else {
00925          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
00926       }
00927    }
00928 }
00929 
00930 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
00931 {
00932    struct member *m, *prev_m;
00933    int penalty = 0;
00934    int paused  = 0;
00935 
00936    if (penalty_str) {
00937       penalty = atoi(penalty_str);
00938       if (penalty < 0)
00939          penalty = 0;
00940    }
00941 
00942    if (paused_str) {
00943       paused = atoi(paused_str);
00944       if (paused < 0)
00945          paused = 0;
00946    }
00947 
00948    /* Find the member, or the place to put a new one. */
00949    for (m = q->members, prev_m = NULL;
00950       m && strcmp(m->interface, interface);
00951       prev_m = m, m = m->next);
00952 
00953    /* Create a new one if not found, else update penalty */
00954    if (!m) {
00955       if ((m = create_queue_member(interface, membername, penalty, paused))) {
00956          m->dead = 0;
00957          add_to_interfaces(interface);
00958          if (prev_m) {
00959             prev_m->next = m;
00960          } else {
00961             q->members = m;
00962          }
00963          q->membercount++;
00964       }
00965    } else {
00966       m->dead = 0;   /* Do not delete this one. */
00967       if (paused_str)
00968          m->paused = paused;
00969       m->penalty = penalty;
00970    }
00971 }
00972 
00973 static void free_members(struct call_queue *q, int all)
00974 {
00975    /* Free non-dynamic members */
00976    struct member *curm, *next, *prev = NULL;
00977 
00978    for (curm = q->members; curm; curm = next) {
00979       next = curm->next;
00980       if (all || !curm->dynamic) {
00981          if (prev)
00982             prev->next = next;
00983          else
00984             q->members = next;
00985          remove_from_interfaces(curm->interface);
00986          q->membercount--;
00987          free(curm);
00988       } else
00989          prev = curm;
00990    }
00991 }
00992 
00993 static void destroy_queue(struct call_queue *q)
00994 {
00995    free_members(q, 1);
00996    ast_mutex_destroy(&q->lock);
00997    free(q);
00998 }
00999 
01000 /*!\brief Reload a single queue via realtime.
01001    \return Return the queue, or NULL if it doesn't exist.
01002    \note Should be called with the global qlock locked. */
01003 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
01004 {
01005    struct ast_variable *v;
01006    struct call_queue *q;
01007    struct member *m, *prev_m, *next_m;
01008    char *interface = NULL;
01009    char *tmp, *tmp_name;
01010    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01011 
01012    /* Find the queue in the in-core list (we will create a new one if not found). */
01013    AST_LIST_TRAVERSE(&queues, q, list) {
01014       if (!strcasecmp(q->name, queuename))
01015          break;
01016    }
01017 
01018    /* Static queues override realtime. */
01019    if (q) {
01020       ast_mutex_lock(&q->lock);
01021       if (!q->realtime) {
01022          if (q->dead) {
01023             ast_mutex_unlock(&q->lock);
01024             return NULL;
01025          } else {
01026             ast_mutex_unlock(&q->lock);
01027             return q;
01028          }
01029       }
01030    } else if (!member_config)
01031       /* Not found in the list, and it's not realtime ... */
01032       return NULL;
01033 
01034    /* Check if queue is defined in realtime. */
01035    if (!queue_vars) {
01036       /* Delete queue from in-core list if it has been deleted in realtime. */
01037       if (q) {
01038          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01039             found condition... So we might delete an in-core queue
01040             in case of DB failure. */
01041          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01042 
01043          q->dead = 1;
01044          /* Delete if unused (else will be deleted when last caller leaves). */
01045          if (!q->count) {
01046             /* Delete. */
01047             AST_LIST_REMOVE(&queues, q, list);
01048             ast_mutex_unlock(&q->lock);
01049             destroy_queue(q);
01050          } else
01051             ast_mutex_unlock(&q->lock);
01052       }
01053       return NULL;
01054    }
01055 
01056    /* Create a new queue if an in-core entry does not exist yet. */
01057    if (!q) {
01058       if (!(q = alloc_queue(queuename)))
01059          return NULL;
01060       ast_mutex_lock(&q->lock);
01061       clear_queue(q);
01062       q->realtime = 1;
01063       AST_LIST_INSERT_HEAD(&queues, q, list);
01064    }
01065    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01066 
01067    memset(tmpbuf, 0, sizeof(tmpbuf));
01068    for (v = queue_vars; v; v = v->next) {
01069       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01070       if ((tmp = strchr(v->name, '_'))) {
01071          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01072          tmp_name = tmpbuf;
01073          tmp = tmp_name;
01074          while ((tmp = strchr(tmp, '_')))
01075             *tmp++ = '-';
01076       } else
01077          tmp_name = v->name;
01078       queue_set_param(q, tmp_name, v->value, -1, 0);
01079    }
01080 
01081    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01082       rr_dep_warning();
01083 
01084    /* Temporarily set non-dynamic members dead so we can detect deleted ones. 
01085     * Also set the membercount correctly for realtime*/
01086    for (m = q->members; m; m = m->next, q->membercount++) {
01087       if (!m->dynamic)
01088          m->dead = 1;
01089    }
01090 
01091    while ((interface = ast_category_browse(member_config, interface))) {
01092       rt_handle_member_record(q, interface,
01093          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01094          ast_variable_retrieve(member_config, interface, "penalty"),
01095          ast_variable_retrieve(member_config, interface, "paused"));
01096    }
01097 
01098    /* Delete all realtime members that have been deleted in DB. */
01099    m = q->members;
01100    prev_m = NULL;
01101    while (m) {
01102       next_m = m->next;
01103       if (m->dead) {
01104          if (prev_m) {
01105             prev_m->next = next_m;
01106          } else {
01107             q->members = next_m;
01108          }
01109          remove_from_interfaces(m->interface);
01110          q->membercount--;
01111          free(m);
01112       } else {
01113          prev_m = m;
01114       }
01115       m = next_m;
01116    }
01117 
01118    ast_mutex_unlock(&q->lock);
01119 
01120    return q;
01121 }
01122 
01123 static struct call_queue *load_realtime_queue(const char *queuename)
01124 {
01125    struct ast_variable *queue_vars;
01126    struct ast_config *member_config = NULL;
01127    struct call_queue *q;
01128 
01129    /* Find the queue in the in-core list first. */
01130    AST_LIST_LOCK(&queues);
01131    AST_LIST_TRAVERSE(&queues, q, list) {
01132       if (!strcasecmp(q->name, queuename)) {
01133          break;
01134       }
01135    }
01136    AST_LIST_UNLOCK(&queues);
01137 
01138    if (!q || q->realtime) {
01139       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01140          queue operations while waiting for the DB.
01141 
01142          This will be two separate database transactions, so we might
01143          see queue parameters as they were before another process
01144          changed the queue and member list as it was after the change.
01145          Thus we might see an empty member list when a queue is
01146          deleted. In practise, this is unlikely to cause a problem. */
01147 
01148       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01149       if (queue_vars) {
01150          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01151          if (!member_config) {
01152             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01153             return NULL;
01154          }
01155       }
01156 
01157       AST_LIST_LOCK(&queues);
01158 
01159       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01160       if (member_config)
01161          ast_config_destroy(member_config);
01162       if (queue_vars)
01163          ast_variables_destroy(queue_vars);
01164 
01165       AST_LIST_UNLOCK(&queues);
01166    }
01167    return q;
01168 }
01169 
01170 static void update_realtime_members(struct call_queue *q)
01171 {
01172    struct ast_config *member_config = NULL;
01173    struct member *m, *prev_m, *next_m;
01174    char *interface = NULL;
01175 
01176    member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
01177    if (!member_config) {
01178       /*This queue doesn't have realtime members*/
01179       if (option_debug > 2)
01180          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01181       return;
01182    }
01183 
01184    ast_mutex_lock(&q->lock);
01185    
01186    /* Temporarily set non-dynamic members dead so we can detect deleted ones.*/ 
01187    for (m = q->members; m; m = m->next) {
01188       if (!m->dynamic)
01189          m->dead = 1;
01190    }
01191 
01192    while ((interface = ast_category_browse(member_config, interface))) {
01193       rt_handle_member_record(q, interface,
01194          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01195          ast_variable_retrieve(member_config, interface, "penalty"),
01196          ast_variable_retrieve(member_config, interface, "paused"));
01197    }
01198 
01199    /* Delete all realtime members that have been deleted in DB. */
01200    m = q->members;
01201    prev_m = NULL;
01202    while (m) {
01203       next_m = m->next;
01204       if (m->dead) {
01205          if (prev_m) {
01206             prev_m->next = next_m;
01207          } else {
01208             q->members = next_m;
01209          }
01210          remove_from_interfaces(m->interface);
01211          q->membercount--;
01212          free(m);
01213       } else {
01214          prev_m = m;
01215       }
01216       m = next_m;
01217    }
01218    ast_mutex_unlock(&q->lock);
01219 }
01220 
01221 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
01222 {
01223    struct call_queue *q;
01224    struct queue_ent *cur, *prev = NULL;
01225    int res = -1;
01226    int pos = 0;
01227    int inserted = 0;
01228    enum queue_member_status stat;
01229 
01230    if (!(q = load_realtime_queue(queuename)))
01231       return res;
01232 
01233    AST_LIST_LOCK(&queues);
01234    ast_mutex_lock(&q->lock);
01235 
01236    /* This is our one */
01237    stat = get_member_status(q, qe->max_penalty);
01238    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01239       *reason = QUEUE_JOINEMPTY;
01240    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
01241       *reason = QUEUE_JOINUNAVAIL;
01242    else if (q->maxlen && (q->count >= q->maxlen))
01243       *reason = QUEUE_FULL;
01244    else {
01245       /* There's space for us, put us at the right position inside
01246        * the queue.
01247        * Take into account the priority of the calling user */
01248       inserted = 0;
01249       prev = NULL;
01250       cur = q->head;
01251       while (cur) {
01252          /* We have higher priority than the current user, enter
01253           * before him, after all the other users with priority
01254           * higher or equal to our priority. */
01255          if ((!inserted) && (qe->prio > cur->prio)) {
01256             insert_entry(q, prev, qe, &pos);
01257             inserted = 1;
01258          }
01259          cur->pos = ++pos;
01260          prev = cur;
01261          cur = cur->next;
01262       }
01263       /* No luck, join at the end of the queue */
01264       if (!inserted)
01265          insert_entry(q, prev, qe, &pos);
01266       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01267       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01268       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01269       q->count++;
01270       res = 0;
01271       manager_event(EVENT_FLAG_CALL, "Join",
01272          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01273          qe->chan->name,
01274          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01275          S_OR(qe->chan->cid.cid_name, "unknown"),
01276          q->name, qe->pos, q->count, qe->chan->uniqueid );
01277       if (option_debug)
01278          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01279    }
01280    ast_mutex_unlock(&q->lock);
01281    AST_LIST_UNLOCK(&queues);
01282 
01283    return res;
01284 }
01285 
01286 static int play_file(struct ast_channel *chan, char *filename)
01287 {
01288    int res;
01289 
01290    ast_stopstream(chan);
01291 
01292    res = ast_streamfile(chan, filename, chan->language);
01293    if (!res)
01294       res = ast_waitstream(chan, AST_DIGIT_ANY);
01295 
01296    ast_stopstream(chan);
01297 
01298    return res;
01299 }
01300 
01301 static int valid_exit(struct queue_ent *qe, char digit)
01302 {
01303    int digitlen = strlen(qe->digits);
01304 
01305    /* Prevent possible buffer overflow */
01306    if (digitlen < sizeof(qe->digits) - 2) {
01307       qe->digits[digitlen] = digit;
01308       qe->digits[digitlen + 1] = '\0';
01309    } else {
01310       qe->digits[0] = '\0';
01311       return 0;
01312    }
01313 
01314    /* If there's no context to goto, short-circuit */
01315    if (ast_strlen_zero(qe->context))
01316       return 0;
01317 
01318    /* If the extension is bad, then reset the digits to blank */
01319    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01320       qe->digits[0] = '\0';
01321       return 0;
01322    }
01323 
01324    /* We have an exact match */
01325    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01326       qe->valid_digits = 1;
01327       /* Return 1 on a successful goto */
01328       return 1;
01329    }
01330 
01331    return 0;
01332 }
01333 
01334 static int say_position(struct queue_ent *qe)
01335 {
01336    int res = 0, avgholdmins, avgholdsecs;
01337    time_t now;
01338 
01339    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01340    time(&now);
01341    if ((now - qe->last_pos) < 15)
01342       return 0;
01343 
01344    /* If either our position has changed, or we are over the freq timer, say position */
01345    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01346       return 0;
01347 
01348    ast_moh_stop(qe->chan);
01349    /* Say we're next, if we are */
01350    if (qe->pos == 1) {
01351       res = play_file(qe->chan, qe->parent->sound_next);
01352       if (res)
01353          goto playout;
01354       else
01355          goto posout;
01356    } else {
01357       res = play_file(qe->chan, qe->parent->sound_thereare);
01358       if (res)
01359          goto playout;
01360       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01361       if (res)
01362          goto playout;
01363       res = play_file(qe->chan, qe->parent->sound_calls);
01364       if (res)
01365          goto playout;
01366    }
01367    /* Round hold time to nearest minute */
01368    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01369 
01370    /* If they have specified a rounding then round the seconds as well */
01371    if (qe->parent->roundingseconds) {
01372       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01373       avgholdsecs *= qe->parent->roundingseconds;
01374    } else {
01375       avgholdsecs = 0;
01376    }
01377 
01378    if (option_verbose > 2)
01379       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01380 
01381    /* If the hold time is >1 min, if it's enabled, and if it's not
01382       supposed to be only once and we have already said it, say it */
01383    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01384       (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01385       res = play_file(qe->chan, qe->parent->sound_holdtime);
01386       if (res)
01387          goto playout;
01388 
01389       if (avgholdmins > 0) {
01390          if (avgholdmins < 2) {
01391             res = play_file(qe->chan, qe->parent->sound_lessthan);
01392             if (res)
01393                goto playout;
01394 
01395             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01396             if (res)
01397                goto playout;
01398          } else {
01399             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01400             if (res)
01401                goto playout;
01402          }
01403          
01404          res = play_file(qe->chan, qe->parent->sound_minutes);
01405          if (res)
01406             goto playout;
01407       }
01408       if (avgholdsecs>0) {
01409          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01410          if (res)
01411             goto playout;
01412 
01413          res = play_file(qe->chan, qe->parent->sound_seconds);
01414          if (res)
01415             goto playout;
01416       }
01417 
01418    }
01419 
01420 posout:
01421    if (option_verbose > 2)
01422       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01423          qe->chan->name, qe->parent->name, qe->pos);
01424    res = play_file(qe->chan, qe->parent->sound_thanks);
01425 
01426 playout:
01427    if (res > 0 && !valid_exit(qe, res))
01428       res = 0;
01429 
01430    /* Set our last_pos indicators */
01431    qe->last_pos = now;
01432    qe->last_pos_said = qe->pos;
01433 
01434    /* Don't restart music on hold if we're about to exit the caller from the queue */
01435    if (!res)
01436       ast_moh_start(qe->chan, qe->moh, NULL);
01437 
01438    return res;
01439 }
01440 
01441 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01442 {
01443    int oldvalue;
01444 
01445    /* Calculate holdtime using a recursive boxcar filter */
01446    /* Thanks to SRT for this contribution */
01447    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01448 
01449    ast_mutex_lock(&qe->parent->lock);
01450    oldvalue = qe->parent->holdtime;
01451    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01452    ast_mutex_unlock(&qe->parent->lock);
01453 }
01454 
01455 
01456 static void leave_queue(struct queue_ent *qe)
01457 {
01458    struct call_queue *q;
01459    struct queue_ent *cur, *prev = NULL;
01460    int pos = 0;
01461 
01462    if (!(q = qe->parent))
01463       return;
01464    ast_mutex_lock(&q->lock);
01465 
01466    prev = NULL;
01467    for (cur = q->head; cur; cur = cur->next) {
01468       if (cur == qe) {
01469          q->count--;
01470 
01471          /* Take us out of the queue */
01472          manager_event(EVENT_FLAG_CALL, "Leave",
01473             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01474             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01475          if (option_debug)
01476             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01477          /* Take us out of the queue */
01478          if (prev)
01479             prev->next = cur->next;
01480          else
01481             q->head = cur->next;
01482       } else {
01483          /* Renumber the people after us in the queue based on a new count */
01484          cur->pos = ++pos;
01485          prev = cur;
01486       }
01487    }
01488    ast_mutex_unlock(&q->lock);
01489 
01490    if (q->dead && !q->count) {   
01491       /* It's dead and nobody is in it, so kill it */
01492       AST_LIST_LOCK(&queues);
01493       AST_LIST_REMOVE(&queues, q, list);
01494       AST_LIST_UNLOCK(&queues);
01495       destroy_queue(q);
01496    }
01497 }
01498 
01499 /* Hang up a list of outgoing calls */
01500 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
01501 {
01502    struct callattempt *oo;
01503 
01504    while (outgoing) {
01505       /* Hangup any existing lines we have open */
01506       if (outgoing->chan && (outgoing->chan != exception))
01507          ast_hangup(outgoing->chan);
01508       oo = outgoing;
01509       outgoing = outgoing->q_next;
01510       free(oo);
01511    }
01512 }
01513 
01514 static int update_status(struct call_queue *q, struct member *member, int status)
01515 {
01516    struct member *cur;
01517 
01518    /* Since a reload could have taken place, we have to traverse the list to
01519       be sure it's still valid */
01520    ast_mutex_lock(&q->lock);
01521    for (cur = q->members; cur; cur = cur->next) {
01522       if (member != cur)
01523          continue;
01524 
01525       cur->status = status;
01526       if (!q->maskmemberstatus) {
01527          manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01528             "Queue: %s\r\n"
01529             "Location: %s\r\n"
01530             "MemberName: %s\r\n"
01531             "Membership: %s\r\n"
01532             "Penalty: %d\r\n"
01533             "CallsTaken: %d\r\n"
01534             "LastCall: %d\r\n"
01535             "Status: %d\r\n"
01536             "Paused: %d\r\n",
01537             q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
01538             cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01539       }
01540    }
01541    ast_mutex_unlock(&q->lock);
01542    return 0;
01543 }
01544 
01545 static int update_dial_status(struct call_queue *q, struct member *member, int status)
01546 {
01547    if (status == AST_CAUSE_BUSY)
01548       status = AST_DEVICE_BUSY;
01549    else if (status == AST_CAUSE_UNREGISTERED)
01550       status = AST_DEVICE_UNAVAILABLE;
01551    else if (status == AST_CAUSE_NOSUCHDRIVER)
01552       status = AST_DEVICE_INVALID;
01553    else
01554       status = AST_DEVICE_UNKNOWN;
01555    return update_status(q, member, status);
01556 }
01557 
01558 /* traverse all defined queues which have calls waiting and contain this member
01559    return 0 if no other queue has precedence (higher weight) or 1 if found  */
01560 static int compare_weight(struct call_queue *rq, struct member *member)
01561 {
01562    struct call_queue *q;
01563    struct member *mem;
01564    int found = 0;
01565    
01566    /* &qlock and &rq->lock already set by try_calling()
01567     * to solve deadlock */
01568    AST_LIST_TRAVERSE(&queues, q, list) {
01569       if (q == rq) /* don't check myself, could deadlock */
01570          continue;
01571       ast_mutex_lock(&q->lock);
01572       if (q->count && q->members) {
01573          for (mem = q->members; mem; mem = mem->next) {
01574             if (strcmp(mem->interface, member->interface))
01575                continue;
01576 
01577             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01578             if (q->weight > rq->weight) {
01579                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);
01580                found = 1;
01581                break;
01582             }
01583          }
01584       }
01585       ast_mutex_unlock(&q->lock);
01586       if (found)
01587          break;
01588    }
01589    return found;
01590 }
01591 
01592 /*! \brief common hangup actions */
01593 static void do_hang(struct callattempt *o)
01594 {
01595    o->stillgoing = 0;
01596    ast_hangup(o->chan);
01597    o->chan = NULL;
01598 }
01599 
01600 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
01601 {
01602    char *tmp = alloca(len);
01603 
01604    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01605       int i, j;
01606 
01607       /* convert "\n" to "\nVariable: " */
01608       strcpy(vars, "Variable: ");
01609 
01610       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01611          vars[j] = tmp[i];
01612 
01613          if (tmp[i + 1] == '\0')
01614             break;
01615          if (tmp[i] == '\n') {
01616             vars[j] = '\r';
01617             vars[++j] = '\n';
01618 
01619             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01620             j += 9;
01621          }
01622       }
01623       if (j > len - 1)
01624          j = len - 1;
01625       vars[j - 2] = '\r';
01626       vars[j - 1] = '\n';
01627       vars[j] = '\0';
01628    } else {
01629       /* there are no channel variables; leave it blank */
01630       *vars = '\0';
01631    }
01632    return vars;
01633 }
01634 
01635 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
01636 {
01637    int res;
01638    int status;
01639    char tech[256];
01640    char *location;
01641 
01642    /* on entry here, we know that tmp->chan == NULL */
01643    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01644       if (option_debug)
01645          ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01646       if (qe->chan->cdr)
01647          ast_cdr_busy(qe->chan->cdr);
01648       tmp->stillgoing = 0;
01649       (*busies)++;
01650       return 0;
01651    }
01652 
01653    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01654       if (option_debug)
01655          ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
01656       if (qe->chan->cdr)
01657          ast_cdr_busy(qe->chan->cdr);
01658       tmp->stillgoing = 0;
01659       return 0;
01660    }
01661 
01662    if (tmp->member->paused) {
01663       if (option_debug)
01664          ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01665       if (qe->chan->cdr)
01666          ast_cdr_busy(qe->chan->cdr);
01667       tmp->stillgoing = 0;
01668       return 0;
01669    }
01670    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01671       ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01672       if (qe->chan->cdr)
01673          ast_cdr_busy(qe->chan->cdr);
01674       tmp->stillgoing = 0;
01675       (*busies)++;
01676       return 0;
01677    }
01678 
01679    ast_copy_string(tech, tmp->interface, sizeof(tech));
01680    if ((location = strchr(tech, '/')))
01681       *location++ = '\0';
01682    else
01683       location = "";
01684 
01685    /* Request the peer */
01686    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01687    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01688       if (qe->chan->cdr)
01689          ast_cdr_busy(qe->chan->cdr);
01690       tmp->stillgoing = 0;
01691       update_dial_status(qe->parent, tmp->member, status);
01692 
01693       ast_mutex_lock(&qe->parent->lock);
01694       qe->parent->rrpos++;
01695       ast_mutex_unlock(&qe->parent->lock);
01696 
01697       (*busies)++;
01698       return 0;
01699    } else if (status != tmp->oldstatus)
01700       update_dial_status(qe->parent, tmp->member, status);
01701    
01702    tmp->chan->appl = "AppQueue";
01703    tmp->chan->data = "(Outgoing Line)";
01704    tmp->chan->whentohangup = 0;
01705    if (tmp->chan->cid.cid_num)
01706       free(tmp->chan->cid.cid_num);
01707    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01708    if (tmp->chan->cid.cid_name)
01709       free(tmp->chan->cid.cid_name);
01710    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01711    if (tmp->chan->cid.cid_ani)
01712       free(tmp->chan->cid.cid_ani);
01713    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01714 
01715    /* Inherit specially named variables from parent channel */
01716    ast_channel_inherit_variables(qe->chan, tmp->chan);
01717 
01718    /* Presense of ADSI CPE on outgoing channel follows ours */
01719    tmp->chan->adsicpe = qe->chan->adsicpe;
01720 
01721    /* Place the call, but don't wait on the answer */
01722    if ((res = ast_call(tmp->chan, location, 0))) {
01723       /* Again, keep going even if there's an error */
01724       if (option_debug)
01725          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01726       if (option_verbose > 2)
01727          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01728       do_hang(tmp);
01729       (*busies)++;
01730       return 0;
01731    } else if (qe->parent->eventwhencalled) {
01732       char vars[2048];
01733 
01734       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01735                "AgentCalled: %s\r\n"
01736                "ChannelCalling: %s\r\n"
01737                "CallerID: %s\r\n"
01738                "CallerIDName: %s\r\n"
01739                "Context: %s\r\n"
01740                "Extension: %s\r\n"
01741                "Priority: %d\r\n"
01742                "%s",
01743                tmp->interface, qe->chan->name,
01744                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01745                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01746                qe->chan->context, qe->chan->exten, qe->chan->priority,
01747                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01748       if (option_verbose > 2)
01749          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01750    }
01751 
01752    return 1;
01753 }
01754 
01755 /*! \brief find the entry with the best metric, or NULL */
01756 static struct callattempt *find_best(struct callattempt *outgoing)
01757 {
01758    struct callattempt *best = NULL, *cur;
01759 
01760    for (cur = outgoing; cur; cur = cur->q_next) {
01761       if (cur->stillgoing &&              /* Not already done */
01762          !cur->chan &&              /* Isn't already going */
01763          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01764          best = cur;
01765       }
01766    }
01767 
01768    return best;
01769 }
01770 
01771 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
01772 {
01773    int ret = 0;
01774 
01775    while (ret == 0) {
01776       struct callattempt *best = find_best(outgoing);
01777       if (!best) {
01778          if (option_debug)
01779             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01780          break;
01781       }
01782       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01783          struct callattempt *cur;
01784          /* Ring everyone who shares this best metric (for ringall) */
01785          for (cur = outgoing; cur; cur = cur->q_next) {
01786             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01787                if (option_debug)
01788                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01789                ring_entry(qe, cur, busies);
01790             }
01791          }
01792       } else {
01793          /* Ring just the best channel */
01794          if (option_debug)
01795             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01796          ring_entry(qe, best, busies);
01797       }
01798       if (best->chan) /* break out with result = 1 */
01799          ret = 1;
01800    }
01801 
01802    return ret;
01803 }
01804 
01805 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
01806 {
01807    struct callattempt *best = find_best(outgoing);
01808 
01809    if (best) {
01810       /* Ring just the best channel */
01811       if (option_debug)
01812          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01813       qe->parent->rrpos = best->metric % 1000;
01814    } else {
01815       /* Just increment rrpos */
01816       if (qe->parent->wrapped) {
01817          /* No more channels, start over */
01818          qe->parent->rrpos = 0;
01819       } else {
01820          /* Prioritize next entry */
01821          qe->parent->rrpos++;
01822       }
01823    }
01824    qe->parent->wrapped = 0;
01825 
01826    return 0;
01827 }
01828 
01829 static int say_periodic_announcement(struct queue_ent *qe)
01830 {
01831    int res = 0;
01832    time_t now;
01833 
01834    /* Get the current time */
01835    time(&now);
01836 
01837    /* Check to see if it is time to announce */
01838    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01839       return 0;
01840 
01841    /* Stop the music on hold so we can play our own file */
01842    ast_moh_stop(qe->chan);
01843 
01844    if (option_verbose > 2)
01845       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01846 
01847    /* Check to make sure we have a sound file. If not, reset to the first sound file */
01848    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
01849       qe->last_periodic_announce_sound = 0;
01850    }
01851    
01852    /* play the announcement */
01853    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
01854 
01855    if (res > 0 && !valid_exit(qe, res))
01856       res = 0;
01857 
01858    /* Resume Music on Hold if the caller is going to stay in the queue */
01859    if (!res)
01860       ast_moh_start(qe->chan, qe->moh, NULL);
01861 
01862    /* update last_periodic_announce_time */
01863    qe->last_periodic_announce_time = now;
01864 
01865    /* Update the current periodic announcement to the next announcement */
01866    qe->last_periodic_announce_sound++;
01867    
01868    return res;
01869 }
01870 
01871 static void record_abandoned(struct queue_ent *qe)
01872 {
01873    ast_mutex_lock(&qe->parent->lock);
01874    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
01875       "Queue: %s\r\n"
01876       "Uniqueid: %s\r\n"
01877       "Position: %d\r\n"
01878       "OriginalPosition: %d\r\n"
01879       "HoldTime: %d\r\n",
01880       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
01881 
01882    qe->parent->callsabandoned++;
01883    ast_mutex_unlock(&qe->parent->lock);
01884 }
01885 
01886 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
01887 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
01888 {
01889    if (option_verbose > 2)
01890       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
01891    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
01892    if (qe->parent->autopause) {
01893       if (!set_member_paused(qe->parent->name, interface, 1)) {
01894          if (option_verbose > 2)
01895             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
01896       } else {
01897          if (option_verbose > 2)
01898             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
01899       }
01900    }
01901    return;
01902 }
01903 
01904 #define AST_MAX_WATCHERS 256
01905 
01906 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
01907 {
01908    char *queue = qe->parent->name;
01909    struct callattempt *o;
01910    int status;
01911    int sentringing = 0;
01912    int numbusies = prebusies;
01913    int numnochan = 0;
01914    int stillgoing = 0;
01915    int orig = *to;
01916    struct ast_frame *f;
01917    struct callattempt *peer = NULL;
01918    struct ast_channel *winner;
01919    struct ast_channel *in = qe->chan;
01920    char on[80] = "";
01921    char membername[80] = "";
01922    long starttime = 0;
01923    long endtime = 0; 
01924 
01925    starttime = (long) time(NULL);
01926    
01927    while (*to && !peer) {
01928       int numlines, retry, pos = 1;
01929       struct ast_channel *watchers[AST_MAX_WATCHERS];
01930       watchers[0] = in;
01931 
01932       for (retry = 0; retry < 2; retry++) {
01933          numlines = 0;
01934          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
01935             if (o->stillgoing) { /* Keep track of important channels */
01936                stillgoing = 1;
01937                if (o->chan)
01938                   watchers[pos++] = o->chan;
01939             }
01940             numlines++;
01941          }
01942          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
01943             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
01944             break;
01945          /* On "ringall" strategy we only move to the next penalty level
01946             when *all* ringing phones are done in the current penalty level */
01947          ring_one(qe, outgoing, &numbusies);
01948          /* and retry... */
01949       }
01950       if (pos == 1 /* not found */) {
01951          if (numlines == (numbusies + numnochan)) {
01952             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
01953          } else {
01954             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
01955          }
01956          *to = 0;
01957          return NULL;
01958       }
01959       winner = ast_waitfor_n(watchers, pos, to);
01960       for (o = outgoing; o; o = o->q_next) {
01961          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
01962             if (!peer) {
01963                if (option_verbose > 2)
01964                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01965                peer = o;
01966             }
01967          } else if (o->chan && (o->chan == winner)) {
01968 
01969             ast_copy_string(on, o->member->interface, sizeof(on));
01970             ast_copy_string(membername, o->member->membername, sizeof(membername));
01971 
01972             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
01973                if (option_verbose > 2)
01974                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
01975                numnochan++;
01976                do_hang(o);
01977                winner = NULL;
01978                continue;
01979             } else if (!ast_strlen_zero(o->chan->call_forward)) {
01980                char tmpchan[256];
01981                char *stuff;
01982                char *tech;
01983 
01984                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
01985                if ((stuff = strchr(tmpchan, '/'))) {
01986                   *stuff++ = '\0';
01987                   tech = tmpchan;
01988                } else {
01989                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
01990                   stuff = tmpchan;
01991                   tech = "Local";
01992                }
01993                /* Before processing channel, go ahead and check for forwarding */
01994                if (option_verbose > 2)
01995                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
01996                /* Setup parameters */
01997                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
01998                if (status != o->oldstatus)
01999                   update_dial_status(qe->parent, o->member, status);                
02000                if (!o->chan) {
02001                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02002                   o->stillgoing = 0;
02003                   numnochan++;
02004                } else {
02005                   ast_channel_inherit_variables(in, o->chan);
02006                   if (o->chan->cid.cid_num)
02007                      free(o->chan->cid.cid_num);
02008                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02009 
02010                   if (o->chan->cid.cid_name)
02011                      free(o->chan->cid.cid_name);
02012                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02013 
02014                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02015                   o->chan->cdrflags = in->cdrflags;
02016 
02017                   if (in->cid.cid_ani) {
02018                      if (o->chan->cid.cid_ani)
02019                         free(o->chan->cid.cid_ani);
02020                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02021                   }
02022                   if (o->chan->cid.cid_rdnis)
02023                      free(o->chan->cid.cid_rdnis);
02024                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02025                   if (ast_call(o->chan, tmpchan, 0)) {
02026                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02027                      do_hang(o);
02028                      numnochan++;
02029                   }
02030                }
02031                /* Hangup the original channel now, in case we needed it */
02032                ast_hangup(winner);
02033                continue;
02034             }
02035             f = ast_read(winner);
02036             if (f) {
02037                if (f->frametype == AST_FRAME_CONTROL) {
02038                   switch (f->subclass) {
02039                   case AST_CONTROL_ANSWER:
02040                      /* This is our guy if someone answered. */
02041                      if (!peer) {
02042                         if (option_verbose > 2)
02043                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02044                         peer = o;
02045                      }
02046                      break;
02047                   case AST_CONTROL_BUSY:
02048                      if (option_verbose > 2)
02049                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02050                      if (in->cdr)
02051                         ast_cdr_busy(in->cdr);
02052                      do_hang(o);
02053                      endtime = (long)time(NULL);
02054                      endtime -= starttime;
02055                      rna(endtime*1000, qe, on, membername);
02056                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02057                         if (qe->parent->timeoutrestart)
02058                            *to = orig;
02059                         ring_one(qe, outgoing, &numbusies);
02060                      }
02061                      numbusies++;
02062                      break;
02063                   case AST_CONTROL_CONGESTION:
02064                      if (option_verbose > 2)
02065                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02066                      if (in->cdr)
02067                         ast_cdr_busy(in->cdr);
02068                      endtime = (long)time(NULL);
02069                      endtime -= starttime;
02070                      rna(endtime*1000, qe, on, membername);
02071                      do_hang(o);
02072                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02073                         if (qe->parent->timeoutrestart)
02074                            *to = orig;
02075                         ring_one(qe, outgoing, &numbusies);
02076                      }
02077                      numbusies++;
02078                      break;
02079                   case AST_CONTROL_RINGING:
02080                      if (option_verbose > 2)
02081                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02082                      if (!sentringing) {
02083 #if 0
02084                         ast_indicate(in, AST_CONTROL_RINGING);
02085 #endif                        
02086                         sentringing++;
02087                      }
02088                      break;
02089                   case AST_CONTROL_OFFHOOK:
02090                      /* Ignore going off hook */
02091                      break;
02092                   default:
02093                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02094                   }
02095                }
02096                ast_frfree(f);
02097             } else {
02098                endtime = (long) time(NULL) - starttime;
02099                rna(endtime * 1000, qe, on, membername);
02100                do_hang(o);
02101                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02102                   if (qe->parent->timeoutrestart)
02103                      *to = orig;
02104                   ring_one(qe, outgoing, &numbusies);
02105                }
02106             }
02107          }
02108       }
02109       if (winner == in) {
02110          f = ast_read(in);
02111          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02112             /* Got hung up */
02113             *to = -1;
02114             if (f)
02115                ast_frfree(f);
02116             return NULL;
02117          }
02118          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02119             if (option_verbose > 3)
02120                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02121             *to = 0;
02122             ast_frfree(f);
02123             return NULL;
02124          }
02125          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02126             if (option_verbose > 3)
02127                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02128             *to = 0;
02129             *digit = f->subclass;
02130             ast_frfree(f);
02131             return NULL;
02132          }
02133          ast_frfree(f);
02134       }
02135       if (!*to)
02136          rna(orig, qe, on, membername);
02137    }
02138 
02139    return peer;
02140 }
02141 
02142 static int is_our_turn(struct queue_ent *qe)
02143 {
02144    struct queue_ent *ch;
02145    struct member *cur;
02146    int avl = 0;
02147    int idx = 0;
02148    int res;
02149 
02150    if (!qe->parent->autofill) {
02151       /* Atomically read the parent head -- does not need a lock */
02152       ch = qe->parent->head;
02153       /* If we are now at the top of the head, break out */
02154       if (ch == qe) {
02155          if (option_debug)
02156             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02157          res = 1;
02158       } else {
02159          if (option_debug)
02160             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02161          res = 0;
02162       }  
02163 
02164    } else {
02165       /* This needs a lock. How many members are available to be served? */
02166       ast_mutex_lock(&qe->parent->lock);
02167          
02168       ch = qe->parent->head;
02169    
02170       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02171          if (option_debug)
02172             ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
02173          avl = 1;
02174       } else {
02175          for (cur = qe->parent->members; cur; cur = cur->next) {
02176             switch (cur->status) {
02177             case AST_DEVICE_NOT_INUSE:
02178             case AST_DEVICE_UNKNOWN:
02179                avl++;
02180                break;
02181             }
02182          }
02183       }
02184 
02185       if (option_debug)
02186          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02187    
02188       while ((idx < avl) && (ch) && (ch != qe)) {
02189          idx++;
02190          ch = ch->next;       
02191       }
02192    
02193       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02194       if (ch && idx < avl) {
02195          if (option_debug)
02196             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02197          res = 1;
02198       } else {
02199          if (option_debug)
02200             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02201          res = 0;
02202       }
02203       
02204       ast_mutex_unlock(&qe->parent->lock);
02205    }
02206 
02207    return res;
02208 }
02209 
02210 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02211 {
02212    int res = 0;
02213 
02214    /* This is the holding pen for callers 2 through maxlen */
02215    for (;;) {
02216       enum queue_member_status stat;
02217 
02218       if (is_our_turn(qe))
02219          break;
02220 
02221       /* If we have timed out, break out */
02222       if (qe->expire && (time(NULL) > qe->expire)) {
02223          *reason = QUEUE_TIMEOUT;
02224          break;
02225       }
02226 
02227       stat = get_member_status(qe->parent, qe->max_penalty);
02228 
02229       /* leave the queue if no agents, if enabled */
02230       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02231          *reason = QUEUE_LEAVEEMPTY;
02232          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02233          leave_queue(qe);
02234          break;
02235       }
02236 
02237       /* leave the queue if no reachable agents, if enabled */
02238       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02239          *reason = QUEUE_LEAVEUNAVAIL;
02240          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02241          leave_queue(qe);
02242          break;
02243       }
02244 
02245       /* Make a position announcement, if enabled */
02246       if (qe->parent->announcefrequency && !ringing &&
02247          (res = say_position(qe)))
02248          break;
02249 
02250       /* Make a periodic announcement, if enabled */
02251       if (qe->parent->periodicannouncefrequency && !ringing &&
02252          (res = say_periodic_announcement(qe)))
02253          break;
02254 
02255       /* Wait a second before checking again */
02256       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02257          if (res > 0 && !valid_exit(qe, res))
02258             res = 0;
02259          else
02260             break;
02261       }
02262    }
02263 
02264    return res;
02265 }
02266 
02267 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
02268 {
02269    struct member *cur;
02270 
02271    /* Since a reload could have taken place, we have to traverse the list to
02272       be sure it's still valid */
02273    ast_mutex_lock(&q->lock);
02274    cur = q->members;
02275    while (cur) {
02276       if (member == cur) {
02277          time(&cur->lastcall);
02278          cur->calls++;
02279          break;
02280       }
02281       cur = cur->next;
02282    }
02283    q->callscompleted++;
02284    if (callcompletedinsl)
02285       q->callscompletedinsl++;
02286    ast_mutex_unlock(&q->lock);
02287    return 0;
02288 }
02289 
02290 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
02291 {
02292    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02293       return -1;
02294 
02295    switch (q->strategy) {
02296    case QUEUE_STRATEGY_RINGALL:
02297       /* Everyone equal, except for penalty */
02298       tmp->metric = mem->penalty * 1000000;
02299       break;
02300    case QUEUE_STRATEGY_ROUNDROBIN:
02301       if (!pos) {
02302          if (!q->wrapped) {
02303             /* No more channels, start over */
02304             q->rrpos = 0;
02305          } else {
02306             /* Prioritize next entry */
02307             q->rrpos++;
02308          }
02309          q->wrapped = 0;
02310       }
02311       /* Fall through */
02312    case QUEUE_STRATEGY_RRMEMORY:
02313       if (pos < q->rrpos) {
02314          tmp->metric = 1000 + pos;
02315       } else {
02316          if (pos > q->rrpos)
02317             /* Indicate there is another priority */
02318             q->wrapped = 1;
02319          tmp->metric = pos;
02320       }
02321       tmp->metric += mem->penalty * 1000000;
02322       break;
02323    case QUEUE_STRATEGY_RANDOM:
02324       tmp->metric = ast_random() % 1000;
02325       tmp->metric += mem->penalty * 1000000;
02326       break;
02327    case QUEUE_STRATEGY_FEWESTCALLS:
02328       tmp->metric = mem->calls;
02329       tmp->metric += mem->penalty * 1000000;
02330       break;
02331    case QUEUE_STRATEGY_LEASTRECENT:
02332       if (!mem->lastcall)
02333          tmp->metric = 0;
02334       else
02335          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02336       tmp->metric += mem->penalty * 1000000;
02337       break;
02338    default:
02339       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02340       break;
02341    }
02342    return 0;
02343 }
02344 
02345 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi)
02346 {
02347    struct member *cur;
02348    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02349    int to;
02350    char oldexten[AST_MAX_EXTENSION]="";
02351    char oldcontext[AST_MAX_CONTEXT]="";
02352    char queuename[256]="";
02353    struct ast_channel *peer;
02354    struct ast_channel *which;
02355    struct callattempt *lpeer;
02356    struct member *member;
02357    struct ast_app *app;
02358    int res = 0, bridge = 0;
02359    int numbusies = 0;
02360    int x=0;
02361    char *announce = NULL;
02362    char digit = 0;
02363    time_t callstart;
02364    time_t now = time(NULL);
02365    struct ast_bridge_config bridge_config;
02366    char nondataquality = 1;
02367    char *agiexec = NULL;
02368    int ret = 0;
02369    const char *monitorfilename;
02370    const char *monitor_exec;
02371    const char *monitor_options;
02372    char tmpid[256], tmpid2[256];
02373    char meid[1024], meid2[1024];
02374    char mixmonargs[1512];
02375    struct ast_app *mixmonapp = NULL;
02376    char *p;
02377    char vars[2048];
02378    int forwardsallowed = 1;
02379    int callcompletedinsl;
02380    int noption = 0;
02381 
02382    memset(&bridge_config, 0, sizeof(bridge_config));
02383    time(&now);
02384       
02385    for (; options && *options; options++)
02386       switch (*options) {
02387       case 't':
02388          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02389          break;
02390       case 'T':
02391          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02392          break;
02393       case 'w':
02394          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02395          break;
02396       case 'W':
02397          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02398          break;
02399       case 'd':
02400          nondataquality = 0;
02401          break;
02402       case 'h':
02403          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02404          break;
02405       case 'H':
02406          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02407          break;
02408       case 'n':
02409          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02410             (*go_on)++;
02411          else
02412             *go_on = qe->parent->membercount;
02413          noption = 1;
02414          break;
02415       case 'i':
02416          forwardsallowed = 0;
02417          break;
02418       }
02419 
02420    if(!noption)
02421       *go_on = -1;
02422 
02423    /* Hold the lock while we setup the outgoing calls */
02424    if (use_weight)
02425       AST_LIST_LOCK(&queues);
02426    ast_mutex_lock(&qe->parent->lock);
02427    if (option_debug)
02428       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02429                      qe->chan->name);
02430    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02431    cur = qe->parent->members;
02432    if (!ast_strlen_zero(qe->announce))
02433       announce = qe->announce;
02434    if (!ast_strlen_zero(announceoverride))
02435       announce = announceoverride;
02436 
02437    for (; cur; cur = cur->next) {
02438       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02439 
02440       if (!tmp) {
02441          ast_mutex_unlock(&qe->parent->lock);
02442          if (use_weight)
02443             AST_LIST_UNLOCK(&queues);
02444          goto out;
02445       }
02446       tmp->stillgoing = -1;
02447       tmp->member = cur;      /* Never directly dereference!  Could change on reload */
02448       tmp->oldstatus = cur->status;
02449       tmp->lastcall = cur->lastcall;
02450       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02451       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02452          just calculate their metric for the appropriate strategy */
02453       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02454          /* Put them in the list of outgoing thingies...  We're ready now.
02455             XXX If we're forcibly removed, these outgoing calls won't get
02456             hung up XXX */
02457          tmp->q_next = outgoing;
02458          outgoing = tmp;      
02459          /* If this line is up, don't try anybody else */
02460          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02461             break;
02462       } else {
02463          free(tmp);
02464       }
02465    }
02466    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02467       to = (qe->expire - now) * 1000;
02468    else
02469       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02470    ring_one(qe, outgoing, &numbusies);
02471    ast_mutex_unlock(&qe->parent->lock);
02472    if (use_weight)
02473       AST_LIST_UNLOCK(&queues);
02474    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02475    ast_mutex_lock(&qe->parent->lock);
02476    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02477       store_next(qe, outgoing);
02478    }
02479    ast_mutex_unlock(&qe->parent->lock);
02480    peer = lpeer ? lpeer->chan : NULL;
02481    if (!peer) {
02482       if (to) {
02483          /* Must gotten hung up */
02484          res = -1;
02485       } else {
02486          res = digit;
02487          if (res > 0 && !valid_exit(qe, res))
02488             res = 0;
02489       }
02490       if (option_debug)
02491          ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02492    } else { /* peer is valid */
02493       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02494          we will always return with -1 so that it is hung up properly after the
02495          conversation.  */
02496       qe->handled++;
02497       if (!strcmp(qe->chan->tech->type, "Zap"))
02498          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02499       if (!strcmp(peer->tech->type, "Zap"))
02500          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02501       /* Update parameters for the queue */
02502       time(&now);
02503       recalc_holdtime(qe, (now - qe->start));
02504       ast_mutex_lock(&qe->parent->lock);
02505       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02506       ast_mutex_unlock(&qe->parent->lock);
02507       member = lpeer->member;
02508       hangupcalls(outgoing, peer);
02509       outgoing = NULL;
02510       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02511          int res2;
02512 
02513          res2 = ast_autoservice_start(qe->chan);
02514          if (!res2) {
02515             if (qe->parent->memberdelay) {
02516                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02517                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02518             }
02519             if (!res2 && announce) {
02520                if (play_file(peer, announce))
02521                   ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
02522             }
02523             if (!res2 && qe->parent->reportholdtime) {
02524                if (!play_file(peer, qe->parent->sound_reporthold)) {
02525                   int holdtime;
02526 
02527                   time(&now);
02528                   holdtime = abs((now - qe->start) / 60);
02529                   if (holdtime < 2) {
02530                      play_file(peer, qe->parent->sound_lessthan);
02531                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02532                   } else
02533                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02534                   play_file(peer, qe->parent->sound_minutes);
02535                }
02536             }
02537          }
02538          res2 |= ast_autoservice_stop(qe->chan);
02539          if (peer->_softhangup) {
02540             /* Agent must have hung up */
02541             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
02542             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02543             record_abandoned(qe);
02544             if (qe->parent->eventwhencalled)
02545                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02546                      "Queue: %s\r\n"
02547                      "Uniqueid: %s\r\n"
02548                      "Channel: %s\r\n"
02549                      "Member: %s\r\n"
02550                      "MemberName: %s\r\n"
02551                      "%s",
02552                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02553                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02554             ast_hangup(peer);
02555             goto out;
02556          } else if (res2) {
02557             /* Caller must have hung up just before being connected*/
02558             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02559             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02560             record_abandoned(qe);
02561             ast_hangup(peer);
02562             return -1;
02563          }
02564       }
02565       /* Stop music on hold */
02566       ast_moh_stop(qe->chan);
02567       /* If appropriate, log that we have a destination channel */
02568       if (qe->chan->cdr)
02569          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02570       /* Make sure channels are compatible */
02571       res = ast_channel_make_compatible(qe->chan, peer);
02572       if (res < 0) {
02573          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02574          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02575          record_abandoned(qe);
02576          ast_hangup(peer);
02577          return -1;
02578       }
02579       /* Begin Monitoring */
02580       if (qe->parent->monfmt && *qe->parent->monfmt) {
02581          if (!qe->parent->montype) {
02582             if (option_debug)
02583                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02584             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02585             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02586                which = qe->chan;
02587             else
02588                which = peer;
02589             if (monitorfilename)
02590                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02591             else if (qe->chan->cdr)
02592                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02593             else {
02594                /* Last ditch effort -- no CDR, make up something */
02595                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02596                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02597             }
02598             if (qe->parent->monjoin)
02599                ast_monitor_setjoinfiles(which, 1);
02600          } else {
02601             if (option_debug)
02602                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02603             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02604             if (!monitorfilename) {
02605                if (qe->chan->cdr)
02606                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02607                else
02608                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02609             } else {
02610                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02611                for (p = tmpid2; *p ; p++) {
02612                   if (*p == '^' && *(p+1) == '{') {
02613                      *p = '$';
02614                   }
02615                }
02616 
02617                memset(tmpid, 0, sizeof(tmpid));
02618                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02619             }
02620 
02621             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02622             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02623 
02624             if (monitor_exec) {
02625                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02626                for (p = meid2; *p ; p++) {
02627                   if (*p == '^' && *(p+1) == '{') {
02628                      *p = '$';
02629                   }
02630                }
02631 
02632                memset(meid, 0, sizeof(meid));
02633                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02634             }
02635    
02636             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02637 
02638             mixmonapp = pbx_findapp("MixMonitor");
02639 
02640             if (strchr(tmpid2, '|')) {
02641                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02642                mixmonapp = NULL;
02643             }
02644 
02645             if (!monitor_options)
02646                monitor_options = "";
02647             
02648             if (strchr(monitor_options, '|')) {
02649                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02650                mixmonapp = NULL;
02651             }
02652 
02653             if (mixmonapp) {
02654                if (!ast_strlen_zero(monitor_exec))
02655                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
02656                else
02657                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
02658                   
02659                if (option_debug)
02660                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
02661 
02662                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
02663 
02664             } else
02665                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
02666 
02667          }
02668       }
02669       /* Drop out of the queue at this point, to prepare for next caller */
02670       leave_queue(qe);        
02671       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02672          if (option_debug)
02673             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02674          ast_channel_sendurl(peer, url);
02675       }
02676       if (qe->parent->setinterfacevar)
02677             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02678       if (!ast_strlen_zero(agi)) {
02679          if (option_debug)
02680             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
02681          app = pbx_findapp("agi");
02682          if (app) {
02683             agiexec = ast_strdupa(agi);
02684             ret = pbx_exec(qe->chan, app, agiexec);
02685          } else
02686             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
02687       }
02688       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
02689       if (qe->parent->eventwhencalled)
02690          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02691                "Queue: %s\r\n"
02692                "Uniqueid: %s\r\n"
02693                "Channel: %s\r\n"
02694                "Member: %s\r\n"
02695                "MemberName: %s\r\n"
02696                "Holdtime: %ld\r\n"
02697                "BridgedChannel: %s\r\n"
02698                "%s",
02699                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02700                (long)time(NULL) - qe->start, peer->uniqueid,
02701                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02702       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02703       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02704       time(&callstart);
02705 
02706       if (member->status == AST_DEVICE_NOT_INUSE)
02707          ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
02708          
02709 
02710       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02711 
02712       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02713          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02714             qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
02715             (long) (time(NULL) - callstart));
02716       } else if (qe->chan->_softhangup) {
02717          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
02718             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02719          if (qe->parent->eventwhencalled)
02720             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02721                   "Queue: %s\r\n"
02722                   "Uniqueid: %s\r\n"
02723                   "Channel: %s\r\n"
02724                   "Member: %s\r\n"
02725                   "MemberName: %s\r\n"
02726                   "HoldTime: %ld\r\n"
02727                   "TalkTime: %ld\r\n"
02728                   "Reason: caller\r\n"
02729                   "%s",
02730                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02731                   (long)(callstart - qe->start), (long)(time(NULL) - callstart),
02732                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02733       } else {
02734          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
02735             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02736          if (qe->parent->eventwhencalled)
02737             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02738                   "Queue: %s\r\n"
02739                   "Uniqueid: %s\r\n"
02740                   "Channel: %s\r\n"
02741                   "MemberName: %s\r\n"
02742                   "HoldTime: %ld\r\n"
02743                   "TalkTime: %ld\r\n"
02744                   "Reason: agent\r\n"
02745                   "%s",
02746                   queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
02747                   (long)(time(NULL) - callstart),
02748                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02749       }
02750 
02751       if (bridge != AST_PBX_NO_HANGUP_PEER)
02752          ast_hangup(peer);
02753       update_queue(qe->parent, member, callcompletedinsl);
02754       res = bridge ? bridge : 1;
02755    }
02756 out:
02757    hangupcalls(outgoing, NULL);
02758 
02759    return res;
02760 }
02761 
02762 static int wait_a_bit(struct queue_ent *qe)
02763 {
02764    /* Don't need to hold the lock while we setup the outgoing calls */
02765    int retrywait = qe->parent->retry * 1000;
02766 
02767    int res = ast_waitfordigit(qe->chan, retrywait);
02768    if (res > 0 && !valid_exit(qe, res))
02769       res = 0;
02770 
02771    return res;
02772 }
02773 
02774 static struct member *interface_exists(struct call_queue *q, const char *interface)
02775 {
02776    struct member *mem;
02777 
02778    if (!q)
02779       return NULL;
02780 
02781    for (mem = q->members; mem; mem = mem->next) {
02782       if (!strcasecmp(interface, mem->interface))
02783          return mem;
02784    }
02785 
02786    return NULL;
02787 }
02788 
02789 
02790 /* Dump all members in a specific queue to the database
02791  *
02792  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
02793  *
02794  */
02795 static void dump_queue_members(struct call_queue *pm_queue)
02796 {
02797    struct member *cur_member;
02798    char value[PM_MAX_LEN];
02799    int value_len = 0;
02800    int res;
02801 
02802    memset(value, 0, sizeof(value));
02803 
02804    if (!pm_queue)
02805       return;
02806 
02807    for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
02808       if (!cur_member->dynamic)
02809          continue;
02810 
02811       res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s",
02812          cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername,
02813          cur_member->next ? "|" : "");
02814       if (res != strlen(value + value_len)) {
02815          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
02816          break;
02817       }
02818       value_len += res;
02819    }
02820    
02821    if (value_len && !cur_member) {
02822       if (ast_db_put(pm_family, pm_queue->name, value))
02823          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
02824    } else
02825       /* Delete the entry if the queue is empty or there is an error */
02826       ast_db_del(pm_family, pm_queue->name);
02827 }
02828 
02829 static int remove_from_queue(const char *queuename, const char *interface)
02830 {
02831    struct call_queue *q;
02832    struct member *last_member, *look;
02833    int res = RES_NOSUCHQUEUE;
02834 
02835    AST_LIST_LOCK(&queues);
02836    AST_LIST_TRAVERSE(&queues, q, list) {
02837       ast_mutex_lock(&q->lock);
02838       if (strcmp(q->name, queuename)) {
02839          ast_mutex_unlock(&q->lock);
02840          continue;
02841       }
02842 
02843       if ((last_member = interface_exists(q, interface))) {
02844          if ((look = q->members) == last_member) {
02845             q->members = last_member->next;
02846             q->membercount--;
02847          } else {
02848             while (look != NULL) {
02849                if (look->next == last_member) {
02850                   look->next = last_member->next;
02851                   q->membercount--;
02852                   break;
02853                } else {
02854                   look = look->next;
02855                }
02856             }
02857          }
02858          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
02859             "Queue: %s\r\n"
02860             "Location: %s\r\n"
02861             "MemberName: %s\r\n",
02862             q->name, last_member->interface, last_member->membername);
02863          free(last_member);
02864          
02865          if (queue_persistent_members)
02866             dump_queue_members(q);
02867          
02868          res = RES_OKAY;
02869       } else {
02870          res = RES_EXISTS;
02871       }
02872       ast_mutex_unlock(&q->lock);
02873       break;
02874    }
02875 
02876    if (res == RES_OKAY)
02877       remove_from_interfaces(interface);
02878 
02879    AST_LIST_UNLOCK(&queues);
02880 
02881    return res;
02882 }
02883 
02884 
02885 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
02886 {
02887    struct call_queue *q;
02888    struct member *new_member;
02889    int res = RES_NOSUCHQUEUE;
02890 
02891    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
02892     * short-circuits if the queue is already in memory. */
02893    if (!(q = load_realtime_queue(queuename)))
02894       return res;
02895 
02896    AST_LIST_LOCK(&queues);
02897 
02898    ast_mutex_lock(&q->lock);
02899    if (interface_exists(q, interface) == NULL) {
02900       add_to_interfaces(interface);
02901       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
02902          new_member->dynamic = 1;
02903          new_member->next = q->members;
02904          q->members = new_member;
02905          q->membercount++;
02906          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
02907             "Queue: %s\r\n"
02908             "Location: %s\r\n"
02909             "MemberName: %s\r\n"
02910             "Membership: %s\r\n"
02911             "Penalty: %d\r\n"
02912             "CallsTaken: %d\r\n"
02913             "LastCall: %d\r\n"
02914             "Status: %d\r\n"
02915             "Paused: %d\r\n",
02916             q->name, new_member->interface, new_member->membername,
02917             new_member->dynamic ? "dynamic" : "static",
02918             new_member->penalty, new_member->calls, (int) new_member->lastcall,
02919             new_member->status, new_member->paused);
02920          
02921          if (dump)
02922             dump_queue_members(q);
02923          
02924          res = RES_OKAY;
02925       } else {
02926          res = RES_OUTOFMEMORY;
02927       }
02928    } else {
02929       res = RES_EXISTS;
02930    }
02931    ast_mutex_unlock(&q->lock);
02932    AST_LIST_UNLOCK(&queues);
02933 
02934    return res;
02935 }
02936 
02937 static int set_member_paused(const char *queuename, const char *interface, int paused)
02938 {
02939    int found = 0;
02940    struct call_queue *q;
02941    struct member *mem;
02942 
02943    /* Special event for when all queues are paused - individual events still generated */
02944    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
02945    if (ast_strlen_zero(queuename))
02946       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
02947 
02948    AST_LIST_LOCK(&queues);
02949    AST_LIST_TRAVERSE(&queues, q, list) {
02950       ast_mutex_lock(&q->lock);
02951       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
02952          if ((mem = interface_exists(q, interface))) {
02953             found++;
02954             if (mem->paused == paused)
02955                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
02956             mem->paused = paused;
02957 
02958             if (queue_persistent_members)
02959                dump_queue_members(q);
02960 
02961             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
02962 
02963             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
02964                "Queue: %s\r\n"
02965                "Location: %s\r\n"
02966                "MemberName: %s\r\n"
02967                "Paused: %d\r\n",
02968                   q->name, mem->interface, mem->membername, paused);
02969          }
02970       }
02971       ast_mutex_unlock(&q->lock);
02972    }
02973    AST_LIST_UNLOCK(&queues);
02974 
02975    return found ? RESULT_SUCCESS : RESULT_FAILURE;
02976 }
02977 
02978 /* Reload dynamic queue members persisted into the astdb */
02979 static void reload_queue_members(void)
02980 {
02981    char *cur_ptr; 
02982    char *queue_name;
02983    char *member;
02984    char *interface;
02985    char *membername;
02986    char *penalty_tok;
02987    int penalty = 0;
02988    char *paused_tok;
02989    int paused = 0;
02990    struct ast_db_entry *db_tree;
02991    struct ast_db_entry *entry;
02992    struct call_queue *cur_queue;
02993    char queue_data[PM_MAX_LEN];
02994 
02995    AST_LIST_LOCK(&queues);
02996 
02997    /* Each key in 'pm_family' is the name of a queue */
02998    db_tree = ast_db_gettree(pm_family, NULL);
02999    for (entry = db_tree; entry; entry = entry->next) {
03000 
03001       queue_name = entry->key + strlen(pm_family) + 2;
03002 
03003       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03004          ast_mutex_lock(&cur_queue->lock);
03005          if (!strcmp(queue_name, cur_queue->name))
03006             break;
03007          ast_mutex_unlock(&cur_queue->lock);
03008       }
03009       
03010       if (!cur_queue)
03011          cur_queue = load_realtime_queue(queue_name);
03012 
03013       if (!cur_queue) {
03014          /* If the queue no longer exists, remove it from the
03015           * database */
03016          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03017          ast_db_del(pm_family, queue_name);
03018          continue;
03019       } else
03020          ast_mutex_unlock(&cur_queue->lock);
03021 
03022       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03023          continue;
03024 
03025       cur_ptr = queue_data;
03026       while ((member = strsep(&cur_ptr, "|"))) {
03027          if (ast_strlen_zero(member))
03028             continue;
03029 
03030          interface = strsep(&member, ";");
03031          penalty_tok = strsep(&member, ";");
03032          paused_tok = strsep(&member, ";");
03033          membername = strsep(&member, ";");
03034 
03035          if (!penalty_tok) {
03036             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03037             break;
03038          }
03039          penalty = strtol(penalty_tok, NULL, 10);
03040          if (errno == ERANGE) {
03041             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03042             break;
03043          }
03044          
03045          if (!paused_tok) {
03046             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03047             break;
03048          }
03049          paused = strtol(paused_tok, NULL, 10);
03050          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03051             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03052             break;
03053          }
03054          if (ast_strlen_zero(membername))
03055             membername = interface;
03056 
03057          if (option_debug)
03058             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03059          
03060          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03061             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03062             break;
03063          }
03064       }
03065    }
03066 
03067    AST_LIST_UNLOCK(&queues);
03068    if (db_tree) {
03069       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03070       ast_db_freetree(db_tree);
03071    }
03072 }
03073 
03074 static int pqm_exec(struct ast_channel *chan, void *data)
03075 {
03076    struct ast_module_user *lu;
03077    char *parse;
03078    int priority_jump = 0;
03079    AST_DECLARE_APP_ARGS(args,
03080       AST_APP_ARG(queuename);
03081       AST_APP_ARG(interface);
03082       AST_APP_ARG(options);
03083    );
03084 
03085    if (ast_strlen_zero(data)) {
03086       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03087       return -1;
03088    }
03089 
03090    parse = ast_strdupa(data);
03091 
03092    AST_STANDARD_APP_ARGS(args, parse);
03093 
03094    lu = ast_module_user_add(chan);
03095 
03096    if (args.options) {
03097       if (strchr(args.options, 'j'))
03098          priority_jump = 1;
03099    }
03100 
03101    if (ast_strlen_zero(args.interface)) {
03102       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03103       ast_module_user_remove(lu);
03104       return -1;
03105    }
03106 
03107    if (set_member_paused(args.queuename, args.interface, 1)) {
03108       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03109       if (priority_jump || ast_opt_priority_jumping) {
03110          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03111             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03112             ast_module_user_remove(lu);
03113             return 0;
03114          }
03115       }
03116       ast_module_user_remove(lu);
03117       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03118       return -1;
03119    }
03120 
03121    ast_module_user_remove(lu);
03122    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03123 
03124    return 0;
03125 }
03126 
03127 static int upqm_exec(struct ast_channel *chan, void *data)
03128 {
03129    struct ast_module_user *lu;
03130    char *parse;
03131    int priority_jump = 0;
03132    AST_DECLARE_APP_ARGS(args,
03133       AST_APP_ARG(queuename);
03134       AST_APP_ARG(interface);
03135       AST_APP_ARG(options);
03136    );
03137 
03138    if (ast_strlen_zero(data)) {
03139       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03140       return -1;
03141    }
03142 
03143    parse = ast_strdupa(data);
03144 
03145    AST_STANDARD_APP_ARGS(args, parse);
03146 
03147    lu = ast_module_user_add(chan);
03148 
03149    if (args.options) {
03150       if (strchr(args.options, 'j'))
03151          priority_jump = 1;
03152    }
03153 
03154    if (ast_strlen_zero(args.interface)) {
03155       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03156       ast_module_user_remove(lu);
03157       return -1;
03158    }
03159 
03160    if (set_member_paused(args.queuename, args.interface, 0)) {
03161       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03162       if (priority_jump || ast_opt_priority_jumping) {
03163          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03164             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03165             ast_module_user_remove(lu);
03166             return 0;
03167          }
03168       }
03169       ast_module_user_remove(lu);
03170       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03171       return -1;
03172    }
03173 
03174    ast_module_user_remove(lu);
03175    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03176 
03177    return 0;
03178 }
03179 
03180 static int rqm_exec(struct ast_channel *chan, void *data)
03181 {
03182    int res=-1;
03183    struct ast_module_user *lu;
03184    char *parse, *temppos = NULL;
03185    int priority_jump = 0;
03186    AST_DECLARE_APP_ARGS(args,
03187       AST_APP_ARG(queuename);
03188       AST_APP_ARG(interface);
03189       AST_APP_ARG(options);
03190    );
03191 
03192 
03193    if (ast_strlen_zero(data)) {
03194       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03195       return -1;
03196    }
03197 
03198    parse = ast_strdupa(data);
03199 
03200    AST_STANDARD_APP_ARGS(args, parse);
03201 
03202    lu = ast_module_user_add(chan);
03203 
03204    if (ast_strlen_zero(args.interface)) {
03205       args.interface = ast_strdupa(chan->name);
03206       temppos = strrchr(args.interface, '-');
03207       if (temppos)
03208          *temppos = '\0';
03209    }
03210 
03211    if (args.options) {
03212       if (strchr(args.options, 'j'))
03213          priority_jump = 1;
03214    }
03215 
03216    switch (remove_from_queue(args.queuename, args.interface)) {
03217    case RES_OKAY:
03218       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03219       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03220       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03221       res = 0;
03222       break;
03223    case RES_EXISTS:
03224       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03225       if (priority_jump || ast_opt_priority_jumping)
03226          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03227       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03228       res = 0;
03229       break;
03230    case RES_NOSUCHQUEUE:
03231       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03232       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03233       res = 0;
03234       break;
03235    }
03236 
03237    ast_module_user_remove(lu);
03238 
03239    return res;
03240 }
03241 
03242 static int aqm_exec(struct ast_channel *chan, void *data)
03243 {
03244    int res=-1;
03245    struct ast_module_user *lu;
03246    char *parse, *temppos = NULL;
03247    int priority_jump = 0;
03248    AST_DECLARE_APP_ARGS(args,
03249       AST_APP_ARG(queuename);
03250       AST_APP_ARG(interface);
03251       AST_APP_ARG(penalty);
03252       AST_APP_ARG(options);
03253       AST_APP_ARG(membername);
03254    );
03255    int penalty = 0;
03256 
03257    if (ast_strlen_zero(data)) {
03258       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03259       return -1;
03260    }
03261 
03262    parse = ast_strdupa(data);
03263 
03264    AST_STANDARD_APP_ARGS(args, parse);
03265 
03266    lu = ast_module_user_add(chan);
03267 
03268    if (ast_strlen_zero(args.interface)) {
03269       args.interface = ast_strdupa(chan->name);
03270       temppos = strrchr(args.interface, '-');
03271       if (temppos)
03272          *temppos = '\0';
03273    }
03274 
03275    if (!ast_strlen_zero(args.penalty)) {
03276       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03277          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03278          penalty = 0;
03279       }
03280    }
03281    
03282    if (args.options) {
03283       if (strchr(args.options, 'j'))
03284          priority_jump = 1;
03285    }
03286 
03287    if (ast_strlen_zero(args.membername))
03288       args.membername = args.interface;
03289 
03290 
03291    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03292    case RES_OKAY:
03293       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03294       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03295       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03296       res = 0;
03297       break;
03298    case RES_EXISTS:
03299       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03300       if (priority_jump || ast_opt_priority_jumping)
03301          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03302       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03303       res = 0;
03304       break;
03305    case RES_NOSUCHQUEUE:
03306       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03307       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03308       res = 0;
03309       break;
03310    case RES_OUTOFMEMORY:
03311       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03312       break;
03313    }
03314 
03315    ast_module_user_remove(lu);
03316 
03317    return res;
03318 }
03319 
03320 static int ql_exec(struct ast_channel *chan, void *data)
03321 {
03322    struct ast_module_user *u;
03323    char *parse;
03324 
03325    AST_DECLARE_APP_ARGS(args,
03326       AST_APP_ARG(queuename);
03327       AST_APP_ARG(uniqueid);
03328       AST_APP_ARG(membername);
03329       AST_APP_ARG(event);
03330       AST_APP_ARG(params);
03331    );
03332 
03333    if (ast_strlen_zero(data)) {
03334       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03335       return -1;
03336    }
03337 
03338    u = ast_module_user_add(chan);
03339 
03340    parse = ast_strdupa(data);
03341 
03342    AST_STANDARD_APP_ARGS(args, parse);
03343 
03344    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03345        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03346       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03347       ast_module_user_remove(u);
03348       return -1;
03349    }
03350 
03351    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03352       "%s", args.params ? args.params : "");
03353 
03354    ast_module_user_remove(u);
03355 
03356    return 0;
03357 }
03358 
03359 static int queue_exec(struct ast_channel *chan, void *data)
03360 {
03361    int res=-1;
03362    int ringing=0;
03363    struct ast_module_user *lu;
03364    const char *user_priority;
03365    const char *max_penalty_str;
03366    int prio;
03367    int max_penalty;
03368    enum queue_result reason = QUEUE_UNKNOWN;
03369    /* whether to exit Queue application after the timeout hits */
03370    int go_on = 0;
03371    char *parse;
03372    AST_DECLARE_APP_ARGS(args,
03373       AST_APP_ARG(queuename);
03374       AST_APP_ARG(options);
03375       AST_APP_ARG(url);
03376       AST_APP_ARG(announceoverride);
03377       AST_APP_ARG(queuetimeoutstr);
03378       AST_APP_ARG(agi);
03379    );
03380    /* Our queue entry */
03381    struct queue_ent qe;
03382    
03383    if (ast_strlen_zero(data)) {
03384       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03385       return -1;
03386    }
03387    
03388    parse = ast_strdupa(data);
03389    AST_STANDARD_APP_ARGS(args, parse);
03390 
03391    lu = ast_module_user_add(chan);
03392 
03393    /* Setup our queue entry */
03394    memset(&qe, 0, sizeof(qe));
03395    qe.start = time(NULL);
03396 
03397    /* set the expire time based on the supplied timeout; */
03398    if (args.queuetimeoutstr)
03399       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03400    else
03401       qe.expire = 0;
03402 
03403    /* Get the priority from the variable ${QUEUE_PRIO} */
03404    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03405    if (user_priority) {
03406       if (sscanf(user_priority, "%d", &prio) == 1) {
03407          if (option_debug)
03408             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03409                chan->name, prio);
03410       } else {
03411          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03412             user_priority, chan->name);
03413          prio = 0;
03414       }
03415    } else {
03416       if (option_debug > 2)
03417          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03418       prio = 0;
03419    }
03420 
03421    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03422    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03423       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03424          if (option_debug)
03425             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03426                chan->name, max_penalty);
03427       } else {
03428          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03429             max_penalty_str, chan->name);
03430          max_penalty = 0;
03431       }
03432    } else {
03433       max_penalty = 0;
03434    }
03435 
03436    if (args.options && (strchr(args.options, 'r')))
03437       ringing = 1;
03438 
03439    if (option_debug)
03440       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03441          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03442 
03443    qe.chan = chan;
03444    qe.prio = prio;
03445    qe.max_penalty = max_penalty;
03446    qe.last_pos_said = 0;
03447    qe.last_pos = 0;
03448    qe.last_periodic_announce_time = time(NULL);
03449    qe.last_periodic_announce_sound = 0;
03450    qe.valid_digits = 0;
03451    if (!join_queue(args.queuename, &qe, &reason)) {
03452       int makeannouncement = 0;
03453 
03454       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03455          S_OR(chan->cid.cid_num, ""));
03456 check_turns:
03457       if (ringing) {
03458          ast_indicate(chan, AST_CONTROL_RINGING);
03459       } else {
03460          ast_moh_start(chan, qe.moh, NULL);
03461       }
03462 
03463       /* This is the wait loop for callers 2 through maxlen */
03464       res = wait_our_turn(&qe, ringing, &reason);
03465       if (res)
03466          goto stop;
03467 
03468       for (;;) {
03469          /* This is the wait loop for the head caller*/
03470          /* To exit, they may get their call answered; */
03471          /* they may dial a digit from the queue context; */
03472          /* or, they may timeout. */
03473 
03474          enum queue_member_status stat;
03475 
03476          /* Leave if we have exceeded our queuetimeout */
03477          if (qe.expire && (time(NULL) > qe.expire)) {
03478             record_abandoned(&qe);
03479             reason = QUEUE_TIMEOUT;
03480             res = 0;
03481             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03482             break;
03483          }
03484 
03485          if (makeannouncement) {
03486             /* Make a position announcement, if enabled */
03487             if (qe.parent->announcefrequency && !ringing)
03488                if ((res = say_position(&qe)))
03489                   goto stop;
03490 
03491          }
03492          makeannouncement = 1;
03493 
03494          /* Make a periodic announcement, if enabled */
03495          if (qe.parent->periodicannouncefrequency && !ringing)
03496             if ((res = say_periodic_announcement(&qe)))
03497                goto stop;
03498 
03499          /* Try calling all queue members for 'timeout' seconds */
03500          res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi);
03501          if (res)
03502             goto stop;
03503 
03504          stat = get_member_status(qe.parent, qe.max_penalty);
03505 
03506          /* exit after 'timeout' cycle if 'n' option enabled */
03507          if (go_on >= qe.parent->membercount) {
03508             if (option_verbose > 2)
03509                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03510             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03511             record_abandoned(&qe);
03512             reason = QUEUE_TIMEOUT;
03513             res = 0;
03514             break;
03515          }
03516 
03517          /* leave the queue if no agents, if enabled */
03518          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03519             record_abandoned(&qe);
03520             reason = QUEUE_LEAVEEMPTY;
03521             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03522             res = 0;
03523             break;
03524          }
03525 
03526          /* leave the queue if no reachable agents, if enabled */
03527          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03528             record_abandoned(&qe);
03529             reason = QUEUE_LEAVEUNAVAIL;
03530             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03531             res = 0;
03532             break;
03533          }
03534 
03535          /* Leave if we have exceeded our queuetimeout */
03536          if (qe.expire && (time(NULL) > qe.expire)) {
03537             record_abandoned(&qe);
03538             reason = QUEUE_TIMEOUT;
03539             res = 0;
03540             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03541             break;
03542          }
03543 
03544          /* If using dynamic realtime members, we should regenerate the member list for this queue */
03545          update_realtime_members(qe.parent);
03546 
03547          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03548          res = wait_a_bit(&qe);
03549          if (res)
03550             goto stop;
03551 
03552 
03553          /* Since this is a priority queue and
03554           * it is not sure that we are still at the head
03555           * of the queue, go and check for our turn again.
03556           */
03557          if (!is_our_turn(&qe)) {
03558             if (option_debug)
03559                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03560                   qe.chan->name);
03561             goto check_turns;
03562          }
03563       }
03564 
03565 stop:
03566       if (res) {
03567          if (res < 0) {
03568             if (!qe.handled) {
03569                record_abandoned(&qe);
03570                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03571                   "%d|%d|%ld", qe.pos, qe.opos,
03572                   (long) time(NULL) - qe.start);
03573             }
03574             res = -1;
03575          } else if (qe.valid_digits) {
03576             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03577                "%s|%d", qe.digits, qe.pos);
03578          }
03579       }
03580 
03581       /* Don't allow return code > 0 */
03582       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03583          res = 0; 
03584          if (ringing) {
03585             ast_indicate(chan, -1);
03586          } else {
03587             ast_moh_stop(chan);
03588          }        
03589          ast_stopstream(chan);
03590       }
03591       leave_queue(&qe);
03592       if (reason != QUEUE_UNKNOWN)
03593          set_queue_result(chan, reason);
03594    } else {
03595       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03596       set_queue_result(chan, reason);
03597       res = 0;
03598    }
03599    ast_module_user_remove(lu);
03600 
03601    return res;
03602 }
03603 
03604 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03605 {
03606    int count = 0;
03607    struct call_queue *q;
03608    struct ast_module_user *lu;
03609 
03610    buf[0] = '\0';
03611    
03612    if (ast_strlen_zero(data)) {
03613       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03614       return -1;
03615    }
03616 
03617    lu = ast_module_user_add(chan);
03618    
03619    AST_LIST_LOCK(&queues);
03620    AST_LIST_TRAVERSE(&queues, q, list) {
03621       if (!strcasecmp(q->name, data)) {
03622          ast_mutex_lock(&q->lock);
03623          break;
03624       }
03625    }
03626    AST_LIST_UNLOCK(&queues);
03627 
03628    if (q) {
03629       count = q->membercount;
03630       ast_mutex_unlock(&q->lock);
03631    } else
03632       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03633 
03634    snprintf(buf, len, "%d", count);
03635    ast_module_user_remove(lu);
03636 
03637    return 0;
03638 }
03639 
03640 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03641 {
03642    int count = 0;
03643    struct call_queue *q;
03644    struct ast_module_user *lu;
03645 
03646    buf[0] = '\0';
03647    
03648    if (ast_strlen_zero(data)) {
03649       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03650       return -1;
03651    }
03652 
03653    lu = ast_module_user_add(chan);
03654    
03655    AST_LIST_LOCK(&queues);
03656    AST_LIST_TRAVERSE(&queues, q, list) {
03657       if (!strcasecmp(q->name, data)) {
03658          ast_mutex_lock(&q->lock);
03659          break;
03660       }
03661    }
03662    AST_LIST_UNLOCK(&queues);
03663 
03664    if (q) {
03665       count = q->count;
03666       ast_mutex_unlock(&q->lock);
03667    } else
03668       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03669 
03670    snprintf(buf, len, "%d", count);
03671    ast_module_user_remove(lu);
03672    return 0;
03673 }
03674 
03675 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03676 {
03677    struct ast_module_user *u;
03678    struct call_queue *q;
03679    struct member *m;
03680 
03681    /* Ensure an otherwise empty list doesn't return garbage */
03682    buf[0] = '\0';
03683 
03684    if (ast_strlen_zero(data)) {
03685       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
03686       return -1;
03687    }
03688    
03689    u = ast_module_user_add(chan);
03690 
03691    AST_LIST_LOCK(&queues);
03692    AST_LIST_TRAVERSE(&queues, q, list) {
03693       if (!strcasecmp(q->name, data)) {
03694          ast_mutex_lock(&q->lock);
03695          break;
03696       }
03697    }
03698    AST_LIST_UNLOCK(&queues);
03699 
03700    if (q) {
03701       int buflen = 0, count = 0;
03702 
03703       for (m = q->members; m; m = m->next) {
03704          /* strcat() is always faster than printf() */
03705          if (count++) {
03706             strncat(buf + buflen, ",", len - buflen - 1);
03707             buflen++;
03708          }
03709          strncat(buf + buflen, m->interface, len - buflen - 1);
03710          buflen += strlen(m->interface);
03711          /* Safeguard against overflow (negative length) */
03712          if (buflen >= len - 2) {
03713             ast_log(LOG_WARNING, "Truncating list\n");
03714             break;
03715          }
03716       }
03717       ast_mutex_unlock(&q->lock);
03718    } else
03719       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03720 
03721    /* We should already be terminated, but let's make sure. */
03722    buf[len - 1] = '\0';
03723    ast_module_user_remove(u);
03724 
03725    return 0;
03726 }
03727 
03728 static struct ast_custom_function queueagentcount_function = {
03729    .name = "QUEUEAGENTCOUNT",
03730    .synopsis = "Count number of agents answering a queue",
03731    .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03732    .desc =
03733 "Returns the number of members currently associated with the specified queue.\n"
03734 "This function is deprecated.  You should use QUEUE_MEMBER_COUNT() instead.\n",
03735    .read = queue_function_qac,
03736 };
03737 
03738 static struct ast_custom_function queuemembercount_function = {
03739    .name = "QUEUE_MEMBER_COUNT",
03740    .synopsis = "Count number of members answering a queue",
03741    .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
03742    .desc =
03743 "Returns the number of members currently associated with the specified queue.\n",
03744    .read = queue_function_qac,
03745 };
03746 
03747 static struct ast_custom_function queuewaitingcount_function = {
03748    .name = "QUEUE_WAITING_COUNT",
03749    .synopsis = "Count number of calls currently waiting in a queue",
03750    .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
03751    .desc =
03752 "Returns the number of callers currently waiting in the specified queue.\n",
03753    .read = queue_function_queuewaitingcount,
03754 };
03755 
03756 static struct ast_custom_function queuememberlist_function = {
03757    .name = "QUEUE_MEMBER_LIST",
03758    .synopsis = "Returns a list of interfaces on a queue",
03759    .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
03760    .desc =
03761 "Returns a comma-separated list of members associated with the specified queue.\n",
03762    .read = queue_function_queuememberlist,
03763 };
03764 
03765 static int reload_queues(void)
03766 {
03767    struct call_queue *q;
03768    struct ast_config *cfg;
03769    char *cat, *tmp;
03770    struct ast_variable *var;
03771    struct member *prev, *cur, *newm, *next;
03772    int new;
03773    const char *general_val = NULL;
03774    char parse[80];
03775    char *interface;
03776    char *membername;
03777    int penalty;
03778    AST_DECLARE_APP_ARGS(args,
03779       AST_APP_ARG(interface);
03780       AST_APP_ARG(penalty);
03781       AST_APP_ARG(membername);
03782    );
03783    
03784    if (!(cfg = ast_config_load("queues.conf"))) {
03785       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03786       return 0;
03787    }
03788    AST_LIST_LOCK(&queues);
03789    use_weight=0;
03790    /* Mark all queues as dead for the moment */
03791    AST_LIST_TRAVERSE(&queues, q, list)
03792       q->dead = 1;
03793 
03794    /* Chug through config file */
03795    cat = NULL;
03796    while ((cat = ast_category_browse(cfg, cat)) ) {
03797       if (!strcasecmp(cat, "general")) {  
03798          /* Initialize global settings */
03799          queue_persistent_members = 0;
03800          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03801             queue_persistent_members = ast_true(general_val);
03802          autofill_default = 0;
03803          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
03804             autofill_default = ast_true(general_val);
03805          montype_default = 0;
03806          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
03807             if (!strcasecmp(general_val, "mixmonitor"))
03808                montype_default = 1;
03809       } else { /* Define queue */
03810          /* Look for an existing one */
03811          AST_LIST_TRAVERSE(&queues, q, list) {
03812             if (!strcmp(q->name, cat))
03813                break;
03814          }
03815          if (!q) {
03816             /* Make one then */
03817             if (!(q = alloc_queue(cat))) {
03818                /* TODO: Handle memory allocation failure */
03819             }
03820             new = 1;
03821          } else
03822             new = 0;
03823          if (q) {
03824             if (!new)
03825                ast_mutex_lock(&q->lock);
03826             /* Re-initialize the queue, and clear statistics */
03827             init_queue(q);
03828             clear_queue(q);
03829             for (cur = q->members; cur; cur = cur->next) {
03830                if (!cur->dynamic) {
03831                   cur->delme = 1;
03832                }
03833             }
03834             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
03835                if (!strcasecmp(var->name, "member")) {
03836                   /* Add a new member */
03837                   ast_copy_string(parse, var->value, sizeof(parse));
03838                   
03839                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
03840 
03841                   interface = args.interface;
03842                   if(!ast_strlen_zero(args.penalty)) {
03843                      tmp = args.penalty;
03844                      while (*tmp && *tmp < 33) tmp++;
03845                      penalty = atoi(tmp);
03846                      if (penalty < 0) {
03847                         penalty = 0;
03848                      }
03849                   } else
03850                      penalty = 0;
03851 
03852                   if (!ast_strlen_zero(args.membername)) {
03853                      membername = args.membername;
03854                      while (*membername && *membername < 33) membername++;
03855                   } else
03856                      membername = interface;
03857 
03858                   /* Find the old position in the list */
03859                   for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
03860                      if (!strcmp(cur->interface, interface)) {
03861                         break;
03862                      }
03863                   }
03864 
03865                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
03866 
03867                   if (cur) {
03868                      /* Delete it now */
03869                      newm->next = cur->next;
03870                      if (prev) {
03871                         prev->next = newm;
03872                      } else {
03873                         q->members = newm;
03874                      }
03875                      free(cur);
03876                   } else {
03877                      /* Add them to the master int list if necessary */
03878                      add_to_interfaces(interface);
03879                      newm->next = q->members;
03880                      q->members = newm;
03881                   }
03882                   q->membercount++;
03883                } else {
03884                   queue_set_param(q, var->name, var->value, var->lineno, 1);
03885                }
03886             }
03887 
03888             /* Free remaining members marked as delme */
03889             for (prev = NULL, cur = q->members;
03890                  cur;
03891                  cur = next) {
03892                next = cur->next;
03893 
03894                if (!cur->delme) {
03895                   prev = cur;
03896                   continue;
03897                }
03898 
03899                if (prev)
03900                   prev->next = next;
03901                else
03902                   q->members = next;
03903 
03904                remove_from_interfaces(cur->interface);
03905                q->membercount--;
03906                free(cur);
03907             }
03908 
03909             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
03910                rr_dep_warning();
03911 
03912             if (new) {
03913                AST_LIST_INSERT_HEAD(&queues, q, list);
03914             } else
03915                ast_mutex_unlock(&q->lock);
03916          }
03917       }
03918    }
03919    ast_config_destroy(cfg);
03920    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
03921       if (q->dead) {
03922          AST_LIST_REMOVE_CURRENT(&queues, list);
03923          if (!q->count)
03924             destroy_queue(q);
03925          else
03926             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
03927       } else {
03928          ast_mutex_lock(&q->lock);
03929          for (cur = q->members; cur; cur = cur->next)
03930             cur->status = ast_device_state(cur->interface);
03931          ast_mutex_unlock(&q->lock);
03932       }
03933    }
03934    AST_LIST_TRAVERSE_SAFE_END;
03935    AST_LIST_UNLOCK(&queues);
03936    return 1;
03937 }
03938 
03939 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
03940 {
03941    struct call_queue *q;
03942    struct queue_ent *qe;
03943    struct member *mem;
03944    int pos, queue_show;
03945    time_t now;
03946    char max_buf[80];
03947    char *max;
03948    size_t max_left;
03949    float sl = 0;
03950    char *term = manager ? "\r\n" : "\n";
03951 
03952    time(&now);
03953    if (argc == 2)
03954       queue_show = 0;
03955    else if (argc == 3)
03956       queue_show = 1;
03957    else
03958       return RESULT_SHOWUSAGE;
03959 
03960    /* We only want to load realtime queues when a specific queue is asked for. */
03961    if (queue_show)
03962       load_realtime_queue(argv[2]);
03963 
03964    AST_LIST_LOCK(&queues);
03965    if (AST_LIST_EMPTY(&queues)) {
03966       AST_LIST_UNLOCK(&queues);
03967       if (queue_show) {
03968          if (s)
03969             astman_append(s, "No such queue: %s.%s",argv[2], term);
03970          else
03971             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03972       } else {
03973          if (s)
03974             astman_append(s, "No queues.%s", term);
03975          else
03976             ast_cli(fd, "No queues.%s", term);
03977       }
03978       return RESULT_SUCCESS;
03979    }
03980    AST_LIST_TRAVERSE(&queues, q, list) {
03981       ast_mutex_lock(&q->lock);
03982       if (queue_show) {
03983          if (strcasecmp(q->name, argv[2]) != 0) {
03984             ast_mutex_unlock(&q->lock);
03985             if (!AST_LIST_NEXT(q, list)) {
03986                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03987                break;
03988             }
03989             continue;
03990          }
03991       }
03992       max_buf[0] = '\0';
03993       max = max_buf;
03994       max_left = sizeof(max_buf);
03995       if (q->maxlen)
03996          ast_build_string(&max, &max_left, "%d", q->maxlen);
03997       else
03998          ast_build_string(&max, &max_left, "unlimited");
03999       sl = 0;
04000       if (q->callscompleted > 0)
04001          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04002       if (s)
04003          astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
04004             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
04005             q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04006       else
04007          ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
04008             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04009       if (q->members) {
04010          if (s)
04011             astman_append(s, "   Members: %s", term);
04012          else
04013             ast_cli(fd, "   Members: %s", term);
04014          for (mem = q->members; mem; mem = mem->next) {
04015             max_buf[0] = '\0';
04016             max = max_buf;
04017             max_left = sizeof(max_buf);
04018             if (mem->penalty)
04019                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04020             if (mem->dynamic)
04021                ast_build_string(&max, &max_left, " (dynamic)");
04022             if (mem->paused)
04023                ast_build_string(&max, &max_left, " (paused)");
04024             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04025             if (mem->calls) {
04026                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04027                   mem->calls, (long) (time(NULL) - mem->lastcall));
04028             } else
04029                ast_build_string(&max, &max_left, " has taken no calls yet");
04030             if (s)
04031                astman_append(s, "      %s%s%s", mem->interface, max_buf, term);
04032             else
04033                ast_cli(fd, "      %s%s%s", mem->interface, max_buf, term);
04034          }
04035       } else if (s)
04036          astman_append(s, "   No Members%s", term);
04037       else  
04038          ast_cli(fd, "   No Members%s", term);
04039       if (q->head) {
04040          pos = 1;
04041          if (s)
04042             astman_append(s, "   Callers: %s", term);
04043          else
04044             ast_cli(fd, "   Callers: %s", term);
04045          for (qe = q->head; qe; qe = qe->next) {
04046             if (s)
04047                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04048                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04049                   (long) (now - qe->start) % 60, qe->prio, term);
04050             else
04051                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04052                   qe->chan->name, (long) (now - qe->start) / 60,
04053                   (long) (now - qe->start) % 60, qe->prio, term);
04054          }
04055       } else if (s)
04056          astman_append(s, "   No Callers%s", term);
04057       else
04058          ast_cli(fd, "   No Callers%s", term);
04059       if (s)
04060          astman_append(s, "%s", term);
04061       else
04062          ast_cli(fd, "%s", term);
04063       ast_mutex_unlock(&q->lock);
04064       if (queue_show)
04065          break;
04066    }
04067    AST_LIST_UNLOCK(&queues);
04068    return RESULT_SUCCESS;
04069 }
04070 
04071 static int queue_show(int fd, int argc, char **argv)
04072 {
04073    return __queues_show(NULL, 0, fd, argc, argv);
04074 }
04075 
04076 static char *complete_queue(const char *line, const char *word, int pos, int state)
04077 {
04078    struct call_queue *q;
04079    char *ret = NULL;
04080    int which = 0;
04081    int wordlen = strlen(word);
04082    
04083    AST_LIST_LOCK(&queues);
04084    AST_LIST_TRAVERSE(&queues, q, list) {
04085       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04086          ret = ast_strdup(q->name); 
04087          break;
04088       }
04089    }
04090    AST_LIST_UNLOCK(&queues);
04091 
04092    return ret;
04093 }
04094 
04095 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
04096 {
04097    if (pos == 2)
04098       return complete_queue(line, word, pos, state);
04099    return NULL;
04100 }
04101 
04102 /*!\brief callback to display queues status in manager
04103    \addtogroup Group_AMI
04104  */
04105 static int manager_queues_show(struct mansession *s, const struct message *m)
04106 {
04107    char *a[] = { "queue", "show" };
04108 
04109    __queues_show(s, 1, -1, 2, a);
04110    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04111 
04112    return RESULT_SUCCESS;
04113 }
04114 
04115 /* Dump queue status */
04116 static int manager_queues_status(struct mansession *s, const struct message *m)
04117 {
04118    time_t now;
04119    int pos;
04120    const char *id = astman_get_header(m,"ActionID");
04121    const char *queuefilter = astman_get_header(m,"Queue");
04122    const char *memberfilter = astman_get_header(m,"Member");
04123    char idText[256] = "";
04124    struct call_queue *q;
04125    struct queue_ent *qe;
04126    float sl = 0;
04127    struct member *mem;
04128 
04129    astman_send_ack(s, m, "Queue status will follow");
04130    time(&now);
04131    AST_LIST_LOCK(&queues);
04132    if (!ast_strlen_zero(id))
04133       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04134 
04135    AST_LIST_TRAVERSE(&queues, q, list) {
04136       ast_mutex_lock(&q->lock);
04137 
04138       /* List queue properties */
04139       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04140          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04141          astman_append(s, "Event: QueueParams\r\n"
04142             "Queue: %s\r\n"
04143             "Max: %d\r\n"
04144             "Calls: %d\r\n"
04145             "Holdtime: %d\r\n"
04146             "Completed: %d\r\n"
04147             "Abandoned: %d\r\n"
04148             "ServiceLevel: %d\r\n"
04149             "ServicelevelPerf: %2.1f\r\n"
04150             "Weight: %d\r\n"
04151             "%s"
04152             "\r\n",
04153             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04154             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04155          /* List Queue Members */
04156          for (mem = q->members; mem; mem = mem->next) {
04157             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04158                astman_append(s, "Event: QueueMember\r\n"
04159                   "Queue: %s\r\n"
04160                   "Location: %s\r\n"
04161                   "Membership: %s\r\n"
04162                   "Penalty: %d\r\n"
04163                   "CallsTaken: %d\r\n"
04164                   "LastCall: %d\r\n"
04165                   "Status: %d\r\n"
04166                   "Paused: %d\r\n"
04167                   "%s"
04168                   "\r\n",
04169                   q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
04170                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04171             }
04172          }
04173          /* List Queue Entries */
04174          pos = 1;
04175          for (qe = q->head; qe; qe = qe->next) {
04176             astman_append(s, "Event: QueueEntry\r\n"
04177                "Queue: %s\r\n"
04178                "Position: %d\r\n"
04179                "Channel: %s\r\n"
04180                "CallerID: %s\r\n"
04181                "CallerIDName: %s\r\n"
04182                "Wait: %ld\r\n"
04183                "%s"
04184                "\r\n",
04185                q->name, pos++, qe->chan->name,
04186                S_OR(qe->chan->cid.cid_num, "unknown"),
04187                S_OR(qe->chan->cid.cid_name, "unknown"),
04188                (long) (now - qe->start), idText);
04189          }
04190       }
04191       ast_mutex_unlock(&q->lock);
04192    }
04193 
04194    astman_append(s,
04195       "Event: QueueStatusComplete\r\n"
04196       "%s"
04197       "\r\n",idText);
04198 
04199    AST_LIST_UNLOCK(&queues);
04200 
04201 
04202    return RESULT_SUCCESS;
04203 }
04204 
04205 static int manager_add_queue_member(struct mansession *s, const struct message *m)
04206 {
04207    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04208    int paused, penalty = 0;
04209 
04210    queuename = astman_get_header(m, "Queue");
04211    interface = astman_get_header(m, "Interface");
04212    penalty_s = astman_get_header(m, "Penalty");
04213    paused_s = astman_get_header(m, "Paused");
04214    membername = astman_get_header(m, "MemberName");
04215 
04216    if (ast_strlen_zero(queuename)) {
04217       astman_send_error(s, m, "'Queue' not specified.");
04218       return 0;
04219    }
04220 
04221    if (ast_strlen_zero(interface)) {
04222       astman_send_error(s, m, "'Interface' not specified.");
04223       return 0;
04224    }
04225 
04226    if (ast_strlen_zero(penalty_s))
04227       penalty = 0;
04228    else if (sscanf(penalty_s, "%d", &penalty) != 1)
04229       penalty = 0;
04230 
04231    if (ast_strlen_zero(paused_s))
04232       paused = 0;
04233    else
04234       paused = abs(ast_true(paused_s));
04235 
04236    if (ast_strlen_zero(membername))
04237       membername = interface;
04238 
04239    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04240    case RES_OKAY:
04241       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04242       astman_send_ack(s, m, "Added interface to queue");
04243       break;
04244    case RES_EXISTS:
04245       astman_send_error(s, m, "Unable to add interface: Already there");
04246       break;
04247    case RES_NOSUCHQUEUE:
04248       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04249       break;
04250    case RES_OUTOFMEMORY:
04251       astman_send_error(s, m, "Out of memory");
04252       break;
04253    }
04254 
04255    return 0;
04256 }
04257 
04258 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
04259 {
04260    const char *queuename, *interface;
04261 
04262    queuename = astman_get_header(m, "Queue");
04263    interface = astman_get_header(m, "Interface");
04264 
04265    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04266       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04267       return 0;
04268    }
04269 
04270    switch (remove_from_queue(queuename, interface)) {
04271    case RES_OKAY:
04272       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04273       astman_send_ack(s, m, "Removed interface from queue");
04274       break;
04275    case RES_EXISTS:
04276       astman_send_error(s, m, "Unable to remove interface: Not there");
04277       break;
04278    case RES_NOSUCHQUEUE:
04279       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04280       break;
04281    case RES_OUTOFMEMORY:
04282       astman_send_error(s, m, "Out of memory");
04283       break;
04284    }
04285 
04286    return 0;
04287 }
04288 
04289 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
04290 {
04291    const char *queuename, *interface, *paused_s;
04292    int paused;
04293 
04294    interface = astman_get_header(m, "Interface");
04295    paused_s = astman_get_header(m, "Paused");
04296    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04297 
04298    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04299       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04300       return 0;
04301    }
04302 
04303    paused = abs(ast_true(paused_s));
04304 
04305    if (set_member_paused(queuename, interface, paused))
04306       astman_send_error(s, m, "Interface not found");
04307    else
04308       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04309    return 0;
04310 }
04311 
04312 static int handle_queue_add_member(int fd, int argc, char *argv[])
04313 {
04314    char *queuename, *interface, *membername;
04315    int penalty;
04316 
04317    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04318       return RESULT_SHOWUSAGE;
04319    } else if (strcmp(argv[4], "to")) {
04320       return RESULT_SHOWUSAGE;
04321    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04322       return RESULT_SHOWUSAGE;
04323    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04324       return RESULT_SHOWUSAGE;
04325    }
04326 
04327    queuename = argv[5];
04328    interface = argv[3];
04329    if (argc >= 8) {
04330       if (sscanf(argv[7], "%d", &penalty) == 1) {
04331          if (penalty < 0) {
04332             ast_cli(fd, "Penalty must be >= 0\n");
04333             penalty = 0;
04334          }
04335       } else {
04336          ast_cli(fd, "Penalty must be an integer >= 0\n");
04337          penalty = 0;
04338       }
04339    } else {
04340       penalty = 0;
04341    }
04342 
04343    if (argc >= 10) {
04344       membername = argv[9];
04345    } else {
04346       membername = interface;
04347    }
04348 
04349    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04350    case RES_OKAY:
04351       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04352       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04353       return RESULT_SUCCESS;
04354    case RES_EXISTS:
04355       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04356       return RESULT_FAILURE;
04357    case RES_NOSUCHQUEUE:
04358       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04359       return RESULT_FAILURE;
04360    case RES_OUTOFMEMORY:
04361       ast_cli(fd, "Out of memory\n");
04362       return RESULT_FAILURE;
04363    default:
04364       return RESULT_FAILURE;
04365    }
04366 }
04367 
04368 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
04369 {
04370    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04371    switch (pos) {
04372    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04373       return NULL;
04374    case 4:  /* only one possible match, "to" */
04375       return state == 0 ? ast_strdup("to") : NULL;
04376    case 5:  /* <queue> */
04377       return complete_queue(line, word, pos, state);
04378    case 6: /* only one possible match, "penalty" */
04379       return state == 0 ? ast_strdup("penalty") : NULL;
04380    case 7:
04381       if (state < 100) {   /* 0-99 */
04382          char *num;
04383          if ((num = ast_malloc(3))) {
04384             sprintf(num, "%d", state);
04385          }
04386          return num;
04387       } else {
04388          return NULL;
04389       }
04390    case 8: /* only one possible match, "as" */
04391       return state == 0 ? ast_strdup("as") : NULL;
04392    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04393       return NULL;
04394    default:
04395       return NULL;
04396    }
04397 }
04398 
04399 static int handle_queue_remove_member(int fd, int argc, char *argv[])
04400 {
04401    char *queuename, *interface;
04402 
04403    if (argc != 6) {
04404       return RESULT_SHOWUSAGE;
04405    } else if (strcmp(argv[4], "from")) {
04406       return RESULT_SHOWUSAGE;
04407    }
04408 
04409    queuename = argv[5];
04410    interface = argv[3];
04411 
04412    switch (remove_from_queue(queuename, interface)) {
04413    case RES_OKAY:
04414       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04415       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04416       return RESULT_SUCCESS;
04417    case RES_EXISTS:
04418       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04419       return RESULT_FAILURE;
04420    case RES_NOSUCHQUEUE:
04421       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04422       return RESULT_FAILURE;
04423    case RES_OUTOFMEMORY:
04424       ast_cli(fd, "Out of memory\n");
04425       return RESULT_FAILURE;
04426    default:
04427       return RESULT_FAILURE;
04428    }
04429 }
04430 
04431 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
04432 {
04433    int which = 0;
04434    struct call_queue *q;
04435    struct member *m;
04436 
04437    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
04438    if (pos > 5 || pos < 3)
04439       return NULL;
04440    if (pos == 4)  /* only one possible match, 'from' */
04441       return state == 0 ? ast_strdup("from") : NULL;
04442 
04443    if (pos == 5)  /* No need to duplicate code */
04444       return complete_queue(line, word, pos, state);
04445 
04446    /* here is the case for 3, <member> */
04447    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
04448       AST_LIST_TRAVERSE(&queues, q, list) {
04449          ast_mutex_lock(&q->lock);
04450          for (m = q->members ; m ; m = m->next) {
04451             if (++which > state) {
04452                ast_mutex_unlock(&q->lock);
04453                return ast_strdup(m->interface);
04454             }
04455          }
04456          ast_mutex_unlock(&q->lock);
04457       }
04458    }
04459 
04460    return NULL;
04461 }
04462 
04463 static char queue_show_usage[] =
04464 "Usage: queue show\n"
04465 "       Provides summary information on a specified queue.\n";
04466 
04467 static char qam_cmd_usage[] =
04468 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
04469 
04470 static char qrm_cmd_usage[] =
04471 "Usage: queue remove member <channel> from <queue>\n";
04472 
04473 static struct ast_cli_entry cli_show_queue_deprecated = {
04474    { "show", "queue", NULL },
04475    queue_show, NULL,
04476    NULL, complete_queue_show };
04477 
04478 static struct ast_cli_entry cli_add_queue_member_deprecated = {
04479    { "add", "queue", "member", NULL },
04480    handle_queue_add_member, NULL,
04481    NULL, complete_queue_add_member };
04482 
04483 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
04484    { "remove", "queue", "member", NULL },
04485    handle_queue_remove_member, NULL,
04486    NULL, complete_queue_remove_member };
04487 
04488 static struct ast_cli_entry cli_queue[] = {
04489    /* Deprecated */
04490    { { "show", "queues", NULL },
04491    queue_show, NULL,
04492    NULL, NULL },
04493 
04494    { { "queue", "show", NULL },
04495    queue_show, "Show status of a specified queue",
04496    queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
04497 
04498    { { "queue", "add", "member", NULL },
04499    handle_queue_add_member, "Add a channel to a specified queue",
04500    qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
04501 
04502    { { "queue", "remove", "member", NULL },
04503    handle_queue_remove_member, "Removes a channel from a specified queue",
04504    qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
04505 };
04506 
04507 static int unload_module(void)
04508 {
04509    int res;
04510 
04511    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04512    res = ast_manager_unregister("QueueStatus");
04513    res |= ast_manager_unregister("Queues");
04514    res |= ast_manager_unregister("QueueStatus");
04515    res |= ast_manager_unregister("QueueAdd");
04516    res |= ast_manager_unregister("QueueRemove");
04517    res |= ast_manager_unregister("QueuePause");
04518    res |= ast_unregister_application(app_aqm);
04519    res |= ast_unregister_application(app_rqm);
04520    res |= ast_unregister_application(app_pqm);
04521    res |= ast_unregister_application(app_upqm);
04522    res |= ast_unregister_application(app_ql);
04523    res |= ast_unregister_application(app);
04524    res |= ast_custom_function_unregister(&queueagentcount_function);
04525    res |= ast_custom_function_unregister(&queuemembercount_function);
04526    res |= ast_custom_function_unregister(&queuememberlist_function);
04527    res |= ast_custom_function_unregister(&queuewaitingcount_function);
04528    ast_devstate_del(statechange_queue, NULL);
04529 
04530    ast_module_user_hangup_all();
04531 
04532    clear_and_free_interfaces();
04533 
04534    return res;
04535 }
04536 
04537 static int load_module(void)
04538 {
04539    int res;
04540    if(!reload_queues())
04541       return AST_MODULE_LOAD_DECLINE;
04542    if (queue_persistent_members)
04543       reload_queue_members();
04544    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04545    res = ast_register_application(app, queue_exec, synopsis, descrip);
04546    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
04547    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
04548    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
04549    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
04550    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
04551    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
04552    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
04553    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
04554    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
04555    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
04556    res |= ast_custom_function_register(&queueagentcount_function);
04557    res |= ast_custom_function_register(&queuemembercount_function);
04558    res |= ast_custom_function_register(&queuememberlist_function);
04559    res |= ast_custom_function_register(&queuewaitingcount_function);
04560    res |= ast_devstate_add(statechange_queue, NULL);
04561 
04562    return res;
04563 }
04564 
04565 static int reload(void)
04566 {
04567    reload_queues();
04568    return 0;
04569 }
04570 
04571 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
04572       .load = load_module,
04573       .unload = unload_module,
04574       .reload = reload,
04575           );
04576 

Generated on Wed Aug 15 01:24:14 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3