Wed Mar 7 19:15:58 2012

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

Generated on Wed Mar 7 19:15:58 2012 for Asterisk - the Open Source PBX by  doxygen 1.4.7