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

Generated on Thu Oct 8 00:58:50 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7