Fri Apr 24 16:25:47 2009

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

Generated on Fri Apr 24 16:25:47 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7