Fri Jul 24 00:40:39 2009

Asterisk developer's documentation


app_queue.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief True call queues with optional send URL on answer
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \arg Config in \ref Config_qu queues.conf
00026  *
00027  * \par Development notes
00028  * \note 2004-11-25: Persistent Dynamic Members added by:
00029  *             NetNation Communications (www.netnation.com)
00030  *             Kevin Lindsay <kevinl@netnation.com>
00031  *
00032  *             Each dynamic agent in each queue is now stored in the astdb.
00033  *             When asterisk is restarted, each agent will be automatically
00034  *             readded into their recorded queues. This feature can be
00035  *             configured with the 'persistent_members=<1|0>' setting in the
00036  *             '[general]' category in queues.conf. The default is on.
00037  *
00038  * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
00039  *
00040  * \note These features added by David C. Troy <dave@toad.net>:
00041  *    - Per-queue holdtime calculation
00042  *    - Estimated holdtime announcement
00043  *    - Position announcement
00044  *    - Abandoned/completed call counters
00045  *    - Failout timer passed as optional app parameter
00046  *    - Optional monitoring of calls, started when call is answered
00047  *
00048  * Patch Version 1.07 2003-12-24 01
00049  *
00050  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
00051  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
00052  *
00053  * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
00054  * by Matthew Enger <m.enger@xi.com.au>
00055  *
00056  * \ingroup applications
00057  */
00058 
00059 /*** MODULEINFO
00060    <depend>res_monitor</depend>
00061  ***/
00062 
00063 #include "asterisk.h"
00064 
00065 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 188473 $")
00066 
00067 #include <sys/time.h>
00068 #include <sys/signal.h>
00069 #include <netinet/in.h>
00070 #include <ctype.h>
00071 
00072 #include "asterisk/lock.h"
00073 #include "asterisk/file.h"
00074 #include "asterisk/channel.h"
00075 #include "asterisk/pbx.h"
00076 #include "asterisk/app.h"
00077 #include "asterisk/linkedlists.h"
00078 #include "asterisk/module.h"
00079 #include "asterisk/translate.h"
00080 #include "asterisk/say.h"
00081 #include "asterisk/features.h"
00082 #include "asterisk/musiconhold.h"
00083 #include "asterisk/cli.h"
00084 #include "asterisk/manager.h"
00085 #include "asterisk/config.h"
00086 #include "asterisk/monitor.h"
00087 #include "asterisk/utils.h"
00088 #include "asterisk/causes.h"
00089 #include "asterisk/astdb.h"
00090 #include "asterisk/devicestate.h"
00091 #include "asterisk/stringfields.h"
00092 #include "asterisk/event.h"
00093 #include "asterisk/astobj2.h"
00094 #include "asterisk/strings.h"
00095 #include "asterisk/global_datastores.h"
00096 #include "asterisk/taskprocessor.h"
00097 
00098 /*!
00099  * \par Please read before modifying this file.
00100  * There are three locks which are regularly used
00101  * throughout this file, the queue list lock, the lock
00102  * for each individual queue, and the interface list lock.
00103  * Please be extra careful to always lock in the following order
00104  * 1) queue list lock
00105  * 2) individual queue lock
00106  * 3) interface list lock
00107  * This order has sort of "evolved" over the lifetime of this
00108  * application, but it is now in place this way, so please adhere
00109  * to this order!
00110  */
00111 
00112 enum {
00113    QUEUE_STRATEGY_RINGALL = 0,
00114    QUEUE_STRATEGY_LEASTRECENT,
00115    QUEUE_STRATEGY_FEWESTCALLS,
00116    QUEUE_STRATEGY_RANDOM,
00117    QUEUE_STRATEGY_RRMEMORY,
00118    QUEUE_STRATEGY_LINEAR,
00119    QUEUE_STRATEGY_WRANDOM
00120 };
00121 
00122 static const struct strategy {
00123    int strategy;
00124    const char *name;
00125 } strategies[] = {
00126    { QUEUE_STRATEGY_RINGALL, "ringall" },
00127    { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00128    { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00129    { QUEUE_STRATEGY_RANDOM, "random" },
00130    { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00131    { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
00132    { QUEUE_STRATEGY_LINEAR, "linear" },
00133    { QUEUE_STRATEGY_WRANDOM, "wrandom"},
00134 };
00135 
00136 static struct ast_taskprocessor *devicestate_tps;
00137 
00138 #define DEFAULT_RETRY      5
00139 #define DEFAULT_TIMEOUT    15
00140 #define RECHECK         1     /*!< Recheck every second to see we we're at the top yet */
00141 #define MAX_PERIODIC_ANNOUNCEMENTS 10           /*!< The maximum periodic announcements we can have */
00142 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15       /*!< The minimum number of seconds between position announcements \
00143                                                      The default value of 15 provides backwards compatibility */
00144 #define MAX_QUEUE_BUCKETS 53
00145 
00146 #define  RES_OKAY 0     /*!< Action completed */
00147 #define  RES_EXISTS  (-1)     /*!< Entry already exists */
00148 #define  RES_OUTOFMEMORY   (-2)     /*!< Out of memory */
00149 #define  RES_NOSUCHQUEUE   (-3)     /*!< No such queue */
00150 #define RES_NOT_DYNAMIC (-4)     /*!< Member is not dynamic */
00151 
00152 static char *app = "Queue";
00153 
00154 static char *synopsis = "Queue a call for a call queue";
00155 
00156 static char *descrip =
00157 "  Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
00158 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00159 "This application will return to the dialplan if the queue does not exist, or\n"
00160 "any of the join options cause the caller to not enter the queue.\n"
00161 "The option string may contain zero or more of the following characters:\n"
00162 "      'c' -- continue in the dialplan if the callee hangs up.\n"
00163 "      'd' -- data-quality (modem) call (minimum delay).\n"
00164 "      'h' -- allow callee to hang up by hitting '*', or whatver disconnect sequence\n"
00165 "             that is defined in the featuremap section in features.conf.\n"
00166 "      'H' -- allow caller to hang up by hitting '*', or whatever disconnect sequence\n"
00167 "             that is defined in the featuremap section in features.conf.\n"
00168 "      'n' -- no retries on the timeout; will exit this application and \n"
00169 "             go to the next step.\n"
00170 "      'i' -- ignore call forward requests from queue members and do nothing\n"
00171 "             when they are requested.\n"
00172 "      'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
00173 "      't' -- allow the called user transfer the calling user by pressing '#' or\n"
00174 "             whatever blindxfer sequence defined in the featuremap section in\n"
00175 "             features.conf\n"
00176 "      'T' -- to allow the calling user to transfer the call by pressing '#' or\n"
00177 "             whatever blindxfer sequence defined in the featuremap section in\n"
00178 "             features.conf\n"
00179 "      'w' -- allow the called user to write the conversation to disk via Monitor\n"
00180 "             by pressing the automon sequence defined in the featuremap section in\n"
00181 "             features.conf\n"
00182 "      'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00183 "             by pressing the automon sequence defined in the featuremap section in\n"
00184 "             features.conf\n"
00185 "      'k' -- Allow the called party to enable parking of the call by sending\n"
00186 "             the DTMF sequence defined for call parking in features.conf.\n"
00187 "      'K' -- Allow the calling party to enable parking of the call by sending\n"
00188 "             the DTMF sequence defined for call parking in features.conf.\n"
00189 "      'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
00190 "             by pressing the automixmon sequence defined in the featuremap section in\n"
00191 "             features.conf\n"
00192 "      'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
00193 "             by pressing the automixmon sequence defined in the featuremap section in\n"
00194 "             features.conf\n"
00195 "  The optional URL will be sent to the called party if the channel supports\n"
00196 "it.\n"
00197 "  The optional AGI parameter will setup an AGI script to be executed on the \n"
00198 "calling party's channel once they are connected to a queue member.\n"
00199 "  The optional macro parameter will run a macro on the \n"
00200 "calling party's channel once they are connected to a queue member.\n"
00201 "  The optional gosub parameter will run a gosub on the \n"
00202 "calling party's channel once they are connected to a queue member.\n"
00203 "  The optional rule parameter will cause the queue's defaultrule to be\n"
00204 "overridden by the rule specified.\n"
00205 "  The timeout will cause the queue to fail out after a specified number of\n"
00206 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00207 "  This application sets the following channel variable upon completion:\n"
00208 "      QUEUESTATUS    The status of the call as a text string, one of\n"
00209 "             TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
00210 
00211 static char *app_aqm = "AddQueueMember" ;
00212 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00213 static char *app_aqm_descrip =
00214 "   AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
00215 "Dynamically adds interface to an existing queue.\n"
00216 "If the interface is already in the queue it will return an error.\n"
00217 "  This application sets the following channel variable upon completion:\n"
00218 "     AQMSTATUS    The status of the attempt to add a queue member as a \n"
00219 "                     text string, one of\n"
00220 "           ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00221 "Example: AddQueueMember(techsupport,SIP/3000)\n"
00222 "";
00223 
00224 static char *app_rqm = "RemoveQueueMember" ;
00225 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00226 static char *app_rqm_descrip =
00227 "   RemoveQueueMember(queuename[,interface[,options]]):\n"
00228 "Dynamically removes interface to an existing queue\n"
00229 "If the interface is NOT in the queue it will return an error.\n"
00230 "  This application sets the following channel variable upon completion:\n"
00231 "     RQMSTATUS      The status of the attempt to remove a queue member as a\n"
00232 "                     text string, one of\n"
00233 "           REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00234 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
00235 "";
00236 
00237 static char *app_pqm = "PauseQueueMember" ;
00238 static char *app_pqm_synopsis = "Pauses a queue member" ;
00239 static char *app_pqm_descrip =
00240 "   PauseQueueMember([queuename],interface[,options[,reason]]):\n"
00241 "Pauses (blocks calls for) a queue member.\n"
00242 "The given interface will be paused in the given queue.  This prevents\n"
00243 "any calls from being sent from the queue to the interface until it is\n"
00244 "unpaused with UnpauseQueueMember or the manager interface.  If no\n"
00245 "queuename is given, the interface is paused in every queue it is a\n"
00246 "member of. The application will fail if the interface is not found.\n"
00247 "The reason string is entirely optional and is used to add extra information\n"
00248 "to the appropriate queue_log entries and manager events.\n"
00249 "  This application sets the following channel variable upon completion:\n"
00250 "     PQMSTATUS      The status of the attempt to pause a queue member as a\n"
00251 "                     text string, one of\n"
00252 "           PAUSED | NOTFOUND\n"
00253 "Example: PauseQueueMember(,SIP/3000)\n";
00254 
00255 static char *app_upqm = "UnpauseQueueMember" ;
00256 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00257 static char *app_upqm_descrip =
00258 "   UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
00259 "Unpauses (resumes calls to) a queue member.\n"
00260 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00261 "same way, except it unpauses instead of pausing the given interface.\n"
00262 "The reason string is entirely optional and is used to add extra information\n"
00263 "to the appropriate queue_log entries and manager events.\n"
00264 "  This application sets the following channel variable upon completion:\n"
00265 "     UPQMSTATUS       The status of the attempt to unpause a queue \n"
00266 "                      member as a text string, one of\n"
00267 "            UNPAUSED | NOTFOUND\n"
00268 "Example: UnpauseQueueMember(,SIP/3000)\n";
00269 
00270 static char *app_ql = "QueueLog" ;
00271 static char *app_ql_synopsis = "Writes to the queue_log" ;
00272 static char *app_ql_descrip =
00273 "   QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
00274 "Allows you to write your own events into the queue log\n"
00275 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
00276 
00277 /*! \brief Persistent Members astdb family */
00278 static const char *pm_family = "Queue/PersistentMembers";
00279 /* The maximum length of each persistent member queue database entry */
00280 #define PM_MAX_LEN 8192
00281 
00282 /*! \brief queues.conf [general] option */
00283 static int queue_keep_stats = 0;
00284 
00285 /*! \brief queues.conf [general] option */
00286 static int queue_persistent_members = 0;
00287 
00288 /*! \brief queues.conf per-queue weight option */
00289 static int use_weight = 0;
00290 
00291 /*! \brief queues.conf [general] option */
00292 static int autofill_default = 0;
00293 
00294 /*! \brief queues.conf [general] option */
00295 static int montype_default = 0;
00296 
00297 /*! \brief queues.conf [general] option */
00298 static int shared_lastcall = 0;
00299 
00300 /*! \brief Subscription to device state change events */
00301 static struct ast_event_sub *device_state_sub;
00302 
00303 /*! \brief queues.conf [general] option */
00304 static int update_cdr = 0;
00305 
00306 enum queue_result {
00307    QUEUE_UNKNOWN = 0,
00308    QUEUE_TIMEOUT = 1,
00309    QUEUE_JOINEMPTY = 2,
00310    QUEUE_LEAVEEMPTY = 3,
00311    QUEUE_JOINUNAVAIL = 4,
00312    QUEUE_LEAVEUNAVAIL = 5,
00313    QUEUE_FULL = 6,
00314    QUEUE_CONTINUE = 7,
00315 };
00316 
00317 const struct {
00318    enum queue_result id;
00319    char *text;
00320 } queue_results[] = {
00321    { QUEUE_UNKNOWN, "UNKNOWN" },
00322    { QUEUE_TIMEOUT, "TIMEOUT" },
00323    { QUEUE_JOINEMPTY,"JOINEMPTY" },
00324    { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00325    { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00326    { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00327    { QUEUE_FULL, "FULL" },
00328    { QUEUE_CONTINUE, "CONTINUE" },
00329 };
00330 
00331 enum queue_timeout_priority {
00332    TIMEOUT_PRIORITY_APP,
00333    TIMEOUT_PRIORITY_CONF,
00334 };
00335 
00336 /*! \brief We define a custom "local user" structure because we
00337  *  use it not only for keeping track of what is in use but
00338  *  also for keeping track of who we're dialing.
00339  *
00340  *  There are two "links" defined in this structure, q_next and call_next.
00341  *  q_next links ALL defined callattempt structures into a linked list. call_next is
00342  *  a link which allows for a subset of the callattempts to be traversed. This subset
00343  *  is used in wait_for_answer so that irrelevant callattempts are not traversed. This
00344  *  also is helpful so that queue logs are always accurate in the case where a call to 
00345  *  a member times out, especially if using the ringall strategy. 
00346 */
00347 
00348 struct callattempt {
00349    struct callattempt *q_next;
00350    struct callattempt *call_next;
00351    struct ast_channel *chan;
00352    char interface[256];
00353    int stillgoing;
00354    int metric;
00355    int oldstatus;
00356    time_t lastcall;
00357    struct call_queue *lastqueue;
00358    struct member *member;
00359 };
00360 
00361 
00362 struct queue_ent {
00363    struct call_queue *parent;             /*!< What queue is our parent */
00364    char moh[80];                          /*!< Name of musiconhold to be used */
00365    char announce[80];                     /*!< Announcement to play for member when call is answered */
00366    char context[AST_MAX_CONTEXT];         /*!< Context when user exits queue */
00367    char digits[AST_MAX_EXTENSION];        /*!< Digits entered while in queue */
00368    int valid_digits;                      /*!< Digits entered correspond to valid extension. Exited */
00369    int pos;                               /*!< Where we are in the queue */
00370    int prio;                              /*!< Our priority */
00371    int last_pos_said;                     /*!< Last position we told the user */
00372    time_t last_periodic_announce_time;    /*!< The last time we played a periodic announcement */
00373    int last_periodic_announce_sound;      /*!< The last periodic announcement we made */
00374    time_t last_pos;                       /*!< Last time we told the user their position */
00375    int opos;                              /*!< Where we started in the queue */
00376    int handled;                           /*!< Whether our call was handled */
00377    int pending;                           /*!< Non-zero if we are attempting to call a member */
00378    int max_penalty;                       /*!< Limit the members that can take this call to this penalty or lower */
00379    int min_penalty;                       /*!< Limit the members that can take this call to this penalty or higher */
00380    int linpos;                            /*!< If using linear strategy, what position are we at? */
00381    int linwrapped;                        /*!< Is the linpos wrapped? */
00382    time_t start;                          /*!< When we started holding */
00383    time_t expire;                         /*!< When this entry should expire (time out of queue) */
00384    struct ast_channel *chan;              /*!< Our channel */
00385    AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
00386    struct penalty_rule *pr;               /*!< Pointer to the next penalty rule to implement */
00387    struct queue_ent *next;                /*!< The next queue entry */
00388 };
00389 
00390 struct member {
00391    char interface[80];                 /*!< Technology/Location to dial to reach this member*/
00392    char state_interface[80];           /*!< Technology/Location from which to read devicestate changes */
00393    char membername[80];                /*!< Member name to use in queue logs */
00394    int penalty;                        /*!< Are we a last resort? */
00395    int calls;                          /*!< Number of calls serviced by this member */
00396    int dynamic;                        /*!< Are we dynamically added? */
00397    int realtime;                       /*!< Is this member realtime? */
00398    int status;                         /*!< Status of queue member */
00399    int paused;                         /*!< Are we paused (not accepting calls)? */
00400    time_t lastcall;                    /*!< When last successful call was hungup */
00401    struct call_queue *lastqueue;     /*!< Last queue we received a call */
00402    unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
00403    unsigned int delme:1;               /*!< Flag to delete entry on reload */
00404    char rt_uniqueid[80];               /*!< Unique id of realtime member entry */
00405 };
00406 
00407 struct member_interface {
00408    char interface[80];
00409    AST_LIST_ENTRY(member_interface) list;    /*!< Next call queue */
00410 };
00411 
00412 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00413 
00414 /* values used in multi-bit flags in call_queue */
00415 #define QUEUE_EMPTY_NORMAL 1
00416 #define QUEUE_EMPTY_STRICT 2
00417 #define QUEUE_EMPTY_LOOSE 3
00418 #define ANNOUNCEHOLDTIME_ALWAYS 1
00419 #define ANNOUNCEHOLDTIME_ONCE 2
00420 #define QUEUE_EVENT_VARIABLES 3
00421 
00422 struct penalty_rule {
00423    int time;                           /*!< Number of seconds that need to pass before applying this rule */
00424    int max_value;                      /*!< The amount specified in the penalty rule for max penalty */
00425    int min_value;                      /*!< The amount specified in the penalty rule for min penalty */
00426    int max_relative;                   /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
00427    int min_relative;                   /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
00428    AST_LIST_ENTRY(penalty_rule) list;  /*!< Next penalty_rule */
00429 };
00430 
00431 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
00432 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
00433 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
00434 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
00435 
00436 struct call_queue {
00437    AST_DECLARE_STRING_FIELDS(
00438       /*! Queue name */
00439       AST_STRING_FIELD(name);
00440       /*! Music on Hold class */
00441       AST_STRING_FIELD(moh);
00442       /*! Announcement to play when call is answered */
00443       AST_STRING_FIELD(announce);
00444       /*! Exit context */
00445       AST_STRING_FIELD(context);
00446       /*! Macro to run upon member connection */
00447       AST_STRING_FIELD(membermacro);
00448       /*! Gosub to run upon member connection */
00449       AST_STRING_FIELD(membergosub);
00450       /*! Default rule to use if none specified in call to Queue() */
00451       AST_STRING_FIELD(defaultrule);
00452       /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
00453       AST_STRING_FIELD(sound_next);
00454       /*! Sound file: "There are currently" (def. queue-thereare) */
00455       AST_STRING_FIELD(sound_thereare);
00456       /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
00457       AST_STRING_FIELD(sound_calls);
00458       /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
00459       AST_STRING_FIELD(queue_quantity1);
00460       /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
00461       AST_STRING_FIELD(queue_quantity2);
00462       /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
00463       AST_STRING_FIELD(sound_holdtime);
00464       /*! Sound file: "minutes." (def. queue-minutes) */
00465       AST_STRING_FIELD(sound_minutes);
00466       /*! Sound file: "minute." (def. queue-minute) */
00467       AST_STRING_FIELD(sound_minute);
00468       /*! Sound file: "seconds." (def. queue-seconds) */
00469       AST_STRING_FIELD(sound_seconds);
00470       /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
00471       AST_STRING_FIELD(sound_thanks);
00472       /*! Sound file: Custom announce for caller, no default */
00473       AST_STRING_FIELD(sound_callerannounce);
00474       /*! Sound file: "Hold time" (def. queue-reporthold) */
00475       AST_STRING_FIELD(sound_reporthold);
00476    );
00477    /*! Sound files: Custom announce, no default */
00478    struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
00479    unsigned int dead:1;
00480    unsigned int joinempty:2;
00481    unsigned int eventwhencalled:2;
00482    unsigned int leavewhenempty:2;
00483    unsigned int ringinuse:1;
00484    unsigned int setinterfacevar:1;
00485    unsigned int setqueuevar:1;
00486    unsigned int setqueueentryvar:1;
00487    unsigned int reportholdtime:1;
00488    unsigned int wrapped:1;
00489    unsigned int timeoutrestart:1;
00490    unsigned int announceholdtime:2;
00491    unsigned int announceposition:3;
00492    int strategy:4;
00493    unsigned int maskmemberstatus:1;
00494    unsigned int realtime:1;
00495    unsigned int found:1;
00496    int announcepositionlimit;          /*!< How many positions we announce? */
00497    int announcefrequency;              /*!< How often to announce their position */
00498    int minannouncefrequency;           /*!< The minimum number of seconds between position announcements (def. 15) */
00499    int periodicannouncefrequency;      /*!< How often to play periodic announcement */
00500    int numperiodicannounce;            /*!< The number of periodic announcements configured */
00501    int randomperiodicannounce;         /*!< Are periodic announcments randomly chosen */
00502    int roundingseconds;                /*!< How many seconds do we round to? */
00503    int holdtime;                       /*!< Current avg holdtime, based on an exponential average */
00504    int callscompleted;                 /*!< Number of queue calls completed */
00505    int callsabandoned;                 /*!< Number of queue calls abandoned */
00506    int servicelevel;                   /*!< seconds setting for servicelevel*/
00507    int callscompletedinsl;             /*!< Number of calls answered with servicelevel*/
00508    char monfmt[8];                     /*!< Format to use when recording calls */
00509    int montype;                        /*!< Monitor type  Monitor vs. MixMonitor */
00510    int count;                          /*!< How many entries */
00511    int maxlen;                         /*!< Max number of entries */
00512    int wrapuptime;                     /*!< Wrapup Time */
00513 
00514    int retry;                          /*!< Retry calling everyone after this amount of time */
00515    int timeout;                        /*!< How long to wait for an answer */
00516    int weight;                         /*!< Respective weight */
00517    int autopause;                      /*!< Auto pause queue members if they fail to answer */
00518    int timeoutpriority;                /*!< Do we allow a fraction of the timeout to occur for a ring? */
00519 
00520    /* Queue strategy things */
00521    int rrpos;                          /*!< Round Robin - position */
00522    int memberdelay;                    /*!< Seconds to delay connecting member to caller */
00523    int autofill;                       /*!< Ignore the head call status and ring an available agent */
00524    
00525    struct ao2_container *members;             /*!< Head of the list of members */
00526    /*! 
00527     * \brief Number of members _logged in_
00528     * \note There will be members in the members container that are not logged
00529     *       in, so this can not simply be replaced with ao2_container_count(). 
00530     */
00531    int membercount;
00532    struct queue_ent *head;             /*!< Head of the list of callers */
00533    AST_LIST_ENTRY(call_queue) list;    /*!< Next call queue */
00534    AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
00535 };
00536 
00537 struct rule_list {
00538    char name[80];
00539    AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
00540    AST_LIST_ENTRY(rule_list) list;
00541 };
00542 
00543 AST_LIST_HEAD_STATIC(rule_lists, rule_list);
00544 
00545 static struct ao2_container *queues;
00546 
00547 static void copy_rules(struct queue_ent *qe, const char *rulename);
00548 static void update_qe_rule(struct queue_ent *qe);
00549 static void update_realtime_members(struct call_queue *q);
00550 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
00551 
00552 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); 
00553 /*! \brief sets the QUEUESTATUS channel variable */
00554 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00555 {
00556    int i;
00557 
00558    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
00559       if (queue_results[i].id == res) {
00560          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00561          return;
00562       }
00563    }
00564 }
00565 
00566 static const char *int2strat(int strategy)
00567 {
00568    int x;
00569 
00570    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00571       if (strategy == strategies[x].strategy)
00572          return strategies[x].name;
00573    }
00574 
00575    return "<unknown>";
00576 }
00577 
00578 static int strat2int(const char *strategy)
00579 {
00580    int x;
00581 
00582    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00583       if (!strcasecmp(strategy, strategies[x].name))
00584          return strategies[x].strategy;
00585    }
00586 
00587    return -1;
00588 }
00589 
00590 static int queue_hash_cb(const void *obj, const int flags)
00591 {
00592    const struct call_queue *q = obj;
00593 
00594    return ast_str_case_hash(q->name);
00595 }
00596 
00597 static int queue_cmp_cb(void *obj, void *arg, int flags)
00598 {
00599    struct call_queue *q = obj, *q2 = arg;
00600    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
00601 }
00602 
00603 static inline struct call_queue *queue_ref(struct call_queue *q)
00604 {
00605    ao2_ref(q, 1);
00606    return q;
00607 }
00608 
00609 static inline struct call_queue *queue_unref(struct call_queue *q)
00610 {
00611    ao2_ref(q, -1);
00612    return q;
00613 }
00614 
00615 /*! \brief Set variables of queue */
00616 static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
00617 {
00618    char interfacevar[256]="";
00619    float sl = 0;
00620 
00621    if (q->setqueuevar) {
00622       sl = 0;
00623       if (q->callscompleted > 0) 
00624          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00625 
00626       snprintf(interfacevar, sizeof(interfacevar),
00627          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00628          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00629    
00630       pbx_builtin_setvar_multiple(chan, interfacevar); 
00631    }
00632 }
00633 
00634 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
00635 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00636 {
00637    struct queue_ent *cur;
00638 
00639    if (!q || !new)
00640       return;
00641    if (prev) {
00642       cur = prev->next;
00643       prev->next = new;
00644    } else {
00645       cur = q->head;
00646       q->head = new;
00647    }
00648    new->next = cur;
00649    new->parent = q;
00650    new->pos = ++(*pos);
00651    new->opos = *pos;
00652 }
00653 
00654 enum queue_member_status {
00655    QUEUE_NO_MEMBERS,
00656    QUEUE_NO_REACHABLE_MEMBERS,
00657    QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
00658    QUEUE_NORMAL
00659 };
00660 
00661 /*! \brief Check if members are available
00662  *
00663  * This function checks to see if members are available to be called. If any member
00664  * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
00665  * the appropriate reason why is returned
00666  */
00667 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
00668 {
00669    struct member *member;
00670    struct ao2_iterator mem_iter;
00671    enum queue_member_status result = QUEUE_NO_MEMBERS;
00672 
00673    ao2_lock(q);
00674    mem_iter = ao2_iterator_init(q->members, 0);
00675    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00676       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00677          continue;
00678 
00679       switch (member->status) {
00680       case AST_DEVICE_INVALID:
00681          /* nothing to do */
00682          break;
00683       case AST_DEVICE_UNAVAILABLE:
00684          if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 
00685             result = QUEUE_NO_REACHABLE_MEMBERS;
00686          break;
00687       default:
00688          if (member->paused) {
00689             result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00690          } else {
00691             ao2_unlock(q);
00692             ao2_ref(member, -1);
00693             return QUEUE_NORMAL;
00694          }
00695          break;
00696       }
00697    }
00698 
00699    ao2_unlock(q);
00700    return result;
00701 }
00702 
00703 struct statechange {
00704    AST_LIST_ENTRY(statechange) entry;
00705    int state;
00706    char dev[0];
00707 };
00708 
00709 /*! \brief set a member's status based on device state of that member's state_interface.
00710  *  
00711  * Lock interface list find sc, iterate through each queues queue_member list for member to
00712  * update state inside queues
00713 */
00714 static int update_status(const char *interface, const int status)
00715 {
00716    struct member *cur;
00717    struct ao2_iterator mem_iter, queue_iter;
00718    struct call_queue *q;
00719 
00720    queue_iter = ao2_iterator_init(queues, 0);
00721    while ((q = ao2_iterator_next(&queue_iter))) {
00722       ao2_lock(q);
00723       mem_iter = ao2_iterator_init(q->members, 0);
00724       while ((cur = ao2_iterator_next(&mem_iter))) {
00725          char *tmp_interface;
00726          char *slash_pos;
00727          tmp_interface = ast_strdupa(cur->state_interface);
00728          if ((slash_pos = strchr(tmp_interface, '/')))
00729             if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00730                *slash_pos = '\0';
00731 
00732          if (strcasecmp(interface, tmp_interface)) {
00733             ao2_ref(cur, -1);
00734             continue;
00735          }
00736 
00737          if (cur->status != status) {
00738             cur->status = status;
00739             if (q->maskmemberstatus) {
00740                ao2_ref(cur, -1);
00741                continue;
00742             }
00743 
00744             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00745                "Queue: %s\r\n"
00746                "Location: %s\r\n"
00747                "MemberName: %s\r\n"
00748                "Membership: %s\r\n"
00749                "Penalty: %d\r\n"
00750                "CallsTaken: %d\r\n"
00751                "LastCall: %d\r\n"
00752                "Status: %d\r\n"
00753                "Paused: %d\r\n",
00754                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00755                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00756          }
00757          ao2_ref(cur, -1);
00758       }
00759       queue_unref(q);
00760       ao2_unlock(q);
00761    }
00762 
00763    return 0;
00764 }
00765 
00766 /*! \brief set a member's status based on device state of that member's interface*/
00767 static int handle_statechange(void *datap)
00768 {
00769    struct member_interface *curint;
00770    struct statechange *sc = datap;
00771 
00772    AST_LIST_LOCK(&interfaces);
00773    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00774       char *interface;
00775       char *slash_pos;
00776       interface = ast_strdupa(curint->interface);
00777       if ((slash_pos = strchr(interface, '/')))
00778          if ((slash_pos = strchr(slash_pos + 1, '/')))
00779             *slash_pos = '\0';
00780 
00781       if (!strcasecmp(interface, sc->dev))
00782          break;
00783    }
00784    AST_LIST_UNLOCK(&interfaces);
00785 
00786    if (!curint) {
00787       ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, devstate2str(sc->state));
00788       ast_free(sc);
00789       return 0;
00790    }
00791 
00792    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00793 
00794    update_status(sc->dev, sc->state);
00795    ast_free(sc);
00796    return 0;
00797 }
00798 
00799 static void device_state_cb(const struct ast_event *event, void *unused)
00800 {
00801    enum ast_device_state state;
00802    const char *device;
00803    struct statechange *sc;
00804    size_t datapsize;
00805 
00806    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00807    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00808 
00809    if (ast_strlen_zero(device)) {
00810       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00811       return;
00812    }
00813    datapsize = sizeof(*sc) + strlen(device) + 1;
00814    if (!(sc = ast_calloc(1, datapsize))) {
00815       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
00816       return;
00817    }
00818    sc->state = state;
00819    strcpy(sc->dev, device);
00820    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
00821       ast_free(sc);
00822    }
00823 }
00824 
00825 /*! \brief allocate space for new queue member and set fields based on parameters passed */
00826 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
00827 {
00828    struct member *cur;
00829    
00830    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00831       cur->penalty = penalty;
00832       cur->paused = paused;
00833       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00834       if (!ast_strlen_zero(state_interface))
00835          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00836       else
00837          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00838       if (!ast_strlen_zero(membername))
00839          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00840       else
00841          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00842       if (!strchr(cur->interface, '/'))
00843          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00844       cur->status = ast_device_state(cur->state_interface);
00845    }
00846 
00847    return cur;
00848 }
00849 
00850 
00851 static int compress_char(const char c)
00852 {
00853    if (c < 32)
00854       return 0;
00855    else if (c > 96)
00856       return c - 64;
00857    else
00858       return c - 32;
00859 }
00860 
00861 static int member_hash_fn(const void *obj, const int flags)
00862 {
00863    const struct member *mem = obj;
00864    const char *chname = strchr(mem->interface, '/');
00865    int ret = 0, i;
00866    if (!chname)
00867       chname = mem->interface;
00868    for (i = 0; i < 5 && chname[i]; i++)
00869       ret += compress_char(chname[i]) << (i * 6);
00870    return ret;
00871 }
00872 
00873 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00874 {
00875    struct member *mem1 = obj1, *mem2 = obj2;
00876    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00877 }
00878 
00879 /*! 
00880  * \brief Initialize Queue default values.
00881  * \note the queue's lock  must be held before executing this function
00882 */
00883 static void init_queue(struct call_queue *q)
00884 {
00885    int i;
00886    struct penalty_rule *pr_iter;
00887 
00888    q->dead = 0;
00889    q->retry = DEFAULT_RETRY;
00890    q->timeout = DEFAULT_TIMEOUT;
00891    q->maxlen = 0;
00892    q->announcefrequency = 0;
00893    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
00894    q->announceholdtime = 1;
00895    q->announcepositionlimit = 10; /* Default 10 positions */
00896    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
00897    q->roundingseconds = 0; /* Default - don't announce seconds */
00898    q->servicelevel = 0;
00899    q->ringinuse = 1;
00900    q->setinterfacevar = 0;
00901    q->setqueuevar = 0;
00902    q->setqueueentryvar = 0;
00903    q->autofill = autofill_default;
00904    q->montype = montype_default;
00905    q->monfmt[0] = '\0';
00906    q->reportholdtime = 0;
00907    q->wrapuptime = 0;
00908    q->joinempty = 0;
00909    q->leavewhenempty = 0;
00910    q->memberdelay = 0;
00911    q->maskmemberstatus = 0;
00912    q->eventwhencalled = 0;
00913    q->weight = 0;
00914    q->timeoutrestart = 0;
00915    q->periodicannouncefrequency = 0;
00916    q->randomperiodicannounce = 0;
00917    q->numperiodicannounce = 0;
00918    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
00919    if (!q->members) {
00920       if (q->strategy == QUEUE_STRATEGY_LINEAR)
00921          /* linear strategy depends on order, so we have to place all members in a single bucket */
00922          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00923       else
00924          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00925    }
00926    q->membercount = 0;
00927    q->found = 1;
00928 
00929    ast_string_field_set(q, sound_next, "queue-youarenext");
00930    ast_string_field_set(q, sound_thereare, "queue-thereare");
00931    ast_string_field_set(q, sound_calls, "queue-callswaiting");
00932    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
00933    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
00934    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
00935    ast_string_field_set(q, sound_minutes, "queue-minutes");
00936    ast_string_field_set(q, sound_minute, "queue-minute");
00937    ast_string_field_set(q, sound_seconds, "queue-seconds");
00938    ast_string_field_set(q, sound_thanks, "queue-thankyou");
00939    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
00940 
00941    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
00942       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
00943 
00944    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00945       if (q->sound_periodicannounce[i])
00946          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
00947    }
00948 
00949    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
00950       ast_free(pr_iter);
00951 }
00952 
00953 static void clear_queue(struct call_queue *q)
00954 {
00955    q->holdtime = 0;
00956    q->callscompleted = 0;
00957    q->callsabandoned = 0;
00958    q->callscompletedinsl = 0;
00959    q->wrapuptime = 0;
00960 }
00961 
00962 static int add_to_interfaces(const char *interface)
00963 {
00964    struct member_interface *curint;
00965 
00966    AST_LIST_LOCK(&interfaces);
00967    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00968       if (!strcasecmp(curint->interface, interface))
00969          break;
00970    }
00971 
00972    if (curint) {
00973       AST_LIST_UNLOCK(&interfaces);
00974       return 0;
00975    }
00976 
00977    ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00978    
00979    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00980       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00981       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00982    }
00983    AST_LIST_UNLOCK(&interfaces);
00984 
00985    return 0;
00986 }
00987 
00988 static int interface_exists_global(const char *interface, int lock_queue_container)
00989 {
00990    struct call_queue *q;
00991    struct member *mem, tmpmem;
00992    struct ao2_iterator queue_iter, mem_iter;
00993    int ret = 0;
00994 
00995    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00996    queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK);
00997    while ((q = ao2_iterator_next(&queue_iter))) {
00998       ao2_lock(q);
00999       mem_iter = ao2_iterator_init(q->members, 0);
01000       while ((mem = ao2_iterator_next(&mem_iter))) { 
01001          if (!strcasecmp(mem->state_interface, interface)) {
01002             ao2_ref(mem, -1);
01003             ret = 1;
01004             break;
01005          }
01006       }
01007       ao2_unlock(q);
01008       queue_unref(q);
01009    }
01010 
01011    return ret;
01012 }
01013 
01014 static int remove_from_interfaces(const char *interface, int lock_queue_container)
01015 {
01016    struct member_interface *curint;
01017 
01018    if (interface_exists_global(interface, lock_queue_container))
01019       return 0;
01020 
01021    AST_LIST_LOCK(&interfaces);
01022    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01023       if (!strcasecmp(curint->interface, interface)) {
01024          ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01025          AST_LIST_REMOVE_CURRENT(list);
01026          ast_free(curint);
01027          break;
01028       }
01029    }
01030    AST_LIST_TRAVERSE_SAFE_END;
01031    AST_LIST_UNLOCK(&interfaces);
01032 
01033    return 0;
01034 }
01035 
01036 static void clear_and_free_interfaces(void)
01037 {
01038    struct member_interface *curint;
01039 
01040    AST_LIST_LOCK(&interfaces);
01041    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01042       ast_free(curint);
01043    AST_LIST_UNLOCK(&interfaces);
01044 }
01045 
01046 /*! 
01047  * \brief Change queue penalty by adding rule.
01048  *
01049  * Check rule for errors with time or fomatting, see if rule is relative to rest 
01050  * of queue, iterate list of rules to find correct insertion point, insert and return.
01051  * \retval -1 on failure
01052  * \retval 0 on success 
01053  * \note Call this with the rule_lists locked 
01054 */
01055 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
01056 {
01057    char *timestr, *maxstr, *minstr, *contentdup;
01058    struct penalty_rule *rule = NULL, *rule_iter;
01059    struct rule_list *rl_iter;
01060    int penaltychangetime, inserted = 0;
01061 
01062    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01063       ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
01064       return -1;
01065    }
01066 
01067    contentdup = ast_strdupa(content);
01068    
01069    if (!(maxstr = strchr(contentdup, ','))) {
01070       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01071       ast_free(rule);
01072       return -1;
01073    }
01074 
01075    *maxstr++ = '\0';
01076    timestr = contentdup;
01077 
01078    if ((penaltychangetime = atoi(timestr)) < 0) {
01079       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01080       ast_free(rule);
01081       return -1;
01082    }
01083 
01084    rule->time = penaltychangetime;
01085 
01086    if ((minstr = strchr(maxstr,',')))
01087       *minstr++ = '\0';
01088    
01089    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01090     * OR if a min penalty change is indicated but no max penalty change is */
01091    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01092       rule->max_relative = 1;
01093    }
01094 
01095    rule->max_value = atoi(maxstr);
01096 
01097    if (!ast_strlen_zero(minstr)) {
01098       if (*minstr == '+' || *minstr == '-')
01099          rule->min_relative = 1;
01100       rule->min_value = atoi(minstr);
01101    } else /*there was no minimum specified, so assume this means no change*/
01102       rule->min_relative = 1;
01103 
01104    /*We have the rule made, now we need to insert it where it belongs*/
01105    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01106       if (strcasecmp(rl_iter->name, list_name))
01107          continue;
01108 
01109       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01110          if (rule->time < rule_iter->time) {
01111             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01112             inserted = 1;
01113             break;
01114          }
01115       }
01116       AST_LIST_TRAVERSE_SAFE_END;
01117    
01118       if (!inserted) {
01119          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01120       }
01121    }
01122 
01123    return 0;
01124 }
01125 
01126 /*! \brief Configure a queue parameter.
01127  * 
01128  * The failunknown flag is set for config files (and static realtime) to show
01129  * errors for unknown parameters. It is cleared for dynamic realtime to allow
01130  *  extra fields in the tables.
01131  * \note For error reporting, line number is passed for .conf static configuration,
01132  * for Realtime queues, linenum is -1.
01133 */
01134 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
01135 {
01136    if (!strcasecmp(param, "musicclass") || 
01137       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01138       ast_string_field_set(q, moh, val);
01139    } else if (!strcasecmp(param, "announce")) {
01140       ast_string_field_set(q, announce, val);
01141    } else if (!strcasecmp(param, "context")) {
01142       ast_string_field_set(q, context, val);
01143    } else if (!strcasecmp(param, "timeout")) {
01144       q->timeout = atoi(val);
01145       if (q->timeout < 0)
01146          q->timeout = DEFAULT_TIMEOUT;
01147    } else if (!strcasecmp(param, "ringinuse")) {
01148       q->ringinuse = ast_true(val);
01149    } else if (!strcasecmp(param, "setinterfacevar")) {
01150       q->setinterfacevar = ast_true(val);
01151    } else if (!strcasecmp(param, "setqueuevar")) {
01152       q->setqueuevar = ast_true(val);
01153    } else if (!strcasecmp(param, "setqueueentryvar")) {
01154       q->setqueueentryvar = ast_true(val);
01155    } else if (!strcasecmp(param, "monitor-format")) {
01156       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01157    } else if (!strcasecmp(param, "membermacro")) {
01158       ast_string_field_set(q, membermacro, val);
01159    } else if (!strcasecmp(param, "membergosub")) {
01160       ast_string_field_set(q, membergosub, val);
01161    } else if (!strcasecmp(param, "queue-youarenext")) {
01162       ast_string_field_set(q, sound_next, val);
01163    } else if (!strcasecmp(param, "queue-thereare")) {
01164       ast_string_field_set(q, sound_thereare, val);
01165    } else if (!strcasecmp(param, "queue-callswaiting")) {
01166       ast_string_field_set(q, sound_calls, val);
01167    } else if (!strcasecmp(param, "queue-quantity1")) {
01168       ast_string_field_set(q, queue_quantity1, val);
01169    } else if (!strcasecmp(param, "queue-quantity2")) {
01170       ast_string_field_set(q, queue_quantity2, val);
01171    } else if (!strcasecmp(param, "queue-holdtime")) {
01172       ast_string_field_set(q, sound_holdtime, val);
01173    } else if (!strcasecmp(param, "queue-minutes")) {
01174       ast_string_field_set(q, sound_minutes, val);
01175    } else if (!strcasecmp(param, "queue-minute")) {
01176       ast_string_field_set(q, sound_minute, val);
01177    } else if (!strcasecmp(param, "queue-seconds")) {
01178       ast_string_field_set(q, sound_seconds, val);
01179    } else if (!strcasecmp(param, "queue-thankyou")) {
01180       ast_string_field_set(q, sound_thanks, val);
01181    } else if (!strcasecmp(param, "queue-callerannounce")) {
01182       ast_string_field_set(q, sound_callerannounce, val);
01183    } else if (!strcasecmp(param, "queue-reporthold")) {
01184       ast_string_field_set(q, sound_reporthold, val);
01185    } else if (!strcasecmp(param, "announce-frequency")) {
01186       q->announcefrequency = atoi(val);
01187    } else if (!strcasecmp(param, "min-announce-frequency")) {
01188       q->minannouncefrequency = atoi(val);
01189       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01190    } else if (!strcasecmp(param, "announce-round-seconds")) {
01191       q->roundingseconds = atoi(val);
01192       /* Rounding to any other values just doesn't make sense... */
01193       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01194          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01195          if (linenum >= 0) {
01196             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01197                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01198                val, param, q->name, linenum);
01199          } else {
01200             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01201                "using 0 instead for queue '%s'\n", val, param, q->name);
01202          }
01203          q->roundingseconds=0;
01204       }
01205    } else if (!strcasecmp(param, "announce-holdtime")) {
01206       if (!strcasecmp(val, "once"))
01207          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01208       else if (ast_true(val))
01209          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01210       else
01211          q->announceholdtime = 0;
01212    } else if (!strcasecmp(param, "announce-position")) {
01213       if (!strcasecmp(val, "limit"))
01214          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01215       else if (!strcasecmp(val, "more"))
01216          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01217       else if (ast_true(val))
01218          q->announceposition = ANNOUNCEPOSITION_YES;
01219       else
01220          q->announceposition = ANNOUNCEPOSITION_NO;
01221    } else if (!strcasecmp(param, "announce-position-limit")) {
01222       q->announcepositionlimit = atoi(val);
01223    } else if (!strcasecmp(param, "periodic-announce")) {
01224       if (strchr(val, ',')) {
01225          char *s, *buf = ast_strdupa(val);
01226          unsigned int i = 0;
01227 
01228          while ((s = strsep(&buf, ",|"))) {
01229             if (!q->sound_periodicannounce[i])
01230                q->sound_periodicannounce[i] = ast_str_create(16);
01231             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01232             i++;
01233             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01234                break;
01235          }
01236          q->numperiodicannounce = i;
01237       } else {
01238          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01239          q->numperiodicannounce = 1;
01240       }
01241    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01242       q->periodicannouncefrequency = atoi(val);
01243    } else if (!strcasecmp(param, "random-periodic-announce")) {
01244       q->randomperiodicannounce = ast_true(val);
01245    } else if (!strcasecmp(param, "retry")) {
01246       q->retry = atoi(val);
01247       if (q->retry <= 0)
01248          q->retry = DEFAULT_RETRY;
01249    } else if (!strcasecmp(param, "wrapuptime")) {
01250       q->wrapuptime = atoi(val);
01251    } else if (!strcasecmp(param, "autofill")) {
01252       q->autofill = ast_true(val);
01253    } else if (!strcasecmp(param, "monitor-type")) {
01254       if (!strcasecmp(val, "mixmonitor"))
01255          q->montype = 1;
01256    } else if (!strcasecmp(param, "autopause")) {
01257       q->autopause = ast_true(val);
01258    } else if (!strcasecmp(param, "maxlen")) {
01259       q->maxlen = atoi(val);
01260       if (q->maxlen < 0)
01261          q->maxlen = 0;
01262    } else if (!strcasecmp(param, "servicelevel")) {
01263       q->servicelevel= atoi(val);
01264    } else if (!strcasecmp(param, "strategy")) {
01265       int strategy;
01266 
01267       /* We are a static queue and already have set this, no need to do it again */
01268       if (failunknown) {
01269          return;
01270       }
01271       strategy = strat2int(val);
01272       if (strategy < 0) {
01273          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01274             val, q->name);
01275          q->strategy = QUEUE_STRATEGY_RINGALL;
01276       }
01277       if (strategy == q->strategy) {
01278          return;
01279       }
01280       if (strategy == QUEUE_STRATEGY_LINEAR) {
01281          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01282          return;
01283       }
01284       q->strategy = strategy;
01285    } else if (!strcasecmp(param, "joinempty")) {
01286       if (!strcasecmp(val, "loose"))
01287          q->joinempty = QUEUE_EMPTY_LOOSE;
01288       else if (!strcasecmp(val, "strict"))
01289          q->joinempty = QUEUE_EMPTY_STRICT;
01290       else if (ast_true(val))
01291          q->joinempty = QUEUE_EMPTY_NORMAL;
01292       else
01293          q->joinempty = 0;
01294    } else if (!strcasecmp(param, "leavewhenempty")) {
01295       if (!strcasecmp(val, "loose"))
01296          q->leavewhenempty = QUEUE_EMPTY_LOOSE;
01297       else if (!strcasecmp(val, "strict"))
01298          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01299       else if (ast_true(val))
01300          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01301       else
01302          q->leavewhenempty = 0;
01303    } else if (!strcasecmp(param, "eventmemberstatus")) {
01304       q->maskmemberstatus = !ast_true(val);
01305    } else if (!strcasecmp(param, "eventwhencalled")) {
01306       if (!strcasecmp(val, "vars")) {
01307          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01308       } else {
01309          q->eventwhencalled = ast_true(val) ? 1 : 0;
01310       }
01311    } else if (!strcasecmp(param, "reportholdtime")) {
01312       q->reportholdtime = ast_true(val);
01313    } else if (!strcasecmp(param, "memberdelay")) {
01314       q->memberdelay = atoi(val);
01315    } else if (!strcasecmp(param, "weight")) {
01316       q->weight = atoi(val);
01317       if (q->weight)
01318          use_weight++;
01319       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01320          we will not see any effect on use_weight until next reload. */
01321    } else if (!strcasecmp(param, "timeoutrestart")) {
01322       q->timeoutrestart = ast_true(val);
01323    } else if (!strcasecmp(param, "defaultrule")) {
01324       ast_string_field_set(q, defaultrule, val);
01325    } else if (!strcasecmp(param, "timeoutpriority")) {
01326       if (!strcasecmp(val, "conf")) {
01327          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01328       } else {
01329          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01330       }
01331    } else if (failunknown) {
01332       if (linenum >= 0) {
01333          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01334             q->name, param, linenum);
01335       } else {
01336          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01337       }
01338    }
01339 }
01340 
01341 /*!
01342  * \brief Find rt member record to update otherwise create one.
01343  *
01344  * Search for member in queue, if found update penalty/paused state,
01345  * if no memeber exists create one flag it as a RT member and add to queue member list. 
01346 */
01347 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
01348 {
01349    struct member *m;
01350    struct ao2_iterator mem_iter;
01351    int penalty = 0;
01352    int paused  = 0;
01353    int found = 0;
01354 
01355    if (penalty_str) {
01356       penalty = atoi(penalty_str);
01357       if (penalty < 0)
01358          penalty = 0;
01359    }
01360 
01361    if (paused_str) {
01362       paused = atoi(paused_str);
01363       if (paused < 0)
01364          paused = 0;
01365    }
01366 
01367    /* Find member by realtime uniqueid and update */
01368    mem_iter = ao2_iterator_init(q->members, 0);
01369    while ((m = ao2_iterator_next(&mem_iter))) {
01370       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01371          m->dead = 0;   /* Do not delete this one. */
01372          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01373          if (paused_str)
01374             m->paused = paused;
01375          if (strcasecmp(state_interface, m->state_interface)) {
01376             remove_from_interfaces(m->state_interface, 0);
01377             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01378             add_to_interfaces(m->state_interface);
01379          }     
01380          m->penalty = penalty;
01381          found = 1;
01382          ao2_ref(m, -1);
01383          break;
01384       }
01385       ao2_ref(m, -1);
01386    }
01387 
01388    /* Create a new member */
01389    if (!found) {
01390       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01391          m->dead = 0;
01392          m->realtime = 1;
01393          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01394          add_to_interfaces(m->state_interface);
01395          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01396          ao2_link(q->members, m);
01397          ao2_ref(m, -1);
01398          m = NULL;
01399          q->membercount++;
01400       }
01401    }
01402 }
01403 
01404 /*! \brief Iterate through queue's member list and delete them */
01405 static void free_members(struct call_queue *q, int all)
01406 {
01407    /* Free non-dynamic members */
01408    struct member *cur;
01409    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01410 
01411    while ((cur = ao2_iterator_next(&mem_iter))) {
01412       if (all || !cur->dynamic) {
01413          ao2_unlink(q->members, cur);
01414          remove_from_interfaces(cur->state_interface, 1);
01415          q->membercount--;
01416       }
01417       ao2_ref(cur, -1);
01418    }
01419 }
01420 
01421 /*! \brief Free queue's member list then its string fields */
01422 static void destroy_queue(void *obj)
01423 {
01424    struct call_queue *q = obj;
01425    int i;
01426 
01427    free_members(q, 1);
01428    ast_string_field_free_memory(q);
01429    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01430       if (q->sound_periodicannounce[i])
01431          free(q->sound_periodicannounce[i]);
01432    }
01433    ao2_ref(q->members, -1);
01434 }
01435 
01436 static struct call_queue *alloc_queue(const char *queuename)
01437 {
01438    struct call_queue *q;
01439 
01440    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
01441       if (ast_string_field_init(q, 64)) {
01442          ao2_ref(q, -1);
01443          return NULL;
01444       }
01445       ast_string_field_set(q, name, queuename);
01446    }
01447    return q;
01448 }
01449 
01450 /*!
01451  * \brief Reload a single queue via realtime.
01452  *
01453  * Check for statically defined queue first, check if deleted RT queue,
01454  * check for new RT queue, if queue vars are not defined init them with defaults.
01455  * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
01456  * \retval the queue, 
01457  * \retval NULL if it doesn't exist.
01458  * \note Should be called with the "queues" container locked. 
01459 */
01460 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
01461 {
01462    struct ast_variable *v;
01463    struct call_queue *q, tmpq = {
01464       .name = queuename,   
01465    };
01466    struct member *m;
01467    struct ao2_iterator mem_iter;
01468    char *interface = NULL;
01469    const char *tmp_name;
01470    char *tmp;
01471    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01472 
01473    /* Static queues override realtime. */
01474    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
01475       ao2_lock(q);
01476       if (!q->realtime) {
01477          if (q->dead) {
01478             ao2_unlock(q);
01479             queue_unref(q);
01480             return NULL;
01481          } else {
01482             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01483             ao2_unlock(q);
01484             return q;
01485          }
01486       }
01487       queue_unref(q);
01488    } else if (!member_config)
01489       /* Not found in the list, and it's not realtime ... */
01490       return NULL;
01491 
01492    /* Check if queue is defined in realtime. */
01493    if (!queue_vars) {
01494       /* Delete queue from in-core list if it has been deleted in realtime. */
01495       if (q) {
01496          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01497             found condition... So we might delete an in-core queue
01498             in case of DB failure. */
01499          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
01500 
01501          q->dead = 1;
01502          /* Delete if unused (else will be deleted when last caller leaves). */
01503          ao2_unlink(queues, q);
01504          ao2_unlock(q);
01505          queue_unref(q);
01506       }
01507       return NULL;
01508    }
01509 
01510    /* Create a new queue if an in-core entry does not exist yet. */
01511    if (!q) {
01512       struct ast_variable *tmpvar = NULL;
01513       if (!(q = alloc_queue(queuename)))
01514          return NULL;
01515       ao2_lock(q);
01516       clear_queue(q);
01517       q->realtime = 1;
01518       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
01519        * will allocate the members properly
01520        */
01521       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01522          if (!strcasecmp(tmpvar->name, "strategy")) {
01523             q->strategy = strat2int(tmpvar->value);
01524             if (q->strategy < 0) {
01525                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01526                tmpvar->value, q->name);
01527                q->strategy = QUEUE_STRATEGY_RINGALL;
01528             }
01529             break;
01530          }
01531       }
01532       /* We traversed all variables and didn't find a strategy */
01533       if (!tmpvar)
01534          q->strategy = QUEUE_STRATEGY_RINGALL;
01535       ao2_link(queues, q);
01536    }
01537    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01538 
01539    memset(tmpbuf, 0, sizeof(tmpbuf));
01540    for (v = queue_vars; v; v = v->next) {
01541       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01542       if ((tmp = strchr(v->name, '_'))) {
01543          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01544          tmp_name = tmpbuf;
01545          tmp = tmpbuf;
01546          while ((tmp = strchr(tmp, '_')))
01547             *tmp++ = '-';
01548       } else
01549          tmp_name = v->name;
01550 
01551       if (!ast_strlen_zero(v->value)) {
01552          /* Don't want to try to set the option if the value is empty */
01553          queue_set_param(q, tmp_name, v->value, -1, 0);
01554       }
01555    }
01556 
01557    /* Temporarily set realtime members dead so we can detect deleted ones. 
01558     * Also set the membercount correctly for realtime*/
01559    mem_iter = ao2_iterator_init(q->members, 0);
01560    while ((m = ao2_iterator_next(&mem_iter))) {
01561       q->membercount++;
01562       if (m->realtime)
01563          m->dead = 1;
01564       ao2_ref(m, -1);
01565    }
01566 
01567    while ((interface = ast_category_browse(member_config, interface))) {
01568       rt_handle_member_record(q, interface,
01569          ast_variable_retrieve(member_config, interface, "uniqueid"),
01570          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
01571          ast_variable_retrieve(member_config, interface, "penalty"),
01572          ast_variable_retrieve(member_config, interface, "paused"),
01573          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01574    }
01575 
01576    /* Delete all realtime members that have been deleted in DB. */
01577    mem_iter = ao2_iterator_init(q->members, 0);
01578    while ((m = ao2_iterator_next(&mem_iter))) {
01579       if (m->dead) {
01580          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01581          ao2_unlink(q->members, m);
01582          remove_from_interfaces(m->state_interface, 0);
01583          q->membercount--;
01584       }
01585       ao2_ref(m, -1);
01586    }
01587 
01588    ao2_unlock(q);
01589 
01590    return q;
01591 }
01592 
01593 static struct call_queue *load_realtime_queue(const char *queuename)
01594 {
01595    struct ast_variable *queue_vars;
01596    struct ast_config *member_config = NULL;
01597    struct call_queue *q = NULL, tmpq = {
01598       .name = queuename,   
01599    };
01600 
01601    /* Find the queue in the in-core list first. */
01602    q = ao2_find(queues, &tmpq, OBJ_POINTER);
01603 
01604    if (!q || q->realtime) {
01605       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
01606          queue operations while waiting for the DB.
01607 
01608          This will be two separate database transactions, so we might
01609          see queue parameters as they were before another process
01610          changed the queue and member list as it was after the change.
01611          Thus we might see an empty member list when a queue is
01612          deleted. In practise, this is unlikely to cause a problem. */
01613 
01614       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01615       if (queue_vars) {
01616          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01617          if (!member_config) {
01618             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01619             ast_variables_destroy(queue_vars);
01620             return NULL;
01621          }
01622       }
01623 
01624       ao2_lock(queues);
01625       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01626       if (member_config)
01627          ast_config_destroy(member_config);
01628       if (queue_vars)
01629          ast_variables_destroy(queue_vars);
01630       ao2_unlock(queues);
01631 
01632    } else {
01633       update_realtime_members(q);
01634    }
01635    return q;
01636 }
01637 
01638 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
01639 {
01640    int ret = -1;
01641 
01642    if (ast_strlen_zero(mem->rt_uniqueid))
01643       return ret;
01644 
01645    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01646       ret = 0;
01647 
01648    return ret;
01649 }
01650 
01651 
01652 static void update_realtime_members(struct call_queue *q)
01653 {
01654    struct ast_config *member_config = NULL;
01655    struct member *m;
01656    char *interface = NULL;
01657    struct ao2_iterator mem_iter;
01658 
01659    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01660       /*This queue doesn't have realtime members*/
01661       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01662       return;
01663    }
01664 
01665    ao2_lock(queues);
01666    ao2_lock(q);
01667    
01668    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01669    mem_iter = ao2_iterator_init(q->members, 0);
01670    while ((m = ao2_iterator_next(&mem_iter))) {
01671       if (m->realtime)
01672          m->dead = 1;
01673       ao2_ref(m, -1);
01674    }
01675 
01676    while ((interface = ast_category_browse(member_config, interface))) {
01677       rt_handle_member_record(q, interface,
01678          ast_variable_retrieve(member_config, interface, "uniqueid"),
01679          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01680          ast_variable_retrieve(member_config, interface, "penalty"),
01681          ast_variable_retrieve(member_config, interface, "paused"),
01682          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01683    }
01684 
01685    /* Delete all realtime members that have been deleted in DB. */
01686    mem_iter = ao2_iterator_init(q->members, 0);
01687    while ((m = ao2_iterator_next(&mem_iter))) {
01688       if (m->dead) {
01689          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01690          ao2_unlink(q->members, m);
01691          remove_from_interfaces(m->state_interface, 0);
01692          q->membercount--;
01693       }
01694       ao2_ref(m, -1);
01695    }
01696    ao2_unlock(q);
01697    ao2_unlock(queues);
01698    ast_config_destroy(member_config);
01699 }
01700 
01701 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule)
01702 {
01703    struct call_queue *q;
01704    struct queue_ent *cur, *prev = NULL;
01705    int res = -1;
01706    int pos = 0;
01707    int inserted = 0;
01708    enum queue_member_status status;
01709    int exit = 0;
01710 
01711    if (!(q = load_realtime_queue(queuename)))
01712       return res;
01713 
01714    ao2_lock(queues);
01715    ao2_lock(q);
01716 
01717    copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
01718    qe->pr = AST_LIST_FIRST(&qe->qe_rules);
01719 
01720    /* This is our one */
01721    while (!exit) {
01722       status = get_member_status(q, qe->max_penalty, qe->min_penalty);
01723       if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
01724          *reason = QUEUE_JOINEMPTY;
01725       else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01726          *reason = QUEUE_JOINUNAVAIL;
01727       else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01728          *reason = QUEUE_JOINUNAVAIL;
01729       else if (q->maxlen && (q->count >= q->maxlen))
01730          *reason = QUEUE_FULL;
01731       else {
01732          /* There's space for us, put us at the right position inside
01733           * the queue.
01734           * Take into account the priority of the calling user */
01735          inserted = 0;
01736          prev = NULL;
01737          cur = q->head;
01738          while (cur) {
01739             /* We have higher priority than the current user, enter
01740              * before him, after all the other users with priority
01741              * higher or equal to our priority. */
01742             if ((!inserted) && (qe->prio > cur->prio)) {
01743                insert_entry(q, prev, qe, &pos);
01744                inserted = 1;
01745             }
01746             cur->pos = ++pos;
01747             prev = cur;
01748             cur = cur->next;
01749          }
01750          /* No luck, join at the end of the queue */
01751          if (!inserted)
01752             insert_entry(q, prev, qe, &pos);
01753          ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01754          ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01755          ast_copy_string(qe->context, q->context, sizeof(qe->context));
01756          q->count++;
01757          res = 0;
01758          manager_event(EVENT_FLAG_CALL, "Join",
01759             "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01760             qe->chan->name,
01761             S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01762             S_OR(qe->chan->cid.cid_name, "unknown"),
01763             q->name, qe->pos, q->count, qe->chan->uniqueid );
01764          ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01765       }
01766       if (!exit && qe->pr && res) {
01767          /* We failed to join the queue, but perhaps we can join if we move
01768           * to the next defined penalty rule
01769           */
01770          update_qe_rule(qe);
01771       } else {
01772          exit = 1;
01773       }
01774    }
01775    ao2_unlock(q);
01776    ao2_unlock(queues);
01777 
01778    return res;
01779 }
01780 
01781 static int play_file(struct ast_channel *chan, const char *filename)
01782 {
01783    int res;
01784 
01785    if (ast_strlen_zero(filename)) {
01786       return 0;
01787    }
01788 
01789    ast_stopstream(chan);
01790 
01791    res = ast_streamfile(chan, filename, chan->language);
01792    if (!res)
01793       res = ast_waitstream(chan, AST_DIGIT_ANY);
01794 
01795    ast_stopstream(chan);
01796 
01797    return res;
01798 }
01799 
01800 /*!
01801  * \brief Check for valid exit from queue via goto
01802  * \retval 0 if failure
01803  * \retval 1 if successful
01804 */
01805 static int valid_exit(struct queue_ent *qe, char digit)
01806 {
01807    int digitlen = strlen(qe->digits);
01808 
01809    /* Prevent possible buffer overflow */
01810    if (digitlen < sizeof(qe->digits) - 2) {
01811       qe->digits[digitlen] = digit;
01812       qe->digits[digitlen + 1] = '\0';
01813    } else {
01814       qe->digits[0] = '\0';
01815       return 0;
01816    }
01817 
01818    /* If there's no context to goto, short-circuit */
01819    if (ast_strlen_zero(qe->context))
01820       return 0;
01821 
01822    /* If the extension is bad, then reset the digits to blank */
01823    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01824       qe->digits[0] = '\0';
01825       return 0;
01826    }
01827 
01828    /* We have an exact match */
01829    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01830       qe->valid_digits = 1;
01831       /* Return 1 on a successful goto */
01832       return 1;
01833    }
01834 
01835    return 0;
01836 }
01837 
01838 static int say_position(struct queue_ent *qe, int ringing)
01839 {
01840    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
01841    int say_thanks = 1;
01842    time_t now;
01843 
01844    /* Let minannouncefrequency seconds pass between the start of each position announcement */
01845    time(&now);
01846    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
01847       return 0;
01848 
01849    /* If either our position has changed, or we are over the freq timer, say position */
01850    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01851       return 0;
01852 
01853    if (ringing) {
01854       ast_indicate(qe->chan,-1);
01855    } else {
01856       ast_moh_stop(qe->chan);
01857    }
01858 
01859    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
01860       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
01861       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
01862       qe->pos <= qe->parent->announcepositionlimit))
01863          announceposition = 1;
01864 
01865 
01866    if (announceposition == 1) {
01867       /* Say we're next, if we are */
01868       if (qe->pos == 1) {
01869          res = play_file(qe->chan, qe->parent->sound_next);
01870          if (res)
01871             goto playout;
01872          else
01873             goto posout;
01874       } else {
01875          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01876             /* More than Case*/
01877             res = play_file(qe->chan, qe->parent->queue_quantity1);
01878             if (res)
01879                goto playout;
01880             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01881             if (res)
01882                goto playout;
01883          } else {
01884             /* Normal Case */
01885             res = play_file(qe->chan, qe->parent->sound_thereare);
01886             if (res)
01887                goto playout;
01888             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01889             if (res)
01890                goto playout;
01891          }
01892          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01893             /* More than Case*/
01894             res = play_file(qe->chan, qe->parent->queue_quantity2);
01895             if (res)
01896                goto playout;
01897          } else {
01898             res = play_file(qe->chan, qe->parent->sound_calls);
01899             if (res)
01900                goto playout;
01901          }
01902       }
01903    }
01904    /* Round hold time to nearest minute */
01905    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01906 
01907    /* If they have specified a rounding then round the seconds as well */
01908    if (qe->parent->roundingseconds) {
01909       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01910       avgholdsecs *= qe->parent->roundingseconds;
01911    } else {
01912       avgholdsecs = 0;
01913    }
01914 
01915    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01916 
01917    /* If the hold time is >1 min, if it's enabled, and if it's not
01918       supposed to be only once and we have already said it, say it */
01919     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01920         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01921         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01922       res = play_file(qe->chan, qe->parent->sound_holdtime);
01923       if (res)
01924          goto playout;
01925 
01926       if (avgholdmins >= 1) {
01927          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01928          if (res)
01929             goto playout;
01930 
01931          if (avgholdmins == 1) {
01932             res = play_file(qe->chan, qe->parent->sound_minute);
01933             if (res)
01934                goto playout;
01935          } else {
01936             res = play_file(qe->chan, qe->parent->sound_minutes);
01937             if (res)
01938                goto playout;
01939          }
01940       }
01941       if (avgholdsecs >= 1) {
01942          res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01943          if (res)
01944             goto playout;
01945 
01946          res = play_file(qe->chan, qe->parent->sound_seconds);
01947          if (res)
01948             goto playout;
01949       }
01950    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
01951       say_thanks = 0;
01952    }
01953 
01954 posout:
01955    if (qe->parent->announceposition) {
01956       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
01957          qe->chan->name, qe->parent->name, qe->pos);
01958    }
01959    if (say_thanks) {
01960       res = play_file(qe->chan, qe->parent->sound_thanks);
01961    }
01962 playout:
01963    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01964       res = 0;
01965 
01966    /* Set our last_pos indicators */
01967    qe->last_pos = now;
01968    qe->last_pos_said = qe->pos;
01969 
01970    /* Don't restart music on hold if we're about to exit the caller from the queue */
01971    if (!res) {
01972       if (ringing) {
01973          ast_indicate(qe->chan, AST_CONTROL_RINGING);
01974       } else {
01975          ast_moh_start(qe->chan, qe->moh, NULL);
01976       }
01977    }
01978    return res;
01979 }
01980 
01981 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01982 {
01983    int oldvalue;
01984 
01985    /* Calculate holdtime using an exponential average */
01986    /* Thanks to SRT for this contribution */
01987    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01988 
01989    ao2_lock(qe->parent);
01990    oldvalue = qe->parent->holdtime;
01991    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01992    ao2_unlock(qe->parent);
01993 }
01994 
01995 /*! \brief Caller leaving queue.
01996  * 
01997  * Search the queue to find the leaving client, if found remove from queue
01998  * create manager event, move others up the queue.
01999 */
02000 static void leave_queue(struct queue_ent *qe)
02001 {
02002    struct call_queue *q;
02003    struct queue_ent *current, *prev = NULL;
02004    struct penalty_rule *pr_iter;
02005    int pos = 0;
02006 
02007    if (!(q = qe->parent))
02008       return;
02009    queue_ref(q);
02010    ao2_lock(q);
02011 
02012    prev = NULL;
02013    for (current = q->head; current; current = current->next) {
02014       if (current == qe) {
02015          q->count--;
02016 
02017          /* Take us out of the queue */
02018          manager_event(EVENT_FLAG_CALL, "Leave",
02019             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02020             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02021          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02022          /* Take us out of the queue */
02023          if (prev)
02024             prev->next = current->next;
02025          else
02026             q->head = current->next;
02027          /* Free penalty rules */
02028          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02029             ast_free(pr_iter);
02030       } else {
02031          /* Renumber the people after us in the queue based on a new count */
02032          current->pos = ++pos;
02033          prev = current;
02034       }
02035    }
02036    ao2_unlock(q);
02037 
02038    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02039    if (q->realtime) {
02040       struct ast_variable *var;
02041       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02042          q->dead = 1;
02043       } else {
02044          ast_variables_destroy(var);
02045       }
02046    }
02047 
02048    if (q->dead) { 
02049       /* It's dead and nobody is in it, so kill it */
02050       ao2_unlink(queues, q);
02051    }
02052    /* unref the explicit ref earlier in the function */
02053    queue_unref(q);
02054 }
02055 
02056 /*! \brief Hang up a list of outgoing calls */
02057 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
02058 {
02059    struct callattempt *oo;
02060 
02061    while (outgoing) {
02062       /* Hangup any existing lines we have open */
02063       if (outgoing->chan && (outgoing->chan != exception))
02064          ast_hangup(outgoing->chan);
02065       oo = outgoing;
02066       outgoing = outgoing->q_next;
02067       if (oo->member)
02068          ao2_ref(oo->member, -1);
02069       ast_free(oo);
02070    }
02071 }
02072 
02073 /*!
02074  * \brief Get the number of members available to accept a call.
02075  *
02076  * \note The queue passed in should be locked prior to this function call
02077  *
02078  * \param[in] q The queue for which we are couting the number of available members
02079  * \return Return the number of available members in queue q
02080  */
02081 static int num_available_members(struct call_queue *q)
02082 {
02083    struct member *mem;
02084    int avl = 0;
02085    struct ao2_iterator mem_iter;
02086 
02087    mem_iter = ao2_iterator_init(q->members, 0);
02088    while ((mem = ao2_iterator_next(&mem_iter))) {
02089       switch (mem->status) {
02090       case AST_DEVICE_INUSE:
02091          if (!q->ringinuse)
02092             break;
02093          /* else fall through */
02094       case AST_DEVICE_NOT_INUSE:
02095       case AST_DEVICE_UNKNOWN:
02096          if (!mem->paused) {
02097             avl++;
02098          }
02099          break;
02100       }
02101       ao2_ref(mem, -1);
02102 
02103       /* If autofill is not enabled or if the queue's strategy is ringall, then
02104        * we really don't care about the number of available members so much as we
02105        * do that there is at least one available.
02106        *
02107        * In fact, we purposely will return from this function stating that only
02108        * one member is available if either of those conditions hold. That way,
02109        * functions which determine what action to take based on the number of available
02110        * members will operate properly. The reasoning is that even if multiple
02111        * members are available, only the head caller can actually be serviced.
02112        */
02113       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02114          break;
02115       }
02116    }
02117 
02118    return avl;
02119 }
02120 
02121 /* traverse all defined queues which have calls waiting and contain this member
02122    return 0 if no other queue has precedence (higher weight) or 1 if found  */
02123 static int compare_weight(struct call_queue *rq, struct member *member)
02124 {
02125    struct call_queue *q;
02126    struct member *mem;
02127    int found = 0;
02128    struct ao2_iterator queue_iter;
02129    
02130    /* q's lock and rq's lock already set by try_calling()
02131     * to solve deadlock */
02132    queue_iter = ao2_iterator_init(queues, 0);
02133    while ((q = ao2_iterator_next(&queue_iter))) {
02134       if (q == rq) { /* don't check myself, could deadlock */
02135          queue_unref(q);
02136          continue;
02137       }
02138       ao2_lock(q);
02139       if (q->count && q->members) {
02140          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02141             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02142             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02143                ast_debug(1, "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);
02144                found = 1;
02145             }
02146             ao2_ref(mem, -1);
02147          }
02148       }
02149       ao2_unlock(q);
02150       queue_unref(q);
02151       if (found) {
02152          break;
02153       }
02154    }
02155    return found;
02156 }
02157 
02158 /*! \brief common hangup actions */
02159 static void do_hang(struct callattempt *o)
02160 {
02161    o->stillgoing = 0;
02162    ast_hangup(o->chan);
02163    o->chan = NULL;
02164 }
02165 
02166 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
02167 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
02168 {
02169    struct ast_str *buf = ast_str_alloca(len + 1);
02170    char *tmp;
02171 
02172    if (pbx_builtin_serialize_variables(chan, &buf)) {
02173       int i, j;
02174 
02175       /* convert "\n" to "\nVariable: " */
02176       strcpy(vars, "Variable: ");
02177       tmp = buf->str;
02178 
02179       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02180          vars[j] = tmp[i];
02181 
02182          if (tmp[i + 1] == '\0')
02183             break;
02184          if (tmp[i] == '\n') {
02185             vars[j++] = '\r';
02186             vars[j++] = '\n';
02187 
02188             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02189             j += 9;
02190          }
02191       }
02192       if (j > len - 3)
02193          j = len - 3;
02194       vars[j++] = '\r';
02195       vars[j++] = '\n';
02196       vars[j] = '\0';
02197    } else {
02198       /* there are no channel variables; leave it blank */
02199       *vars = '\0';
02200    }
02201    return vars;
02202 }
02203 
02204 /*! 
02205  * \brief Part 2 of ring_one
02206  *
02207  * Does error checking before attempting to request a channel and call a member. 
02208  * This function is only called from ring_one(). 
02209  * Failure can occur if:
02210  * - Agent on call
02211  * - Agent is paused
02212  * - Wrapup time not expired
02213  * - Priority by another queue
02214  *
02215  * \retval 1 on success to reach a free agent
02216  * \retval 0 on failure to get agent.
02217  */
02218 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
02219 {
02220    int res;
02221    int status;
02222    char tech[256];
02223    char *location;
02224    const char *macrocontext, *macroexten;
02225 
02226    /* on entry here, we know that tmp->chan == NULL */
02227    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02228       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02229       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
02230             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02231       if (qe->chan->cdr)
02232          ast_cdr_busy(qe->chan->cdr);
02233       tmp->stillgoing = 0;
02234       (*busies)++;
02235       return 0;
02236    }
02237 
02238    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02239       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02240       if (qe->chan->cdr)
02241          ast_cdr_busy(qe->chan->cdr);
02242       tmp->stillgoing = 0;
02243       return 0;
02244    }
02245 
02246    if (tmp->member->paused) {
02247       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02248       if (qe->chan->cdr)
02249          ast_cdr_busy(qe->chan->cdr);
02250       tmp->stillgoing = 0;
02251       return 0;
02252    }
02253    if (use_weight && compare_weight(qe->parent,tmp->member)) {
02254       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02255       if (qe->chan->cdr)
02256          ast_cdr_busy(qe->chan->cdr);
02257       tmp->stillgoing = 0;
02258       (*busies)++;
02259       return 0;
02260    }
02261 
02262    ast_copy_string(tech, tmp->interface, sizeof(tech));
02263    if ((location = strchr(tech, '/')))
02264       *location++ = '\0';
02265    else
02266       location = "";
02267 
02268    /* Request the peer */
02269    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02270    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02271       if (qe->chan->cdr)
02272          ast_cdr_busy(qe->chan->cdr);
02273       tmp->stillgoing = 0;
02274 
02275       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02276 
02277       ao2_lock(qe->parent);
02278       qe->parent->rrpos++;
02279       qe->linpos++;
02280       ao2_unlock(qe->parent);
02281 
02282 
02283       (*busies)++;
02284       return 0;
02285    }
02286    
02287    tmp->chan->appl = "AppQueue";
02288    tmp->chan->data = "(Outgoing Line)";
02289    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02290    if (tmp->chan->cid.cid_num)
02291       ast_free(tmp->chan->cid.cid_num);
02292    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02293    if (tmp->chan->cid.cid_name)
02294       ast_free(tmp->chan->cid.cid_name);
02295    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02296    if (tmp->chan->cid.cid_ani)
02297       ast_free(tmp->chan->cid.cid_ani);
02298    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02299 
02300    /* Inherit specially named variables from parent channel */
02301    ast_channel_inherit_variables(qe->chan, tmp->chan);
02302 
02303    /* Presense of ADSI CPE on outgoing channel follows ours */
02304    tmp->chan->adsicpe = qe->chan->adsicpe;
02305 
02306    /* Inherit context and extension */
02307    ast_channel_lock(qe->chan);
02308    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02309    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02310    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02311    if (!ast_strlen_zero(macroexten))
02312       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02313    else
02314       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02315    ast_channel_unlock(qe->chan);
02316 
02317    /* Place the call, but don't wait on the answer */
02318    if ((res = ast_call(tmp->chan, location, 0))) {
02319       /* Again, keep going even if there's an error */
02320       ast_debug(1, "ast call on peer returned %d\n", res);
02321       ast_verb(3, "Couldn't call %s\n", tmp->interface);
02322       do_hang(tmp);
02323       (*busies)++;
02324       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02325       return 0;
02326    } else if (qe->parent->eventwhencalled) {
02327       char vars[2048];
02328 
02329       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02330                "Queue: %s\r\n"
02331                "AgentCalled: %s\r\n"
02332                "AgentName: %s\r\n"
02333                "ChannelCalling: %s\r\n"
02334                "DestinationChannel: %s\r\n"
02335                "CallerIDNum: %s\r\n"
02336                "CallerIDName: %s\r\n"
02337                "Context: %s\r\n"
02338                "Extension: %s\r\n"
02339                "Priority: %d\r\n"
02340                "Uniqueid: %s\r\n"
02341                "%s",
02342                qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02343                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02344                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02345                qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02346                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02347       ast_verb(3, "Called %s\n", tmp->interface);
02348    }
02349 
02350    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02351    return 1;
02352 }
02353 
02354 /*! \brief find the entry with the best metric, or NULL */
02355 static struct callattempt *find_best(struct callattempt *outgoing)
02356 {
02357    struct callattempt *best = NULL, *cur;
02358 
02359    for (cur = outgoing; cur; cur = cur->q_next) {
02360       if (cur->stillgoing &&              /* Not already done */
02361          !cur->chan &&              /* Isn't already going */
02362          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02363          best = cur;
02364       }
02365    }
02366 
02367    return best;
02368 }
02369 
02370 /*! 
02371  * \brief Place a call to a queue member.
02372  *
02373  * Once metrics have been calculated for each member, this function is used
02374  * to place a call to the appropriate member (or members). The low-level
02375  * channel-handling and error detection is handled in ring_entry
02376  *
02377  * \retval 1 if a member was called successfully
02378  * \retval 0 otherwise
02379  */
02380 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
02381 {
02382    int ret = 0;
02383 
02384    while (ret == 0) {
02385       struct callattempt *best = find_best(outgoing);
02386       if (!best) {
02387          ast_debug(1, "Nobody left to try ringing in queue\n");
02388          break;
02389       }
02390       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02391          struct callattempt *cur;
02392          /* Ring everyone who shares this best metric (for ringall) */
02393          for (cur = outgoing; cur; cur = cur->q_next) {
02394             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02395                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02396                ret |= ring_entry(qe, cur, busies);
02397             }
02398          }
02399       } else {
02400          /* Ring just the best channel */
02401          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02402          ret = ring_entry(qe, best, busies);
02403       }
02404       
02405       /* If we have timed out, break out */
02406       if (qe->expire && (time(NULL) >= qe->expire)) {
02407          ast_debug(1, "Queue timed out while ringing members.\n");
02408          ret = 0;
02409          break;
02410       }
02411    }
02412 
02413    return ret;
02414 }
02415 
02416 /*! \brief Search for best metric and add to Round Robbin queue */
02417 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
02418 {
02419    struct callattempt *best = find_best(outgoing);
02420 
02421    if (best) {
02422       /* Ring just the best channel */
02423       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02424       qe->parent->rrpos = best->metric % 1000;
02425    } else {
02426       /* Just increment rrpos */
02427       if (qe->parent->wrapped) {
02428          /* No more channels, start over */
02429          qe->parent->rrpos = 0;
02430       } else {
02431          /* Prioritize next entry */
02432          qe->parent->rrpos++;
02433       }
02434    }
02435    qe->parent->wrapped = 0;
02436 
02437    return 0;
02438 }
02439 
02440 /*! \brief Search for best metric and add to Linear queue */
02441 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
02442 {
02443    struct callattempt *best = find_best(outgoing);
02444 
02445    if (best) {
02446       /* Ring just the best channel */
02447       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02448       qe->linpos = best->metric % 1000;
02449    } else {
02450       /* Just increment rrpos */
02451       if (qe->linwrapped) {
02452          /* No more channels, start over */
02453          qe->linpos = 0;
02454       } else {
02455          /* Prioritize next entry */
02456          qe->linpos++;
02457       }
02458    }
02459    qe->linwrapped = 0;
02460 
02461    return 0;
02462 }
02463 
02464 /*! \brief Playback announcement to queued members if peroid has elapsed */
02465 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
02466 {
02467    int res = 0;
02468    time_t now;
02469 
02470    /* Get the current time */
02471    time(&now);
02472 
02473    /* Check to see if it is time to announce */
02474    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02475       return 0;
02476 
02477    /* Stop the music on hold so we can play our own file */
02478    if (ringing)
02479       ast_indicate(qe->chan,-1);
02480    else
02481       ast_moh_stop(qe->chan);
02482 
02483    ast_verb(3, "Playing periodic announcement\n");
02484    
02485    if (qe->parent->randomperiodicannounce) {
02486       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02487    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
02488       ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02489       qe->last_periodic_announce_sound = 0;
02490    }
02491    
02492    /* play the announcement */
02493    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02494 
02495    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02496       res = 0;
02497 
02498    /* Resume Music on Hold if the caller is going to stay in the queue */
02499    if (!res) {
02500       if (ringing)
02501          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02502       else
02503          ast_moh_start(qe->chan, qe->moh, NULL);
02504    }
02505 
02506    /* update last_periodic_announce_time */
02507    qe->last_periodic_announce_time = now;
02508 
02509    /* Update the current periodic announcement to the next announcement */
02510    if (!qe->parent->randomperiodicannounce) {
02511       qe->last_periodic_announce_sound++;
02512    }
02513    
02514    return res;
02515 }
02516 
02517 /*! \brief Record that a caller gave up on waiting in queue */
02518 static void record_abandoned(struct queue_ent *qe)
02519 {
02520    ao2_lock(qe->parent);
02521    set_queue_variables(qe->parent, qe->chan);
02522    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02523       "Queue: %s\r\n"
02524       "Uniqueid: %s\r\n"
02525       "Position: %d\r\n"
02526       "OriginalPosition: %d\r\n"
02527       "HoldTime: %d\r\n",
02528       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02529 
02530    qe->parent->callsabandoned++;
02531    ao2_unlock(qe->parent);
02532 }
02533 
02534 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
02535 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
02536 {
02537    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02538    if (qe->parent->eventwhencalled)
02539       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02540                   "Queue: %s\r\n"
02541                   "Uniqueid: %s\r\n"
02542                   "Channel: %s\r\n"
02543                   "Member: %s\r\n"
02544                   "MemberName: %s\r\n"
02545                   "Ringtime: %d\r\n",
02546                   qe->parent->name,
02547                   qe->chan->uniqueid,
02548                   qe->chan->name,
02549                   interface,
02550                   membername,
02551                   rnatime);
02552    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02553    if (qe->parent->autopause && pause) {
02554       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02555          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02556       } else {
02557          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02558       }
02559    }
02560    return;
02561 }
02562 
02563 #define AST_MAX_WATCHERS 256
02564 /*! \brief Wait for a member to answer the call
02565  *
02566  * \param[in] qe the queue_ent corresponding to the caller in the queue
02567  * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
02568  * \param[in] to the amount of time (in milliseconds) to wait for a response
02569  * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
02570  * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
02571  * \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
02572  * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
02573  */
02574 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02575 {
02576    const char *queue = qe->parent->name;
02577    struct callattempt *o, *start = NULL, *prev = NULL;
02578    int status;
02579    int numbusies = prebusies;
02580    int numnochan = 0;
02581    int stillgoing = 0;
02582    int orig = *to;
02583    struct ast_frame *f;
02584    struct callattempt *peer = NULL;
02585    struct ast_channel *winner;
02586    struct ast_channel *in = qe->chan;
02587    char on[80] = "";
02588    char membername[80] = "";
02589    long starttime = 0;
02590    long endtime = 0;
02591 #ifdef HAVE_EPOLL
02592    struct callattempt *epollo;
02593 #endif
02594 
02595    starttime = (long) time(NULL);
02596 #ifdef HAVE_EPOLL
02597    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02598       if (epollo->chan)
02599          ast_poll_channel_add(in, epollo->chan);
02600    }
02601 #endif
02602    
02603    while (*to && !peer) {
02604       int numlines, retry, pos = 1;
02605       struct ast_channel *watchers[AST_MAX_WATCHERS];
02606       watchers[0] = in;
02607       start = NULL;
02608 
02609       for (retry = 0; retry < 2; retry++) {
02610          numlines = 0;
02611          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02612             if (o->stillgoing) { /* Keep track of important channels */
02613                stillgoing = 1;
02614                if (o->chan) {
02615                   watchers[pos++] = o->chan;
02616                   if (!start)
02617                      start = o;
02618                   else
02619                      prev->call_next = o;
02620                   prev = o;
02621                }
02622             }
02623             numlines++;
02624          }
02625          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02626             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02627             break;
02628          /* On "ringall" strategy we only move to the next penalty level
02629             when *all* ringing phones are done in the current penalty level */
02630          ring_one(qe, outgoing, &numbusies);
02631          /* and retry... */
02632       }
02633       if (pos == 1 /* not found */) {
02634          if (numlines == (numbusies + numnochan)) {
02635             ast_debug(1, "Everyone is busy at this time\n");
02636          } else {
02637             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02638          }
02639          *to = 0;
02640          return NULL;
02641       }
02642       winner = ast_waitfor_n(watchers, pos, to);
02643       for (o = start; o; o = o->call_next) {
02644          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02645             if (!peer) {
02646                ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02647                peer = o;
02648             }
02649          } else if (o->chan && (o->chan == winner)) {
02650 
02651             ast_copy_string(on, o->member->interface, sizeof(on));
02652             ast_copy_string(membername, o->member->membername, sizeof(membername));
02653 
02654             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02655                ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02656                numnochan++;
02657                do_hang(o);
02658                winner = NULL;
02659                continue;
02660             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02661                char tmpchan[256];
02662                char *stuff;
02663                char *tech;
02664 
02665                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02666                if ((stuff = strchr(tmpchan, '/'))) {
02667                   *stuff++ = '\0';
02668                   tech = tmpchan;
02669                } else {
02670                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02671                   stuff = tmpchan;
02672                   tech = "Local";
02673                }
02674                /* Before processing channel, go ahead and check for forwarding */
02675                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02676                /* Setup parameters */
02677                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02678                if (!o->chan) {
02679                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02680                   o->stillgoing = 0;
02681                   numnochan++;
02682                } else {
02683                   ast_channel_inherit_variables(in, o->chan);
02684                   ast_channel_datastore_inherit(in, o->chan);
02685                   if (o->chan->cid.cid_num)
02686                      ast_free(o->chan->cid.cid_num);
02687                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02688 
02689                   if (o->chan->cid.cid_name)
02690                      ast_free(o->chan->cid.cid_name);
02691                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02692 
02693                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02694                   o->chan->cdrflags = in->cdrflags;
02695 
02696                   if (in->cid.cid_ani) {
02697                      if (o->chan->cid.cid_ani)
02698                         ast_free(o->chan->cid.cid_ani);
02699                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02700                   }
02701                   if (o->chan->cid.cid_rdnis)
02702                      ast_free(o->chan->cid.cid_rdnis);
02703                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02704                   if (ast_call(o->chan, tmpchan, 0)) {
02705                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02706                      do_hang(o);
02707                      numnochan++;
02708                   }
02709                }
02710                /* Hangup the original channel now, in case we needed it */
02711                ast_hangup(winner);
02712                continue;
02713             }
02714             f = ast_read(winner);
02715             if (f) {
02716                if (f->frametype == AST_FRAME_CONTROL) {
02717                   switch (f->subclass) {
02718                   case AST_CONTROL_ANSWER:
02719                      /* This is our guy if someone answered. */
02720                      if (!peer) {
02721                         ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02722                         peer = o;
02723                      }
02724                      break;
02725                   case AST_CONTROL_BUSY:
02726                      ast_verb(3, "%s is busy\n", o->chan->name);
02727                      if (in->cdr)
02728                         ast_cdr_busy(in->cdr);
02729                      do_hang(o);
02730                      endtime = (long) time(NULL);
02731                      endtime -= starttime;
02732                      rna(endtime * 1000, qe, on, membername, 0);
02733                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02734                         if (qe->parent->timeoutrestart)
02735                            *to = orig;
02736                         ring_one(qe, outgoing, &numbusies);
02737                      }
02738                      numbusies++;
02739                      break;
02740                   case AST_CONTROL_CONGESTION:
02741                      ast_verb(3, "%s is circuit-busy\n", o->chan->name);
02742                      if (in->cdr)
02743                         ast_cdr_busy(in->cdr);
02744                      endtime = (long) time(NULL);
02745                      endtime -= starttime;
02746                      rna(endtime * 1000, qe, on, membername, 0);
02747                      do_hang(o);
02748                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02749                         if (qe->parent->timeoutrestart)
02750                            *to = orig;
02751                         ring_one(qe, outgoing, &numbusies);
02752                      }
02753                      numbusies++;
02754                      break;
02755                   case AST_CONTROL_RINGING:
02756                      ast_verb(3, "%s is ringing\n", o->chan->name);
02757                      break;
02758                   case AST_CONTROL_OFFHOOK:
02759                      /* Ignore going off hook */
02760                      break;
02761                   default:
02762                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
02763                   }
02764                }
02765                ast_frfree(f);
02766             } else {
02767                endtime = (long) time(NULL) - starttime;
02768                rna(endtime * 1000, qe, on, membername, 1);
02769                do_hang(o);
02770                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02771                   if (qe->parent->timeoutrestart)
02772                      *to = orig;
02773                   ring_one(qe, outgoing, &numbusies);
02774                }
02775             }
02776          }
02777       }
02778       if (winner == in) {
02779          f = ast_read(in);
02780          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02781             /* Got hung up */
02782             *to = -1;
02783             if (f) {
02784                if (f->data.uint32) {
02785                   in->hangupcause = f->data.uint32;
02786                }
02787                ast_frfree(f);
02788             }
02789             return NULL;
02790          }
02791          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02792             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
02793             *to = 0;
02794             ast_frfree(f);
02795             return NULL;
02796          }
02797          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02798             ast_verb(3, "User pressed digit: %c\n", f->subclass);
02799             *to = 0;
02800             *digit = f->subclass;
02801             ast_frfree(f);
02802             return NULL;
02803          }
02804          ast_frfree(f);
02805       }
02806       if (!*to) {
02807          for (o = start; o; o = o->call_next)
02808             rna(orig, qe, o->interface, o->member->membername, 1);
02809       }
02810    }
02811 
02812 #ifdef HAVE_EPOLL
02813    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02814       if (epollo->chan)
02815          ast_poll_channel_del(in, epollo->chan);
02816    }
02817 #endif
02818 
02819    return peer;
02820 }
02821 
02822 /*! 
02823  * \brief Check if we should start attempting to call queue members.
02824  *
02825  * A simple process, really. Count the number of members who are available
02826  * to take our call and then see if we are in a position in the queue at
02827  * which a member could accept our call.
02828  *
02829  * \param[in] qe The caller who wants to know if it is his turn
02830  * \retval 0 It is not our turn
02831  * \retval 1 It is our turn
02832  */
02833 static int is_our_turn(struct queue_ent *qe)
02834 {
02835    struct queue_ent *ch;
02836    int res;
02837    int avl;
02838    int idx = 0;
02839    /* This needs a lock. How many members are available to be served? */
02840    ao2_lock(qe->parent);
02841 
02842    avl = num_available_members(qe->parent);
02843 
02844    ch = qe->parent->head;
02845 
02846    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02847 
02848    while ((idx < avl) && (ch) && (ch != qe)) {
02849       if (!ch->pending)
02850          idx++;
02851       ch = ch->next;       
02852    }
02853 
02854    ao2_unlock(qe->parent);
02855 
02856    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02857    if (ch && idx < avl) {
02858       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02859       res = 1;
02860    } else {
02861       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02862       res = 0;
02863    }
02864 
02865    return res;
02866 }
02867 
02868 /*!
02869  * \brief update rules for queues
02870  *
02871  * Calculate min/max penalties making sure if relative they stay within bounds.
02872  * Update queues penalty and set dialplan vars, goto next list entry.
02873 */
02874 static void update_qe_rule(struct queue_ent *qe)
02875 {
02876    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02877    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02878    char max_penalty_str[20], min_penalty_str[20]; 
02879    /* a relative change to the penalty could put it below 0 */
02880    if (max_penalty < 0)
02881       max_penalty = 0;
02882    if (min_penalty < 0)
02883       min_penalty = 0;
02884    if (min_penalty > max_penalty)
02885       min_penalty = max_penalty;
02886    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02887    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02888    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02889    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02890    qe->max_penalty = max_penalty;
02891    qe->min_penalty = min_penalty;
02892    ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
02893    qe->pr = AST_LIST_NEXT(qe->pr, list);
02894 }
02895 
02896 /*! \brief The waiting areas for callers who are not actively calling members
02897  *
02898  * This function is one large loop. This function will return if a caller
02899  * either exits the queue or it becomes that caller's turn to attempt calling
02900  * queue members. Inside the loop, we service the caller with periodic announcements,
02901  * holdtime announcements, etc. as configured in queues.conf
02902  *
02903  * \retval  0 if the caller's turn has arrived
02904  * \retval -1 if the caller should exit the queue.
02905  */
02906 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02907 {
02908    int res = 0;
02909 
02910    /* This is the holding pen for callers 2 through maxlen */
02911    for (;;) {
02912       enum queue_member_status status = QUEUE_NORMAL;
02913       int exit = 0;
02914 
02915       if (is_our_turn(qe))
02916          break;
02917 
02918       /* If we have timed out, break out */
02919       if (qe->expire && (time(NULL) >= qe->expire)) {
02920          *reason = QUEUE_TIMEOUT;
02921          break;
02922       }
02923 
02924       /* If we are going to exit due to a leavewhenempty condition, we should
02925        * actually attempt to keep the caller in the queue until we have
02926        * exhausted all penalty rules.
02927        */
02928       for (; !exit || qe->pr; update_qe_rule(qe)) {
02929          status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02930   
02931          if (!qe->pr || status == QUEUE_NORMAL) {
02932             break;
02933          }
02934 
02935          /* leave the queue if no agents, if enabled */
02936          if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
02937                ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02938                ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
02939             continue;
02940          } else {
02941             exit = 1;
02942          }
02943       }
02944 
02945       if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
02946          *reason = QUEUE_LEAVEEMPTY;
02947          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02948          leave_queue(qe);
02949          break;
02950       }
02951 
02952       /* leave the queue if no reachable agents, if enabled */
02953       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02954          *reason = QUEUE_LEAVEUNAVAIL;
02955          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02956          leave_queue(qe);
02957          break;
02958       }
02959       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
02960          *reason = QUEUE_LEAVEUNAVAIL;
02961          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02962          leave_queue(qe);
02963          break;
02964       }
02965 
02966       /* Make a position announcement, if enabled */
02967       if (qe->parent->announcefrequency &&
02968          (res = say_position(qe,ringing)))
02969          break;
02970 
02971       /* If we have timed out, break out */
02972       if (qe->expire && (time(NULL) >= qe->expire)) {
02973          *reason = QUEUE_TIMEOUT;
02974          break;
02975       }
02976 
02977       /* Make a periodic announcement, if enabled */
02978       if (qe->parent->periodicannouncefrequency &&
02979          (res = say_periodic_announcement(qe,ringing)))
02980          break;
02981       
02982       /* see if we need to move to the next penalty level for this queue */
02983       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
02984          update_qe_rule(qe);
02985       }
02986 
02987       /* If we have timed out, break out */
02988       if (qe->expire && (time(NULL) >= qe->expire)) {
02989          *reason = QUEUE_TIMEOUT;
02990          break;
02991       }
02992       
02993       /* Wait a second before checking again */
02994       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02995          if (res > 0 && !valid_exit(qe, res))
02996             res = 0;
02997          else
02998             break;
02999       }
03000       
03001       /* If we have timed out, break out */
03002       if (qe->expire && (time(NULL) >= qe->expire)) {
03003          *reason = QUEUE_TIMEOUT;
03004          break;
03005       }
03006    }
03007 
03008    return res;
03009 }
03010 
03011 /*!
03012  * \brief update the queue status
03013  * \retval Always 0
03014 */
03015 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
03016 {
03017    struct member *mem;
03018    struct call_queue *qtmp;
03019    struct ao2_iterator queue_iter;  
03020    
03021    if (shared_lastcall) {
03022       queue_iter = ao2_iterator_init(queues, 0);
03023       while ((qtmp = ao2_iterator_next(&queue_iter))) {
03024          ao2_lock(qtmp);
03025          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03026             time(&mem->lastcall);
03027             mem->calls++;
03028             mem->lastqueue = q;
03029             ao2_ref(mem, -1);
03030          }
03031          ao2_unlock(qtmp);
03032          ao2_ref(qtmp, -1);
03033       }
03034    } else {
03035       ao2_lock(q);
03036       time(&member->lastcall);
03037       member->calls++;
03038       member->lastqueue = q;
03039       ao2_unlock(q);
03040    }  
03041    ao2_lock(q);
03042    q->callscompleted++;
03043    if (callcompletedinsl)
03044       q->callscompletedinsl++;
03045    ao2_unlock(q);
03046    return 0;
03047 }
03048 
03049 /*! \brief Calculate the metric of each member in the outgoing callattempts
03050  *
03051  * A numeric metric is given to each member depending on the ring strategy used
03052  * by the queue. Members with lower metrics will be called before members with
03053  * higher metrics
03054  * \retval -1 if penalties are exceeded
03055  * \retval 0 otherwise
03056  */
03057 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
03058 {
03059    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03060       return -1;
03061 
03062    switch (q->strategy) {
03063    case QUEUE_STRATEGY_RINGALL:
03064       /* Everyone equal, except for penalty */
03065       tmp->metric = mem->penalty * 1000000;
03066       break;
03067    case QUEUE_STRATEGY_LINEAR:
03068       if (pos < qe->linpos) {
03069          tmp->metric = 1000 + pos;
03070       } else {
03071          if (pos > qe->linpos)
03072             /* Indicate there is another priority */
03073             qe->linwrapped = 1;
03074          tmp->metric = pos;
03075       }
03076       tmp->metric += mem->penalty * 1000000;
03077       break;
03078    case QUEUE_STRATEGY_RRMEMORY:
03079       if (pos < q->rrpos) {
03080          tmp->metric = 1000 + pos;
03081       } else {
03082          if (pos > q->rrpos)
03083             /* Indicate there is another priority */
03084             q->wrapped = 1;
03085          tmp->metric = pos;
03086       }
03087       tmp->metric += mem->penalty * 1000000;
03088       break;
03089    case QUEUE_STRATEGY_RANDOM:
03090       tmp->metric = ast_random() % 1000;
03091       tmp->metric += mem->penalty * 1000000;
03092       break;
03093    case QUEUE_STRATEGY_WRANDOM:
03094       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03095       break;
03096    case QUEUE_STRATEGY_FEWESTCALLS:
03097       tmp->metric = mem->calls;
03098       tmp->metric += mem->penalty * 1000000;
03099       break;
03100    case QUEUE_STRATEGY_LEASTRECENT:
03101       if (!mem->lastcall)
03102          tmp->metric = 0;
03103       else
03104          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03105       tmp->metric += mem->penalty * 1000000;
03106       break;
03107    default:
03108       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03109       break;
03110    }
03111    return 0;
03112 }
03113 
03114 enum agent_complete_reason {
03115    CALLER,
03116    AGENT,
03117    TRANSFER
03118 };
03119 
03120 /*! \brief Send out AMI message with member call completion status information */
03121 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
03122    const struct ast_channel *peer, const struct member *member, time_t callstart,
03123    char *vars, size_t vars_len, enum agent_complete_reason rsn)
03124 {
03125    const char *reason = NULL; /* silence dumb compilers */
03126 
03127    if (!qe->parent->eventwhencalled)
03128       return;
03129 
03130    switch (rsn) {
03131    case CALLER:
03132       reason = "caller";
03133       break;
03134    case AGENT:
03135       reason = "agent";
03136       break;
03137    case TRANSFER:
03138       reason = "transfer";
03139       break;
03140    }
03141 
03142    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03143       "Queue: %s\r\n"
03144       "Uniqueid: %s\r\n"
03145       "Channel: %s\r\n"
03146       "Member: %s\r\n"
03147       "MemberName: %s\r\n"
03148       "HoldTime: %ld\r\n"
03149       "TalkTime: %ld\r\n"
03150       "Reason: %s\r\n"
03151       "%s",
03152       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03153       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03154       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03155 }
03156 
03157 struct queue_transfer_ds {
03158    struct queue_ent *qe;
03159    struct member *member;
03160    time_t starttime;
03161    int callcompletedinsl;
03162 };
03163 
03164 static void queue_transfer_destroy(void *data)
03165 {
03166    struct queue_transfer_ds *qtds = data;
03167    ast_free(qtds);
03168 }
03169 
03170 /*! \brief a datastore used to help correctly log attended transfers of queue callers
03171  */
03172 static const struct ast_datastore_info queue_transfer_info = {
03173    .type = "queue_transfer",
03174    .chan_fixup = queue_transfer_fixup,
03175    .destroy = queue_transfer_destroy,
03176 };
03177 
03178 /*! \brief Log an attended transfer when a queue caller channel is masqueraded
03179  *
03180  * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
03181  * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
03182  * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
03183  *
03184  * At the end of this, we want to remove the datastore so that this fixup function is not called on any
03185  * future masquerades of the caller during the current call.
03186  */
03187 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
03188 {
03189    struct queue_transfer_ds *qtds = data;
03190    struct queue_ent *qe = qtds->qe;
03191    struct member *member = qtds->member;
03192    time_t callstart = qtds->starttime;
03193    int callcompletedinsl = qtds->callcompletedinsl;
03194    struct ast_datastore *datastore;
03195 
03196    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03197             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03198             (long) (time(NULL) - callstart), qe->opos);
03199 
03200    update_queue(qe->parent, member, callcompletedinsl);
03201    
03202    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03203    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03204       ast_channel_datastore_remove(old_chan, datastore);
03205    } else {
03206       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03207    }
03208 }
03209 
03210 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
03211  *
03212  * When a caller is atxferred, then the queue_transfer_info datastore
03213  * is removed from the channel. If it's still there after the bridge is
03214  * broken, then the caller was not atxferred.
03215  *
03216  * \note Only call this with chan locked
03217  */
03218 static int attended_transfer_occurred(struct ast_channel *chan)
03219 {
03220    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03221 }
03222 
03223 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
03224  */
03225 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
03226 {
03227    struct ast_datastore *ds;
03228    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03229 
03230    if (!qtds) {
03231       ast_log(LOG_WARNING, "Memory allocation error!\n");
03232       return NULL;
03233    }
03234 
03235    ast_channel_lock(qe->chan);
03236    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03237       ast_channel_unlock(qe->chan);
03238       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03239       return NULL;
03240    }
03241 
03242    qtds->qe = qe;
03243    /* This member is refcounted in try_calling, so no need to add it here, too */
03244    qtds->member = member;
03245    qtds->starttime = starttime;
03246    qtds->callcompletedinsl = callcompletedinsl;
03247    ds->data = qtds;
03248    ast_channel_datastore_add(qe->chan, ds);
03249    ast_channel_unlock(qe->chan);
03250    return ds;
03251 }
03252 
03253 struct queue_end_bridge {
03254    struct call_queue *q;
03255    struct ast_channel *chan;
03256 };
03257 
03258 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
03259 {
03260    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03261    ao2_ref(qeb, +1);
03262    qeb->chan = originator;
03263 }
03264 
03265 static void end_bridge_callback(void *data)
03266 {
03267    struct queue_end_bridge *qeb = data;
03268    struct call_queue *q = qeb->q;
03269    struct ast_channel *chan = qeb->chan;
03270 
03271    if (ao2_ref(qeb, -1) == 1) {
03272       ao2_lock(q);
03273       set_queue_variables(q, chan);
03274       ao2_unlock(q);
03275       /* This unrefs the reference we made in try_calling when we allocated qeb */
03276       queue_unref(q);
03277    }
03278 }
03279 
03280 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
03281  * 
03282  * Here is the process of this function
03283  * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
03284  * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
03285  *    iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
03286  *    member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
03287  *    during each iteration, we call calc_metric to determine which members should be rung when.
03288  * 3. Call ring_one to place a call to the appropriate member(s)
03289  * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
03290  * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
03291  * 6. Start the monitor or mixmonitor if the option is set
03292  * 7. Remove the caller from the queue to allow other callers to advance
03293  * 8. Bridge the call.
03294  * 9. Do any post processing after the call has disconnected.
03295  *
03296  * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
03297  * \param[in] options the options passed as the third parameter to the Queue() application
03298  * \param[in] announceoverride filename to play to user when waiting 
03299  * \param[in] url the url passed as the fourth parameter to the Queue() application
03300  * \param[in,out] tries the number of times we have tried calling queue members
03301  * \param[out] noption set if the call to Queue() has the 'n' option set.
03302  * \param[in] agi the agi passed as the fifth parameter to the Queue() application
03303  * \param[in] macro the macro passed as the sixth parameter to the Queue() application
03304  * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
03305  * \param[in] ringing 1 if the 'r' option is set, otherwise 0
03306  */
03307 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
03308 {
03309    struct member *cur;
03310    struct callattempt *outgoing = NULL; /* the list of calls we are building */
03311    int to, orig;
03312    char oldexten[AST_MAX_EXTENSION]="";
03313    char oldcontext[AST_MAX_CONTEXT]="";
03314    char queuename[256]="";
03315    char interfacevar[256]="";
03316    struct ast_channel *peer;
03317    struct ast_channel *which;
03318    struct callattempt *lpeer;
03319    struct member *member;
03320    struct ast_app *application;
03321    int res = 0, bridge = 0;
03322    int numbusies = 0;
03323    int x=0;
03324    char *announce = NULL;
03325    char digit = 0;
03326    time_t callstart;
03327    time_t now = time(NULL);
03328    struct ast_bridge_config bridge_config;
03329    char nondataquality = 1;
03330    char *agiexec = NULL;
03331    char *macroexec = NULL;
03332    char *gosubexec = NULL;
03333    int ret = 0;
03334    const char *monitorfilename;
03335    const char *monitor_exec;
03336    const char *monitor_options;
03337    char tmpid[256], tmpid2[256];
03338    char meid[1024], meid2[1024];
03339    char mixmonargs[1512];
03340    struct ast_app *mixmonapp = NULL;
03341    char *p;
03342    char vars[2048];
03343    int forwardsallowed = 1;
03344    int callcompletedinsl;
03345    struct ao2_iterator memi;
03346    struct ast_datastore *datastore, *transfer_ds;
03347    struct queue_end_bridge *queue_end_bridge = NULL;
03348 
03349    ast_channel_lock(qe->chan);
03350    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03351    ast_channel_unlock(qe->chan);
03352 
03353    memset(&bridge_config, 0, sizeof(bridge_config));
03354    tmpid[0] = 0;
03355    meid[0] = 0;
03356    time(&now);
03357 
03358    /* If we've already exceeded our timeout, then just stop
03359     * This should be extremely rare. queue_exec will take care
03360     * of removing the caller and reporting the timeout as the reason.
03361     */
03362    if (qe->expire && now >= qe->expire) {
03363       res = 0;
03364       goto out;
03365    }
03366       
03367    for (; options && *options; options++)
03368       switch (*options) {
03369       case 't':
03370          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03371          break;
03372       case 'T':
03373          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03374          break;
03375       case 'w':
03376          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03377          break;
03378       case 'W':
03379          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03380          break;
03381       case 'c':
03382          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03383          break;
03384       case 'd':
03385          nondataquality = 0;
03386          break;
03387       case 'h':
03388          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03389          break;
03390       case 'H':
03391          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03392          break;
03393       case 'k':
03394          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03395          break;
03396       case 'K':
03397          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03398          break;
03399       case 'n':
03400          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03401             (*tries)++;
03402          else
03403             *tries = qe->parent->membercount;
03404          *noption = 1;
03405          break;
03406       case 'i':
03407          forwardsallowed = 0;
03408          break;
03409       case 'x':
03410          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03411          break;
03412       case 'X':
03413          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03414          break;
03415 
03416       }
03417 
03418    /* Hold the lock while we setup the outgoing calls */
03419    if (use_weight)
03420       ao2_lock(queues);
03421    ao2_lock(qe->parent);
03422    ast_debug(1, "%s is trying to call a queue member.\n",
03423                      qe->chan->name);
03424    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03425    if (!ast_strlen_zero(qe->announce))
03426       announce = qe->announce;
03427    if (!ast_strlen_zero(announceoverride))
03428       announce = announceoverride;
03429 
03430    memi = ao2_iterator_init(qe->parent->members, 0);
03431    while ((cur = ao2_iterator_next(&memi))) {
03432       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03433       struct ast_dialed_interface *di;
03434       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03435       if (!tmp) {
03436          ao2_ref(cur, -1);
03437          ao2_unlock(qe->parent);
03438          if (use_weight)
03439             ao2_unlock(queues);
03440          goto out;
03441       }
03442       if (!datastore) {
03443          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03444             ao2_ref(cur, -1);
03445             ao2_unlock(qe->parent);
03446             if (use_weight)
03447                ao2_unlock(queues);
03448             free(tmp);
03449             goto out;
03450          }
03451          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03452          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03453             ao2_ref(cur, -1);
03454             ao2_unlock(&qe->parent);
03455             if (use_weight)
03456                ao2_unlock(queues);
03457             free(tmp);
03458             goto out;
03459          }
03460          datastore->data = dialed_interfaces;
03461          AST_LIST_HEAD_INIT(dialed_interfaces);
03462 
03463          ast_channel_lock(qe->chan);
03464          ast_channel_datastore_add(qe->chan, datastore);
03465          ast_channel_unlock(qe->chan);
03466       } else
03467          dialed_interfaces = datastore->data;
03468 
03469       AST_LIST_LOCK(dialed_interfaces);
03470       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03471          if (!strcasecmp(cur->interface, di->interface)) {
03472             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
03473                di->interface);
03474             break;
03475          }
03476       }
03477       AST_LIST_UNLOCK(dialed_interfaces);
03478       
03479       if (di) {
03480          free(tmp);
03481          continue;
03482       }
03483 
03484       /* It is always ok to dial a Local interface.  We only keep track of
03485        * which "real" interfaces have been dialed.  The Local channel will
03486        * inherit this list so that if it ends up dialing a real interface,
03487        * it won't call one that has already been called. */
03488       if (strncasecmp(cur->interface, "Local/", 6)) {
03489          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03490             ao2_ref(cur, -1);
03491             ao2_unlock(qe->parent);
03492             if (use_weight)
03493                ao2_unlock(queues);
03494             free(tmp);
03495             goto out;
03496          }
03497          strcpy(di->interface, cur->interface);
03498 
03499          AST_LIST_LOCK(dialed_interfaces);
03500          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03501          AST_LIST_UNLOCK(dialed_interfaces);
03502       }
03503 
03504       tmp->stillgoing = -1;
03505       tmp->member = cur;
03506       tmp->oldstatus = cur->status;
03507       tmp->lastcall = cur->lastcall;
03508       tmp->lastqueue = cur->lastqueue;
03509       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03510       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03511          just calculate their metric for the appropriate strategy */
03512       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03513          /* Put them in the list of outgoing thingies...  We're ready now.
03514             XXX If we're forcibly removed, these outgoing calls won't get
03515             hung up XXX */
03516          tmp->q_next = outgoing;
03517          outgoing = tmp;      
03518          /* If this line is up, don't try anybody else */
03519          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03520             break;
03521       } else {
03522          ao2_ref(cur, -1);
03523          ast_free(tmp);
03524       }
03525    }
03526 
03527    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03528       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
03529       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03530          to = (qe->expire - now) * 1000;
03531       else
03532          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03533    } else {
03534       /* Config timeout is higher priority thatn application timeout */
03535       if (qe->expire && qe->expire<=now) {
03536          to = 0;
03537       } else if (qe->parent->timeout) {
03538          to = qe->parent->timeout * 1000;
03539       } else {
03540          to = -1;
03541       }
03542    }
03543    orig = to;
03544    ++qe->pending;
03545    ao2_unlock(qe->parent);
03546    ring_one(qe, outgoing, &numbusies);
03547    if (use_weight)
03548       ao2_unlock(queues);
03549    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03550    /* The ast_channel_datastore_remove() function could fail here if the
03551     * datastore was moved to another channel during a masquerade. If this is
03552     * the case, don't free the datastore here because later, when the channel
03553     * to which the datastore was moved hangs up, it will attempt to free this
03554     * datastore again, causing a crash
03555     */
03556    ast_channel_lock(qe->chan);
03557    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03558       ast_datastore_free(datastore);
03559    }
03560    ast_channel_unlock(qe->chan);
03561    ao2_lock(qe->parent);
03562    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03563       store_next_rr(qe, outgoing);
03564    }
03565    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03566       store_next_lin(qe, outgoing);
03567    }
03568    ao2_unlock(qe->parent);
03569    peer = lpeer ? lpeer->chan : NULL;
03570    if (!peer) {
03571       qe->pending = 0;
03572       if (to) {
03573          /* Must gotten hung up */
03574          res = -1;
03575       } else {
03576          /* User exited by pressing a digit */
03577          res = digit;
03578       }
03579       if (res == -1)
03580          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03581    } else { /* peer is valid */
03582       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03583          we will always return with -1 so that it is hung up properly after the
03584          conversation.  */
03585       if (!strcmp(qe->chan->tech->type, "DAHDI"))
03586          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03587       if (!strcmp(peer->tech->type, "DAHDI"))
03588          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03589       /* Update parameters for the queue */
03590       time(&now);
03591       recalc_holdtime(qe, (now - qe->start));
03592       ao2_lock(qe->parent);
03593       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03594       ao2_unlock(qe->parent);
03595       member = lpeer->member;
03596       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03597       ao2_ref(member, 1);
03598       hangupcalls(outgoing, peer);
03599       outgoing = NULL;
03600       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03601          int res2;
03602 
03603          res2 = ast_autoservice_start(qe->chan);
03604          if (!res2) {
03605             if (qe->parent->memberdelay) {
03606                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03607                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03608             }
03609             if (!res2 && announce) {
03610                play_file(peer, announce);
03611             }
03612             if (!res2 && qe->parent->reportholdtime) {
03613                if (!play_file(peer, qe->parent->sound_reporthold)) {
03614                   int holdtime, holdtimesecs;
03615 
03616                   time(&now);
03617                   holdtime = abs((now - qe->start) / 60);
03618                   holdtimesecs = abs((now - qe->start));
03619                   if (holdtime == 1) {
03620                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03621                      play_file(peer, qe->parent->sound_minute);
03622                   } else {
03623                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03624                      play_file(peer, qe->parent->sound_minutes);
03625                   }
03626                   if (holdtimesecs > 1) {
03627                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03628                      play_file(peer, qe->parent->sound_seconds);
03629                   }
03630                }
03631             }
03632          }
03633          res2 |= ast_autoservice_stop(qe->chan);
03634          if (ast_check_hangup(peer)) {
03635             /* Agent must have hung up */
03636             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03637             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03638             if (qe->parent->eventwhencalled)
03639                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03640                      "Queue: %s\r\n"
03641                      "Uniqueid: %s\r\n"
03642                      "Channel: %s\r\n"
03643                      "Member: %s\r\n"
03644                      "MemberName: %s\r\n"
03645                      "%s",
03646                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03647                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03648             ast_hangup(peer);
03649             ao2_ref(member, -1);
03650             goto out;
03651          } else if (res2) {
03652             /* Caller must have hung up just before being connected*/
03653             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03654             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03655             record_abandoned(qe);
03656             ast_hangup(peer);
03657             ao2_ref(member, -1);
03658             return -1;
03659          }
03660       }
03661       /* Stop music on hold */
03662       if (ringing)
03663          ast_indicate(qe->chan,-1);
03664       else
03665          ast_moh_stop(qe->chan);
03666       /* If appropriate, log that we have a destination channel */
03667       if (qe->chan->cdr)
03668          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03669       /* Make sure channels are compatible */
03670       res = ast_channel_make_compatible(qe->chan, peer);
03671       if (res < 0) {
03672          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03673          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03674          record_abandoned(qe);
03675          ast_hangup(peer);
03676          ao2_ref(member, -1);
03677          return -1;
03678       }
03679 
03680       /* Play announcement to the caller telling it's his turn if defined */
03681       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03682          if (play_file(qe->chan, qe->parent->sound_callerannounce))
03683             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03684       }
03685 
03686       ao2_lock(qe->parent);
03687       /* if setinterfacevar is defined, make member variables available to the channel */
03688       /* use  pbx_builtin_setvar to set a load of variables with one call */
03689       if (qe->parent->setinterfacevar) {
03690          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03691             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03692          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03693          pbx_builtin_setvar_multiple(peer, interfacevar);
03694       }
03695       
03696       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
03697       /* use  pbx_builtin_setvar to set a load of variables with one call */
03698       if (qe->parent->setqueueentryvar) {
03699          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03700             (long) time(NULL) - qe->start, qe->opos);
03701          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03702          pbx_builtin_setvar_multiple(peer, interfacevar);
03703       }
03704    
03705       /* try to set queue variables if configured to do so*/
03706       set_queue_variables(qe->parent, qe->chan);
03707       set_queue_variables(qe->parent, peer);
03708       ao2_unlock(qe->parent);
03709       
03710       ast_channel_lock(qe->chan);
03711       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03712             monitorfilename = ast_strdupa(monitorfilename);
03713       }
03714       ast_channel_unlock(qe->chan);
03715       /* Begin Monitoring */
03716       if (qe->parent->monfmt && *qe->parent->monfmt) {
03717          if (!qe->parent->montype) {
03718             const char *monexec, *monargs;
03719             ast_debug(1, "Starting Monitor as requested.\n");
03720             ast_channel_lock(qe->chan);
03721             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03722                which = qe->chan;
03723                monexec = monexec ? ast_strdupa(monexec) : NULL;
03724             }
03725             else
03726                which = peer;
03727             ast_channel_unlock(qe->chan);
03728             if (monitorfilename)
03729                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03730             else if (qe->chan->cdr)
03731                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03732             else {
03733                /* Last ditch effort -- no CDR, make up something */
03734                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03735                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03736             }
03737             if (!ast_strlen_zero(monexec)) {
03738                ast_monitor_setjoinfiles(which, 1);
03739             }
03740          } else {
03741             mixmonapp = pbx_findapp("MixMonitor");
03742             
03743             if (mixmonapp) {
03744                ast_debug(1, "Starting MixMonitor as requested.\n");
03745                if (!monitorfilename) {
03746                   if (qe->chan->cdr)
03747                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03748                   else
03749                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03750                } else {
03751                   const char *m = monitorfilename;
03752                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
03753                      switch (*m) {
03754                      case '^':
03755                         if (*(m + 1) == '{')
03756                            *p = '$';
03757                         break;
03758                      case ',':
03759                         *p++ = '\\';
03760                         /* Fall through */
03761                      default:
03762                         *p = *m;
03763                      }
03764                      if (*m == '\0')
03765                         break;
03766                   }
03767                   if (p == tmpid2 + sizeof(tmpid2))
03768                      tmpid2[sizeof(tmpid2) - 1] = '\0';
03769 
03770                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03771                }
03772 
03773                ast_channel_lock(qe->chan);
03774                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03775                      monitor_exec = ast_strdupa(monitor_exec);
03776                }
03777                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03778                      monitor_options = ast_strdupa(monitor_options);
03779                } else {
03780                   monitor_options = "";
03781                }
03782                ast_channel_unlock(qe->chan);
03783 
03784                if (monitor_exec) {
03785                   const char *m = monitor_exec;
03786                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03787                      switch (*m) {
03788                      case '^':
03789                         if (*(m + 1) == '{')
03790                            *p = '$';
03791                         break;
03792                      case ',':
03793                         *p++ = '\\';
03794                         /* Fall through */
03795                      default:
03796                         *p = *m;
03797                      }
03798                      if (*m == '\0')
03799                         break;
03800                   }
03801                   if (p == meid2 + sizeof(meid2))
03802                      meid2[sizeof(meid2) - 1] = '\0';
03803 
03804                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03805                }
03806    
03807                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03808 
03809                if (!ast_strlen_zero(monitor_exec))
03810                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03811                else
03812                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03813                
03814                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03815                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03816                if (qe->chan->cdr)
03817                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03818                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03819                if (qe->chan->cdr)
03820                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03821 
03822             } else {
03823                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03824             }
03825          }
03826       }
03827       /* Drop out of the queue at this point, to prepare for next caller */
03828       leave_queue(qe);        
03829       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03830          ast_debug(1, "app_queue: sendurl=%s.\n", url);
03831          ast_channel_sendurl(peer, url);
03832       }
03833       
03834       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
03835       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
03836       if (!ast_strlen_zero(macro)) {
03837             macroexec = ast_strdupa(macro);
03838       } else {
03839          if (qe->parent->membermacro)
03840             macroexec = ast_strdupa(qe->parent->membermacro);
03841       }
03842 
03843       if (!ast_strlen_zero(macroexec)) {
03844          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03845          
03846          res = ast_autoservice_start(qe->chan);
03847          if (res) {
03848             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03849             res = -1;
03850          }
03851          
03852          application = pbx_findapp("Macro");
03853 
03854          if (application) {
03855             res = pbx_exec(peer, application, macroexec);
03856             ast_debug(1, "Macro exited with status %d\n", res);
03857             res = 0;
03858          } else {
03859             ast_log(LOG_ERROR, "Could not find application Macro\n");
03860             res = -1;
03861          }
03862 
03863          if (ast_autoservice_stop(qe->chan) < 0) {
03864             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03865             res = -1;
03866          }
03867       }
03868 
03869       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
03870       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
03871       if (!ast_strlen_zero(gosub)) {
03872             gosubexec = ast_strdupa(gosub);
03873       } else {
03874          if (qe->parent->membergosub)
03875             gosubexec = ast_strdupa(qe->parent->membergosub);
03876       }
03877 
03878       if (!ast_strlen_zero(gosubexec)) {
03879          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03880          
03881          res = ast_autoservice_start(qe->chan);
03882          if (res) {
03883             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03884             res = -1;
03885          }
03886          
03887          application = pbx_findapp("Gosub");
03888          
03889          if (application) {
03890             char *gosub_args, *gosub_argstart;
03891 
03892             /* Set where we came from */
03893             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03894             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03895             peer->priority = 0;
03896 
03897             gosub_argstart = strchr(gosubexec, ',');
03898             if (gosub_argstart) {
03899                *gosub_argstart = 0;
03900                if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03901                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03902                   gosub_args = NULL;
03903                }
03904                *gosub_argstart = ',';
03905             } else {
03906                if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03907                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03908                   gosub_args = NULL;
03909                }
03910             }
03911             if (gosub_args) {
03912                res = pbx_exec(peer, application, gosub_args);
03913                if (!res) {
03914                   struct ast_pbx_args args;
03915                   memset(&args, 0, sizeof(args));
03916                   args.no_hangup_chan = 1;
03917                   ast_pbx_run_args(peer, &args);
03918                }
03919                ast_free(gosub_args);
03920                ast_debug(1, "Gosub exited with status %d\n", res);
03921             } else {
03922                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
03923             }
03924          } else {
03925             ast_log(LOG_ERROR, "Could not find application Gosub\n");
03926             res = -1;
03927          }
03928       
03929          if (ast_autoservice_stop(qe->chan) < 0) {
03930             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03931             res = -1;
03932          }
03933       }
03934 
03935       if (!ast_strlen_zero(agi)) {
03936          ast_debug(1, "app_queue: agi=%s.\n", agi);
03937          application = pbx_findapp("agi");
03938          if (application) {
03939             agiexec = ast_strdupa(agi);
03940             ret = pbx_exec(qe->chan, application, agiexec);
03941          } else
03942             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03943       }
03944       qe->handled++;
03945       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
03946                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
03947       if (update_cdr && qe->chan->cdr) 
03948          ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
03949       if (qe->parent->eventwhencalled)
03950          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03951                "Queue: %s\r\n"
03952                "Uniqueid: %s\r\n"
03953                "Channel: %s\r\n"
03954                "Member: %s\r\n"
03955                "MemberName: %s\r\n"
03956                "Holdtime: %ld\r\n"
03957                "BridgedChannel: %s\r\n"
03958                "Ringtime: %ld\r\n"
03959                "%s",
03960                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03961                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
03962                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03963       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03964       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03965    
03966       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
03967          queue_end_bridge->q = qe->parent;
03968          queue_end_bridge->chan = qe->chan;
03969          bridge_config.end_bridge_callback = end_bridge_callback;
03970          bridge_config.end_bridge_callback_data = queue_end_bridge;
03971          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
03972          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
03973           * to make sure to increase the refcount of this queue so it cannot be freed until we
03974           * are done with it. We remove this reference in end_bridge_callback.
03975           */
03976          queue_ref(qe->parent);
03977       }
03978 
03979       time(&callstart);
03980       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03981       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03982 
03983       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
03984        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
03985        */
03986       ast_channel_lock(qe->chan);
03987       if (!attended_transfer_occurred(qe->chan)) {
03988          struct ast_datastore *tds;
03989          if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03990             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03991                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03992                (long) (time(NULL) - callstart), qe->opos);
03993             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
03994          } else if (ast_check_hangup(qe->chan)) {
03995             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03996                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03997             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
03998          } else {
03999             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04000                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04001             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04002          }
04003          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
04004             ast_channel_datastore_remove(qe->chan, tds);
04005          }
04006          update_queue(qe->parent, member, callcompletedinsl);
04007       }
04008 
04009       if (transfer_ds) {
04010          ast_datastore_free(transfer_ds);
04011       }
04012       ast_channel_unlock(qe->chan);
04013       ast_hangup(peer);
04014       res = bridge ? bridge : 1;
04015       ao2_ref(member, -1);
04016    }
04017 out:
04018    hangupcalls(outgoing, NULL);
04019 
04020    return res;
04021 }
04022 
04023 static int wait_a_bit(struct queue_ent *qe)
04024 {
04025    /* Don't need to hold the lock while we setup the outgoing calls */
04026    int retrywait = qe->parent->retry * 1000;
04027 
04028    int res = ast_waitfordigit(qe->chan, retrywait);
04029    if (res > 0 && !valid_exit(qe, res))
04030       res = 0;
04031 
04032    return res;
04033 }
04034 
04035 static struct member *interface_exists(struct call_queue *q, const char *interface)
04036 {
04037    struct member *mem;
04038    struct ao2_iterator mem_iter;
04039 
04040    if (!q)
04041       return NULL;
04042 
04043    mem_iter = ao2_iterator_init(q->members, 0);
04044    while ((mem = ao2_iterator_next(&mem_iter))) {
04045       if (!strcasecmp(interface, mem->interface))
04046          return mem;
04047       ao2_ref(mem, -1);
04048    }
04049 
04050    return NULL;
04051 }
04052 
04053 
04054 /*! \brief Dump all members in a specific queue to the database
04055  *
04056  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
04057  */
04058 static void dump_queue_members(struct call_queue *pm_queue)
04059 {
04060    struct member *cur_member;
04061    char value[PM_MAX_LEN];
04062    int value_len = 0;
04063    int res;
04064    struct ao2_iterator mem_iter;
04065 
04066    memset(value, 0, sizeof(value));
04067 
04068    if (!pm_queue)
04069       return;
04070 
04071    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04072    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04073       if (!cur_member->dynamic) {
04074          ao2_ref(cur_member, -1);
04075          continue;
04076       }
04077 
04078       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04079          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04080 
04081       ao2_ref(cur_member, -1);
04082 
04083       if (res != strlen(value + value_len)) {
04084          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04085          break;
04086       }
04087       value_len += res;
04088    }
04089    
04090    if (value_len && !cur_member) {
04091       if (ast_db_put(pm_family, pm_queue->name, value))
04092          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04093    } else
04094       /* Delete the entry if the queue is empty or there is an error */
04095       ast_db_del(pm_family, pm_queue->name);
04096 }
04097 
04098 /*! \brief Remove member from queue 
04099  * \retval RES_NOT_DYNAMIC when they aren't a RT member
04100  * \retval RES_NOSUCHQUEUE queue does not exist
04101  * \retval RES_OKAY removed member from queue
04102  * \retval RES_EXISTS queue exists but no members
04103 */
04104 static int remove_from_queue(const char *queuename, const char *interface)
04105 {
04106    struct call_queue *q, tmpq = {
04107       .name = queuename,   
04108    };
04109    struct member *mem, tmpmem;
04110    int res = RES_NOSUCHQUEUE;
04111 
04112    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04113    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04114       ao2_lock(queues);
04115       ao2_lock(q);
04116       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04117          /* XXX future changes should beware of this assumption!! */
04118          if (!mem->dynamic) {
04119             ao2_ref(mem, -1);
04120             ao2_unlock(q);
04121             queue_unref(q);
04122             ao2_unlock(queues);
04123             return RES_NOT_DYNAMIC;
04124          }
04125          q->membercount--;
04126          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04127             "Queue: %s\r\n"
04128             "Location: %s\r\n"
04129             "MemberName: %s\r\n",
04130             q->name, mem->interface, mem->membername);
04131          ao2_unlink(q->members, mem);
04132          remove_from_interfaces(mem->state_interface, 0);
04133          ao2_ref(mem, -1);
04134 
04135          if (queue_persistent_members)
04136             dump_queue_members(q);
04137          
04138          res = RES_OKAY;
04139       } else {
04140          res = RES_EXISTS;
04141       }
04142       ao2_unlock(q);
04143       ao2_unlock(queues);
04144       queue_unref(q);
04145    }
04146 
04147    return res;
04148 }
04149 
04150 /*! \brief Add member to queue 
04151  * \retval RES_NOT_DYNAMIC when they aren't a RT member
04152  * \retval RES_NOSUCHQUEUE queue does not exist
04153  * \retval RES_OKAY added member from queue
04154  * \retval RES_EXISTS queue exists but no members
04155  * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
04156 */
04157 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
04158 {
04159    struct call_queue *q;
04160    struct member *new_member, *old_member;
04161    int res = RES_NOSUCHQUEUE;
04162 
04163    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04164     * short-circuits if the queue is already in memory. */
04165    if (!(q = load_realtime_queue(queuename)))
04166       return res;
04167 
04168    ao2_lock(queues);
04169 
04170    ao2_lock(q);
04171    if ((old_member = interface_exists(q, interface)) == NULL) {
04172       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04173          add_to_interfaces(new_member->state_interface);
04174          new_member->dynamic = 1;
04175          ao2_link(q->members, new_member);
04176          q->membercount++;
04177          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04178             "Queue: %s\r\n"
04179             "Location: %s\r\n"
04180             "MemberName: %s\r\n"
04181             "Membership: %s\r\n"
04182             "Penalty: %d\r\n"
04183             "CallsTaken: %d\r\n"
04184             "LastCall: %d\r\n"
04185             "Status: %d\r\n"
04186             "Paused: %d\r\n",
04187             q->name, new_member->interface, new_member->membername,
04188             "dynamic",
04189             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04190             new_member->status, new_member->paused);
04191          
04192          ao2_ref(new_member, -1);
04193          new_member = NULL;
04194 
04195          if (dump)
04196             dump_queue_members(q);
04197          
04198          res = RES_OKAY;
04199       } else {
04200          res = RES_OUTOFMEMORY;
04201       }
04202    } else {
04203       ao2_ref(old_member, -1);
04204       res = RES_EXISTS;
04205    }
04206    ao2_unlock(q);
04207    ao2_unlock(queues);
04208 
04209    return res;
04210 }
04211 
04212 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
04213 {
04214    int found = 0;
04215    struct call_queue *q;
04216    struct member *mem;
04217    struct ao2_iterator queue_iter;
04218    int failed;
04219 
04220    /* Special event for when all queues are paused - individual events still generated */
04221    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04222    if (ast_strlen_zero(queuename))
04223       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04224 
04225    queue_iter = ao2_iterator_init(queues, 0);
04226    while ((q = ao2_iterator_next(&queue_iter))) {
04227       ao2_lock(q);
04228       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04229          if ((mem = interface_exists(q, interface))) {
04230             if (mem->paused == paused) {
04231                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04232             }
04233 
04234             failed = 0;
04235             if (mem->realtime) {
04236                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04237             }
04238          
04239             if (failed) {
04240                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04241                ao2_ref(mem, -1);
04242                ao2_unlock(q);
04243                continue;
04244             }  
04245             found++;
04246             mem->paused = paused;
04247 
04248             if (queue_persistent_members)
04249                dump_queue_members(q);
04250 
04251             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04252             
04253             if (!ast_strlen_zero(reason)) {
04254                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04255                   "Queue: %s\r\n"
04256                   "Location: %s\r\n"
04257                   "MemberName: %s\r\n"
04258                   "Paused: %d\r\n"
04259                   "Reason: %s\r\n",
04260                      q->name, mem->interface, mem->membername, paused, reason);
04261             } else {
04262                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04263                   "Queue: %s\r\n"
04264                   "Location: %s\r\n"
04265                   "MemberName: %s\r\n"
04266                   "Paused: %d\r\n",
04267                      q->name, mem->interface, mem->membername, paused);
04268             }
04269             ao2_ref(mem, -1);
04270          }
04271       }
04272       
04273       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04274          ao2_unlock(q);
04275          queue_unref(q);
04276          break;
04277       }
04278       
04279       ao2_unlock(q);
04280       queue_unref(q);
04281    }
04282 
04283    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04284 }
04285 
04286 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
04287 static int set_member_penalty(char *queuename, char *interface, int penalty)
04288 {
04289    int foundinterface = 0, foundqueue = 0;
04290    struct call_queue *q;
04291    struct member *mem;
04292    struct ao2_iterator queue_iter;
04293 
04294    if (penalty < 0) {
04295       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04296       return RESULT_FAILURE;
04297    }
04298 
04299    queue_iter = ao2_iterator_init(queues, 0);
04300    while ((q = ao2_iterator_next(&queue_iter))) {
04301       ao2_lock(q);
04302       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04303          foundqueue++;
04304          if ((mem = interface_exists(q, interface))) {
04305             foundinterface++;
04306             mem->penalty = penalty;
04307             
04308             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04309             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04310                "Queue: %s\r\n"
04311                "Location: %s\r\n"
04312                "Penalty: %d\r\n",
04313                q->name, mem->interface, penalty);
04314             ao2_ref(mem, -1);
04315          }
04316       }
04317       ao2_unlock(q);
04318       queue_unref(q);
04319    }
04320 
04321    if (foundinterface) {
04322       return RESULT_SUCCESS;
04323    } else if (!foundqueue) {
04324       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04325    } else {
04326       ast_log (LOG_ERROR, "Invalid interface\n");
04327    }  
04328 
04329    return RESULT_FAILURE;
04330 }
04331 
04332 /* \brief Gets members penalty. 
04333  * \return Return the members penalty or RESULT_FAILURE on error. 
04334 */
04335 static int get_member_penalty(char *queuename, char *interface)
04336 {
04337    int foundqueue = 0, penalty;
04338    struct call_queue *q, tmpq = {
04339       .name = queuename,   
04340    };
04341    struct member *mem;
04342    
04343    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04344       foundqueue = 1;
04345       ao2_lock(q);
04346       if ((mem = interface_exists(q, interface))) {
04347          penalty = mem->penalty;
04348          ao2_ref(mem, -1);
04349          ao2_unlock(q);
04350          queue_unref(q);
04351          return penalty;
04352       }
04353       ao2_unlock(q);
04354       queue_unref(q);
04355    }
04356 
04357    /* some useful debuging */
04358    if (foundqueue) 
04359       ast_log (LOG_ERROR, "Invalid queuename\n");
04360    else 
04361       ast_log (LOG_ERROR, "Invalid interface\n");
04362 
04363    return RESULT_FAILURE;
04364 }
04365 
04366 /*! \brief Reload dynamic queue members persisted into the astdb */
04367 static void reload_queue_members(void)
04368 {
04369    char *cur_ptr;
04370    const char *queue_name;
04371    char *member;
04372    char *interface;
04373    char *membername = NULL;
04374    char *state_interface;
04375    char *penalty_tok;
04376    int penalty = 0;
04377    char *paused_tok;
04378    int paused = 0;
04379    struct ast_db_entry *db_tree;
04380    struct ast_db_entry *entry;
04381    struct call_queue *cur_queue;
04382    char queue_data[PM_MAX_LEN];
04383 
04384    ao2_lock(queues);
04385 
04386    /* Each key in 'pm_family' is the name of a queue */
04387    db_tree = ast_db_gettree(pm_family, NULL);
04388    for (entry = db_tree; entry; entry = entry->next) {
04389 
04390       queue_name = entry->key + strlen(pm_family) + 2;
04391 
04392       {
04393          struct call_queue tmpq = {
04394             .name = queue_name,
04395          };
04396          cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04397       }  
04398 
04399       if (!cur_queue)
04400          cur_queue = load_realtime_queue(queue_name);
04401 
04402       if (!cur_queue) {
04403          /* If the queue no longer exists, remove it from the
04404           * database */
04405          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04406          ast_db_del(pm_family, queue_name);
04407          continue;
04408       } 
04409 
04410       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04411          queue_unref(cur_queue);
04412          continue;
04413       }
04414 
04415       cur_ptr = queue_data;
04416       while ((member = strsep(&cur_ptr, ",|"))) {
04417          if (ast_strlen_zero(member))
04418             continue;
04419 
04420          interface = strsep(&member, ";");
04421          penalty_tok = strsep(&member, ";");
04422          paused_tok = strsep(&member, ";");
04423          membername = strsep(&member, ";");
04424          state_interface = strsep(&member, ";");
04425 
04426          if (!penalty_tok) {
04427             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04428             break;
04429          }
04430          penalty = strtol(penalty_tok, NULL, 10);
04431          if (errno == ERANGE) {
04432             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04433             break;
04434          }
04435          
04436          if (!paused_tok) {
04437             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04438             break;
04439          }
04440          paused = strtol(paused_tok, NULL, 10);
04441          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04442             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04443             break;
04444          }
04445 
04446          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04447          
04448          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04449             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04450             break;
04451          }
04452       }
04453       queue_unref(cur_queue);
04454    }
04455 
04456    ao2_unlock(queues);
04457    if (db_tree) {
04458       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04459       ast_db_freetree(db_tree);
04460    }
04461 }
04462 
04463 /*! \brief PauseQueueMember application */
04464 static int pqm_exec(struct ast_channel *chan, void *data)
04465 {
04466    char *parse;
04467    AST_DECLARE_APP_ARGS(args,
04468       AST_APP_ARG(queuename);
04469       AST_APP_ARG(interface);
04470       AST_APP_ARG(options);
04471       AST_APP_ARG(reason);
04472    );
04473 
04474    if (ast_strlen_zero(data)) {
04475       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04476       return -1;
04477    }
04478 
04479    parse = ast_strdupa(data);
04480 
04481    AST_STANDARD_APP_ARGS(args, parse);
04482 
04483    if (ast_strlen_zero(args.interface)) {
04484       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04485       return -1;
04486    }
04487 
04488    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04489       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04490       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04491       return 0;
04492    }
04493 
04494    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04495 
04496    return 0;
04497 }
04498 
04499 /*! \brief UnPauseQueueMember application */
04500 static int upqm_exec(struct ast_channel *chan, void *data)
04501 {
04502    char *parse;
04503    AST_DECLARE_APP_ARGS(args,
04504       AST_APP_ARG(queuename);
04505       AST_APP_ARG(interface);
04506       AST_APP_ARG(options);
04507       AST_APP_ARG(reason);
04508    );
04509 
04510    if (ast_strlen_zero(data)) {
04511       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04512       return -1;
04513    }
04514 
04515    parse = ast_strdupa(data);
04516 
04517    AST_STANDARD_APP_ARGS(args, parse);
04518 
04519    if (ast_strlen_zero(args.interface)) {
04520       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04521       return -1;
04522    }
04523 
04524    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04525       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04526       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04527       return 0;
04528    }
04529 
04530    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04531 
04532    return 0;
04533 }
04534 
04535 /*! \brief RemoveQueueMember application */
04536 static int rqm_exec(struct ast_channel *chan, void *data)
04537 {
04538    int res=-1;
04539    char *parse, *temppos = NULL;
04540    AST_DECLARE_APP_ARGS(args,
04541       AST_APP_ARG(queuename);
04542       AST_APP_ARG(interface);
04543       AST_APP_ARG(options);
04544    );
04545 
04546 
04547    if (ast_strlen_zero(data)) {
04548       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04549       return -1;
04550    }
04551 
04552    parse = ast_strdupa(data);
04553 
04554    AST_STANDARD_APP_ARGS(args, parse);
04555 
04556    if (ast_strlen_zero(args.interface)) {
04557       args.interface = ast_strdupa(chan->name);
04558       temppos = strrchr(args.interface, '-');
04559       if (temppos)
04560          *temppos = '\0';
04561    }
04562 
04563    switch (remove_from_queue(args.queuename, args.interface)) {
04564    case RES_OKAY:
04565       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04566       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04567       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04568       res = 0;
04569       break;
04570    case RES_EXISTS:
04571       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04572       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04573       res = 0;
04574       break;
04575    case RES_NOSUCHQUEUE:
04576       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04577       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04578       res = 0;
04579       break;
04580    case RES_NOT_DYNAMIC:
04581       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04582       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04583       res = 0;
04584       break;
04585    }
04586 
04587    return res;
04588 }
04589 
04590 /*! \brief AddQueueMember application */
04591 static int aqm_exec(struct ast_channel *chan, void *data)
04592 {
04593    int res=-1;
04594    char *parse, *temppos = NULL;
04595    AST_DECLARE_APP_ARGS(args,
04596       AST_APP_ARG(queuename);
04597       AST_APP_ARG(interface);
04598       AST_APP_ARG(penalty);
04599       AST_APP_ARG(options);
04600       AST_APP_ARG(membername);
04601       AST_APP_ARG(state_interface);
04602    );
04603    int penalty = 0;
04604 
04605    if (ast_strlen_zero(data)) {
04606       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04607       return -1;
04608    }
04609 
04610    parse = ast_strdupa(data);
04611 
04612    AST_STANDARD_APP_ARGS(args, parse);
04613 
04614    if (ast_strlen_zero(args.interface)) {
04615       args.interface = ast_strdupa(chan->name);
04616       temppos = strrchr(args.interface, '-');
04617       if (temppos)
04618          *temppos = '\0';
04619    }
04620 
04621    if (!ast_strlen_zero(args.penalty)) {
04622       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
04623          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04624          penalty = 0;
04625       }
04626    }
04627 
04628    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04629    case RES_OKAY:
04630       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04631       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04632       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04633       res = 0;
04634       break;
04635    case RES_EXISTS:
04636       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04637       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04638       res = 0;
04639       break;
04640    case RES_NOSUCHQUEUE:
04641       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04642       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04643       res = 0;
04644       break;
04645    case RES_OUTOFMEMORY:
04646       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04647       break;
04648    }
04649 
04650    return res;
04651 }
04652 
04653 /*! \brief QueueLog application */
04654 static int ql_exec(struct ast_channel *chan, void *data)
04655 {
04656    char *parse;
04657 
04658    AST_DECLARE_APP_ARGS(args,
04659       AST_APP_ARG(queuename);
04660       AST_APP_ARG(uniqueid);
04661       AST_APP_ARG(membername);
04662       AST_APP_ARG(event);
04663       AST_APP_ARG(params);
04664    );
04665 
04666    if (ast_strlen_zero(data)) {
04667       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04668       return -1;
04669    }
04670 
04671    parse = ast_strdupa(data);
04672 
04673    AST_STANDARD_APP_ARGS(args, parse);
04674 
04675    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04676        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04677       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04678       return -1;
04679    }
04680 
04681    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04682       "%s", args.params ? args.params : "");
04683 
04684    return 0;
04685 }
04686 
04687 /*! \brief Copy rule from global list into specified queue */
04688 static void copy_rules(struct queue_ent *qe, const char *rulename)
04689 {
04690    struct penalty_rule *pr_iter;
04691    struct rule_list *rl_iter;
04692    const char *tmp = rulename;
04693    if (ast_strlen_zero(tmp)) {
04694       return;
04695    }
04696    AST_LIST_LOCK(&rule_lists);
04697    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04698       if (!strcasecmp(rl_iter->name, tmp))
04699          break;
04700    }
04701    if (rl_iter) {
04702       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04703          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04704          if (!new_pr) {
04705             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04706             AST_LIST_UNLOCK(&rule_lists);
04707             break;
04708          }
04709          new_pr->time = pr_iter->time;
04710          new_pr->max_value = pr_iter->max_value;
04711          new_pr->min_value = pr_iter->min_value;
04712          new_pr->max_relative = pr_iter->max_relative;
04713          new_pr->min_relative = pr_iter->min_relative;
04714          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04715       }
04716    }
04717    AST_LIST_UNLOCK(&rule_lists);
04718 }
04719 
04720 /*!\brief The starting point for all queue calls
04721  *
04722  * The process involved here is to 
04723  * 1. Parse the options specified in the call to Queue()
04724  * 2. Join the queue
04725  * 3. Wait in a loop until it is our turn to try calling a queue member
04726  * 4. Attempt to call a queue member
04727  * 5. If 4. did not result in a bridged call, then check for between
04728  *    call options such as periodic announcements etc.
04729  * 6. Try 4 again unless some condition (such as an expiration time) causes us to 
04730  *    exit the queue.
04731  */
04732 static int queue_exec(struct ast_channel *chan, void *data)
04733 {
04734    int res=-1;
04735    int ringing=0;
04736    const char *user_priority;
04737    const char *max_penalty_str;
04738    const char *min_penalty_str;
04739    int prio;
04740    int qcontinue = 0;
04741    int max_penalty, min_penalty;
04742    enum queue_result reason = QUEUE_UNKNOWN;
04743    /* whether to exit Queue application after the timeout hits */
04744    int tries = 0;
04745    int noption = 0;
04746    char *parse;
04747    int makeannouncement = 0;
04748    AST_DECLARE_APP_ARGS(args,
04749       AST_APP_ARG(queuename);
04750       AST_APP_ARG(options);
04751       AST_APP_ARG(url);
04752       AST_APP_ARG(announceoverride);
04753       AST_APP_ARG(queuetimeoutstr);
04754       AST_APP_ARG(agi);
04755       AST_APP_ARG(macro);
04756       AST_APP_ARG(gosub);
04757       AST_APP_ARG(rule);
04758    );
04759    /* Our queue entry */
04760    struct queue_ent qe;
04761    
04762    if (ast_strlen_zero(data)) {
04763       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04764       return -1;
04765    }
04766    
04767    parse = ast_strdupa(data);
04768    AST_STANDARD_APP_ARGS(args, parse);
04769 
04770    /* Setup our queue entry */
04771    memset(&qe, 0, sizeof(qe));
04772    qe.start = time(NULL);
04773 
04774    /* set the expire time based on the supplied timeout; */
04775    if (!ast_strlen_zero(args.queuetimeoutstr))
04776       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04777    else
04778       qe.expire = 0;
04779 
04780    /* Get the priority from the variable ${QUEUE_PRIO} */
04781    ast_channel_lock(chan);
04782    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04783    if (user_priority) {
04784       if (sscanf(user_priority, "%d", &prio) == 1) {
04785          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04786       } else {
04787          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04788             user_priority, chan->name);
04789          prio = 0;
04790       }
04791    } else {
04792       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04793       prio = 0;
04794    }
04795 
04796    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04797 
04798    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04799       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
04800          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04801       } else {
04802          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04803             max_penalty_str, chan->name);
04804          max_penalty = 0;
04805       }
04806    } else {
04807       max_penalty = 0;
04808    }
04809 
04810    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04811       if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
04812          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04813       } else {
04814          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04815             min_penalty_str, chan->name);
04816          min_penalty = 0;
04817       }
04818    } else {
04819       min_penalty = 0;
04820    }
04821    ast_channel_unlock(chan);
04822 
04823    if (args.options && (strchr(args.options, 'r')))
04824       ringing = 1;
04825 
04826    if (args.options && (strchr(args.options, 'c')))
04827       qcontinue = 1;
04828 
04829    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04830       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04831 
04832    qe.chan = chan;
04833    qe.prio = prio;
04834    qe.max_penalty = max_penalty;
04835    qe.min_penalty = min_penalty;
04836    qe.last_pos_said = 0;
04837    qe.last_pos = 0;
04838    qe.last_periodic_announce_time = time(NULL);
04839    qe.last_periodic_announce_sound = 0;
04840    qe.valid_digits = 0;
04841    if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04842       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04843       set_queue_result(chan, reason);
04844       return 0;
04845    }
04846    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04847       S_OR(chan->cid.cid_num, ""));
04848 check_turns:
04849    if (ringing) {
04850       ast_indicate(chan, AST_CONTROL_RINGING);
04851    } else {
04852       ast_moh_start(chan, qe.moh, NULL);
04853    }
04854 
04855    /* This is the wait loop for callers 2 through maxlen */
04856    res = wait_our_turn(&qe, ringing, &reason);
04857    if (res) {
04858       goto stop;
04859    }
04860 
04861    makeannouncement = 0;
04862 
04863    for (;;) {
04864       /* This is the wait loop for the head caller*/
04865       /* To exit, they may get their call answered; */
04866       /* they may dial a digit from the queue context; */
04867       /* or, they may timeout. */
04868 
04869       enum queue_member_status status = QUEUE_NORMAL;
04870       int exit = 0;
04871 
04872       /* Leave if we have exceeded our queuetimeout */
04873       if (qe.expire && (time(NULL) >= qe.expire)) {
04874          record_abandoned(&qe);
04875          reason = QUEUE_TIMEOUT;
04876          res = 0;
04877          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
04878             qe.pos, qe.opos, (long) time(NULL) - qe.start);
04879          break;
04880       }
04881 
04882       if (makeannouncement) {
04883          /* Make a position announcement, if enabled */
04884          if (qe.parent->announcefrequency)
04885             if ((res = say_position(&qe,ringing)))
04886                goto stop;
04887       }
04888       makeannouncement = 1;
04889 
04890       /* Make a periodic announcement, if enabled */
04891       if (qe.parent->periodicannouncefrequency)
04892          if ((res = say_periodic_announcement(&qe,ringing)))
04893             goto stop;
04894    
04895       /* Leave if we have exceeded our queuetimeout */
04896       if (qe.expire && (time(NULL) >= qe.expire)) {
04897          record_abandoned(&qe);
04898          reason = QUEUE_TIMEOUT;
04899          res = 0;
04900          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04901          break;
04902       }
04903 
04904       /* see if we need to move to the next penalty level for this queue */
04905       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04906          update_qe_rule(&qe);
04907       }
04908 
04909       /* Try calling all queue members for 'timeout' seconds */
04910       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04911       if (res) {
04912          goto stop;
04913       }
04914 
04915       /* exit after 'timeout' cycle if 'n' option enabled */
04916       if (noption && tries >= qe.parent->membercount) {
04917          ast_verb(3, "Exiting on time-out cycle\n");
04918          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04919          record_abandoned(&qe);
04920          reason = QUEUE_TIMEOUT;
04921          res = 0;
04922          break;
04923       }
04924 
04925       for (; !exit || qe.pr; update_qe_rule(&qe)) {
04926          status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04927 
04928          if (!qe.pr || status == QUEUE_NORMAL) {
04929             break;
04930          }
04931 
04932          if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
04933                ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04934                ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
04935             continue;
04936          } else {
04937             exit = 1;
04938          }
04939       }
04940 
04941       /* leave the queue if no agents, if enabled */
04942       if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
04943          record_abandoned(&qe);
04944          reason = QUEUE_LEAVEEMPTY;
04945          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04946          res = 0;
04947          break;
04948       }
04949 
04950       /* leave the queue if no reachable agents, if enabled */
04951       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04952          record_abandoned(&qe);
04953          reason = QUEUE_LEAVEUNAVAIL;
04954          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04955          res = 0;
04956          break;
04957       }
04958       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
04959          record_abandoned(&qe);
04960          reason = QUEUE_LEAVEUNAVAIL;
04961          res = 0;
04962          break;
04963       }
04964 
04965       /* Leave if we have exceeded our queuetimeout */
04966       if (qe.expire && (time(NULL) >= qe.expire)) {
04967          record_abandoned(&qe);
04968          reason = QUEUE_TIMEOUT;
04969          res = 0;
04970          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
04971          break;
04972       }
04973 
04974       /* If using dynamic realtime members, we should regenerate the member list for this queue */
04975       update_realtime_members(qe.parent);
04976       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04977       res = wait_a_bit(&qe);
04978       if (res)
04979          goto stop;
04980 
04981       /* Since this is a priority queue and
04982        * it is not sure that we are still at the head
04983        * of the queue, go and check for our turn again.
04984        */
04985       if (!is_our_turn(&qe)) {
04986          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
04987          goto check_turns;
04988       }
04989    }
04990 
04991 stop:
04992    if (res) {
04993       if (res < 0) {
04994          if (!qe.handled) {
04995             record_abandoned(&qe);
04996             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04997                "%d|%d|%ld", qe.pos, qe.opos,
04998                (long) time(NULL) - qe.start);
04999             res = -1;
05000          } else if (qcontinue) {
05001             reason = QUEUE_CONTINUE;
05002             res = 0;
05003          }
05004       } else if (qe.valid_digits) {
05005          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05006             "%s|%d", qe.digits, qe.pos);
05007       }
05008    }
05009 
05010    /* Don't allow return code > 0 */
05011    if (res >= 0) {
05012       res = 0; 
05013       if (ringing) {
05014          ast_indicate(chan, -1);
05015       } else {
05016          ast_moh_stop(chan);
05017       }        
05018       ast_stopstream(chan);
05019    }
05020 
05021    set_queue_variables(qe.parent, qe.chan);
05022 
05023    leave_queue(&qe);
05024    if (reason != QUEUE_UNKNOWN)
05025       set_queue_result(chan, reason);
05026 
05027    return res;
05028 }
05029 
05030 /*!
05031  * \brief create interface var with all queue details.
05032  * \retval 0 on success
05033  * \retval -1 on error
05034 */
05035 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05036 {
05037    int res = -1;
05038    struct call_queue *q, tmpq = {
05039       .name = data,  
05040    };
05041 
05042    char interfacevar[256] = "";
05043    float sl = 0;
05044 
05045    if (ast_strlen_zero(data)) {
05046       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05047       return -1;
05048    }
05049 
05050    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05051       ao2_lock(q);
05052       if (q->setqueuevar) {
05053          sl = 0;
05054          res = 0;
05055 
05056          if (q->callscompleted > 0) {
05057             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05058          }
05059 
05060          snprintf(interfacevar, sizeof(interfacevar),
05061             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05062             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05063 
05064          pbx_builtin_setvar_multiple(chan, interfacevar);
05065       }
05066 
05067       ao2_unlock(q);
05068       queue_unref(q);
05069    } else {
05070       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05071    }
05072 
05073    snprintf(buf, len, "%d", res);
05074 
05075    return 0;
05076 }
05077 
05078 /*! 
05079  * \brief Get number either busy / free or total members of a specific queue
05080  * \retval number of members (busy / free / total)
05081  * \retval -1 on error
05082 */
05083 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05084 {
05085    int count = 0;
05086    struct member *m;
05087    struct ao2_iterator mem_iter;
05088    struct call_queue *q;
05089    char *option;
05090 
05091    if (ast_strlen_zero(data)) {
05092       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05093       return -1;
05094    }
05095 
05096    if ((option = strchr(data, ',')))
05097       *option++ = '\0';
05098    else
05099       option = "logged";
05100    if ((q = load_realtime_queue(data))) {
05101       ao2_lock(q);
05102       if (!strcasecmp(option, "logged")) {
05103          mem_iter = ao2_iterator_init(q->members, 0);
05104          while ((m = ao2_iterator_next(&mem_iter))) {
05105             /* Count the agents who are logged in and presently answering calls */
05106             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05107                count++;
05108             }
05109             ao2_ref(m, -1);
05110          }
05111       } else if (!strcasecmp(option, "free")) {
05112          mem_iter = ao2_iterator_init(q->members, 0);
05113          while ((m = ao2_iterator_next(&mem_iter))) {
05114             /* Count the agents who are logged in and presently answering calls */
05115             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05116                count++;
05117             }
05118             ao2_ref(m, -1);
05119          }
05120       } else /* must be "count" */
05121          count = q->membercount;
05122       ao2_unlock(q);
05123       queue_unref(q);
05124    } else
05125       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05126 
05127    snprintf(buf, len, "%d", count);
05128 
05129    return 0;
05130 }
05131 
05132 /*! 
05133  * \brief Get the total number of members in a specific queue (Deprecated)
05134  * \retval number of members 
05135  * \retval -1 on error 
05136 */
05137 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05138 {
05139    int count = 0;
05140    struct member *m;
05141    struct call_queue *q;
05142    struct ao2_iterator mem_iter;
05143    static int depflag = 1;
05144 
05145    if (depflag) {
05146       depflag = 0;
05147       ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
05148    }
05149 
05150    if (ast_strlen_zero(data)) {
05151       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05152       return -1;
05153    }
05154    
05155    if ((q = load_realtime_queue(data))) {
05156       ao2_lock(q);
05157       mem_iter = ao2_iterator_init(q->members, 0);
05158       while ((m = ao2_iterator_next(&mem_iter))) {
05159          /* Count the agents who are logged in and presently answering calls */
05160          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05161             count++;
05162          }
05163          ao2_ref(m, -1);
05164       }
05165       ao2_unlock(q);
05166       queue_unref(q);
05167    } else
05168       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05169 
05170    snprintf(buf, len, "%d", count);
05171 
05172    return 0;
05173 }
05174 
05175 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
05176 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05177 {
05178    int count = 0;
05179    struct call_queue *q, tmpq = {
05180       .name = data,  
05181    };
05182    struct ast_variable *var = NULL;
05183 
05184    buf[0] = '\0';
05185    
05186    if (ast_strlen_zero(data)) {
05187       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05188       return -1;
05189    }
05190 
05191    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05192       ao2_lock(q);
05193       count = q->count;
05194       ao2_unlock(q);
05195       queue_unref(q);
05196    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05197       /* if the queue is realtime but was not found in memory, this
05198        * means that the queue had been deleted from memory since it was 
05199        * "dead." This means it has a 0 waiting count
05200        */
05201       count = 0;
05202       ast_variables_destroy(var);
05203    } else
05204       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05205 
05206    snprintf(buf, len, "%d", count);
05207 
05208    return 0;
05209 }
05210 
05211 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
05212 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05213 {
05214    struct call_queue *q, tmpq = {
05215       .name = data,  
05216    };
05217    struct member *m;
05218 
05219    /* Ensure an otherwise empty list doesn't return garbage */
05220    buf[0] = '\0';
05221 
05222    if (ast_strlen_zero(data)) {
05223       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05224       return -1;
05225    }
05226 
05227    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05228       int buflen = 0, count = 0;
05229       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05230 
05231       ao2_lock(q);
05232       while ((m = ao2_iterator_next(&mem_iter))) {
05233          /* strcat() is always faster than printf() */
05234          if (count++) {
05235             strncat(buf + buflen, ",", len - buflen - 1);
05236             buflen++;
05237          }
05238          strncat(buf + buflen, m->membername, len - buflen - 1);
05239          buflen += strlen(m->membername);
05240          /* Safeguard against overflow (negative length) */
05241          if (buflen >= len - 2) {
05242             ao2_ref(m, -1);
05243             ast_log(LOG_WARNING, "Truncating list\n");
05244             break;
05245          }
05246          ao2_ref(m, -1);
05247       }
05248       ao2_unlock(q);
05249       queue_unref(q);
05250    } else
05251       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05252 
05253    /* We should already be terminated, but let's make sure. */
05254    buf[len - 1] = '\0';
05255 
05256    return 0;
05257 }
05258 
05259 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
05260 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05261 {
05262    int penalty;
05263    AST_DECLARE_APP_ARGS(args,
05264       AST_APP_ARG(queuename);
05265       AST_APP_ARG(interface);
05266    );
05267    /* Make sure the returned value on error is NULL. */
05268    buf[0] = '\0';
05269 
05270    if (ast_strlen_zero(data)) {
05271       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05272       return -1;
05273    }
05274 
05275    AST_STANDARD_APP_ARGS(args, data);
05276 
05277    if (args.argc < 2) {
05278       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05279       return -1;
05280    }
05281 
05282    penalty = get_member_penalty (args.queuename, args.interface);
05283    
05284    if (penalty >= 0) /* remember that buf is already '\0' */
05285       snprintf (buf, len, "%d", penalty);
05286 
05287    return 0;
05288 }
05289 
05290 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
05291 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
05292 {
05293    int penalty;
05294    AST_DECLARE_APP_ARGS(args,
05295       AST_APP_ARG(queuename);
05296       AST_APP_ARG(interface);
05297    );
05298 
05299    if (ast_strlen_zero(data)) {
05300       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05301       return -1;
05302    }
05303 
05304    AST_STANDARD_APP_ARGS(args, data);
05305 
05306    if (args.argc < 2) {
05307       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05308       return -1;
05309    }
05310 
05311    penalty = atoi(value);
05312 
05313    if (ast_strlen_zero(args.interface)) {
05314       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05315       return -1;
05316    }
05317 
05318    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05319    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05320       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05321       return -1;
05322    }
05323 
05324    return 0;
05325 }
05326 
05327 static struct ast_custom_function queuevar_function = {
05328    .name = "QUEUE_VARIABLES",
05329    .synopsis = "Return Queue information in variables",
05330    .syntax = "QUEUE_VARIABLES(<queuename>)",
05331    .desc =
05332 "Makes the following queue variables available.\n"
05333 "QUEUEMAX maxmimum number of calls allowed\n"
05334 "QUEUESTRATEGY the strategy of the queue\n"
05335 "QUEUECALLS number of calls currently in the queue\n"
05336 "QUEUEHOLDTIME current average hold time\n"
05337 "QUEUECOMPLETED number of completed calls for the queue\n"
05338 "QUEUEABANDONED number of abandoned calls\n"
05339 "QUEUESRVLEVEL queue service level\n"
05340 "QUEUESRVLEVELPERF current service level performance\n"
05341 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
05342    .read = queue_function_var,
05343 };
05344 
05345 static struct ast_custom_function queuemembercount_function = {
05346    .name = "QUEUE_MEMBER",
05347    .synopsis = "Count number of members answering a queue",
05348    .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
05349    .desc =
05350 "Returns the number of members currently associated with the specified queue.\n"
05351 "One of three options may be passed to determine the count returned:\n"
05352    "\"logged\" - Returns the number of logged-in members for the specified queue\n"
05353    "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
05354    "\"count\" - Returns the total number of members for the specified queue\n",
05355    .read = queue_function_qac,
05356 };
05357 
05358 static struct ast_custom_function queuemembercount_dep = {
05359    .name = "QUEUE_MEMBER_COUNT",
05360    .synopsis = "Count number of members answering a queue",
05361    .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
05362    .desc =
05363 "Returns the number of members currently associated with the specified queue.\n\n"
05364 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
05365    .read = queue_function_qac_dep,
05366 };
05367 
05368 static struct ast_custom_function queuewaitingcount_function = {
05369    .name = "QUEUE_WAITING_COUNT",
05370    .synopsis = "Count number of calls currently waiting in a queue",
05371    .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
05372    .desc =
05373 "Returns the number of callers currently waiting in the specified queue.\n",
05374    .read = queue_function_queuewaitingcount,
05375 };
05376 
05377 static struct ast_custom_function queuememberlist_function = {
05378    .name = "QUEUE_MEMBER_LIST",
05379    .synopsis = "Returns a list of interfaces on a queue",
05380    .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
05381    .desc =
05382 "Returns a comma-separated list of members associated with the specified queue.\n",
05383    .read = queue_function_queuememberlist,
05384 };
05385 
05386 static struct ast_custom_function queuememberpenalty_function = {
05387    .name = "QUEUE_MEMBER_PENALTY",
05388    .synopsis = "Gets or sets queue members penalty.",
05389    .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
05390    .desc =
05391 "Gets or sets queue members penalty\n",
05392    .read = queue_function_memberpenalty_read,
05393    .write = queue_function_memberpenalty_write,
05394 };
05395 
05396 static int reload_queue_rules(int reload)
05397 {
05398    struct ast_config *cfg;
05399    struct rule_list *rl_iter, *new_rl;
05400    struct penalty_rule *pr_iter;
05401    char *rulecat = NULL;
05402    struct ast_variable *rulevar = NULL;
05403    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05404    
05405    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05406       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05407    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05408       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05409       return AST_MODULE_LOAD_SUCCESS;
05410    } else {
05411       AST_LIST_LOCK(&rule_lists);
05412       while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05413          while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05414             ast_free(pr_iter);
05415          ast_free(rl_iter);
05416       }
05417       while ((rulecat = ast_category_browse(cfg, rulecat))) {
05418          if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05419             ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05420             AST_LIST_UNLOCK(&rule_lists);
05421             return AST_MODULE_LOAD_FAILURE;
05422          } else {
05423             ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05424             AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05425             for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05426                if(!strcasecmp(rulevar->name, "penaltychange")) {
05427                   insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05428                } else {
05429                   ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05430                }
05431          }
05432       }
05433       AST_LIST_UNLOCK(&rule_lists);
05434    }
05435 
05436    ast_config_destroy(cfg);
05437 
05438    return AST_MODULE_LOAD_SUCCESS;
05439 }
05440 
05441 
05442 static int reload_queues(int reload)
05443 {
05444    struct call_queue *q;
05445    struct ast_config *cfg;
05446    char *cat, *tmp;
05447    struct ast_variable *var;
05448    struct member *cur, *newm;
05449    struct ao2_iterator mem_iter;
05450    int new;
05451    const char *general_val = NULL;
05452    char parse[80];
05453    char *interface, *state_interface;
05454    char *membername = NULL;
05455    int penalty;
05456    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05457    struct ao2_iterator queue_iter;
05458    AST_DECLARE_APP_ARGS(args,
05459       AST_APP_ARG(interface);
05460       AST_APP_ARG(penalty);
05461       AST_APP_ARG(membername);
05462       AST_APP_ARG(state_interface);
05463    );
05464 
05465    /*First things first. Let's load queuerules.conf*/
05466    if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05467       return AST_MODULE_LOAD_FAILURE;
05468       
05469    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05470       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05471       return 0;
05472    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05473       return 0;
05474    ao2_lock(queues);
05475    use_weight=0;
05476    /* Mark all queues as dead for the moment */
05477    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05478    while ((q = ao2_iterator_next(&queue_iter))) {
05479       if (!q->realtime) {
05480          q->dead = 1;
05481          q->found = 0;
05482       }
05483       queue_unref(q);
05484    }
05485 
05486    /* Chug through config file */
05487    cat = NULL;
05488    while ((cat = ast_category_browse(cfg, cat)) ) {
05489       if (!strcasecmp(cat, "general")) {  
05490          /* Initialize global settings */
05491          queue_keep_stats = 0;
05492          if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05493             queue_keep_stats = ast_true(general_val);
05494          queue_persistent_members = 0;
05495          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05496             queue_persistent_members = ast_true(general_val);
05497          autofill_default = 0;
05498          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05499             autofill_default = ast_true(general_val);
05500          montype_default = 0;
05501          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05502             if (!strcasecmp(general_val, "mixmonitor"))
05503                montype_default = 1;
05504          }
05505          update_cdr = 0;
05506          if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05507             update_cdr = ast_true(general_val);
05508          shared_lastcall = 0;
05509          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05510             shared_lastcall = ast_true(general_val);
05511       } else { /* Define queue */
05512          /* Look for an existing one */
05513          struct call_queue tmpq = {
05514             .name = cat,
05515          };
05516          if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05517             /* Make one then */
05518             if (!(q = alloc_queue(cat))) {
05519                /* TODO: Handle memory allocation failure */
05520             }
05521             new = 1;
05522          } else
05523             new = 0;
05524          if (q) {
05525             const char *tmpvar = NULL;
05526             if (!new)
05527                ao2_lock(q);
05528             /* Check if a queue with this name already exists */
05529             if (q->found) {
05530                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05531                if (!new) {
05532                   ao2_unlock(q);
05533                   queue_unref(q);
05534                }
05535                continue;
05536             }
05537             /* Due to the fact that the "linear" strategy will have a different allocation
05538              * scheme for queue members, we must devise the queue's strategy before other initializations
05539              */
05540             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05541                q->strategy = strat2int(tmpvar);
05542                if (q->strategy < 0) {
05543                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05544                   tmpvar, q->name);
05545                   q->strategy = QUEUE_STRATEGY_RINGALL;
05546                }
05547             } else
05548                q->strategy = QUEUE_STRATEGY_RINGALL;
05549             /* Re-initialize the queue, and clear statistics */
05550             init_queue(q);
05551             if (!queue_keep_stats) 
05552                clear_queue(q);
05553             mem_iter = ao2_iterator_init(q->members, 0);
05554             while ((cur = ao2_iterator_next(&mem_iter))) {
05555                if (!cur->dynamic) {
05556                   cur->delme = 1;
05557                }
05558                ao2_ref(cur, -1);
05559             }
05560             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05561                if (!strcasecmp(var->name, "member")) {
05562                   struct member tmpmem;
05563                   membername = NULL;
05564 
05565                   if (ast_strlen_zero(var->value)) {
05566                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05567                      continue;
05568                   }
05569 
05570                   /* Add a new member */
05571                   ast_copy_string(parse, var->value, sizeof(parse));
05572                   
05573                   AST_STANDARD_APP_ARGS(args, parse);
05574 
05575                   interface = args.interface;
05576                   if (!ast_strlen_zero(args.penalty)) {
05577                      tmp = args.penalty;
05578                      while (*tmp && *tmp < 33) tmp++;
05579                      penalty = atoi(tmp);
05580                      if (penalty < 0) {
05581                         penalty = 0;
05582                      }
05583                   } else
05584                      penalty = 0;
05585 
05586                   if (!ast_strlen_zero(args.membername)) {
05587                      membername = args.membername;
05588                      while (*membername && *membername < 33) membername++;
05589                   }
05590 
05591                   if (!ast_strlen_zero(args.state_interface)) {
05592                      state_interface = args.state_interface;
05593                      while (*state_interface && *state_interface < 33) state_interface++;
05594                   } else
05595                      state_interface = interface;
05596 
05597                   /* Find the old position in the list */
05598                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05599                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05600                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05601                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05602                      remove_from_interfaces(cur->state_interface, 0);
05603                   }
05604                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05605                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05606                      add_to_interfaces(state_interface);
05607                   ao2_link(q->members, newm);
05608                   ao2_ref(newm, -1);
05609                   newm = NULL;
05610 
05611                   if (cur)
05612                      ao2_ref(cur, -1);
05613                   else {
05614                      q->membercount++;
05615                   }
05616                } else {
05617                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05618                }
05619             }
05620 
05621             /* Free remaining members marked as delme */
05622             mem_iter = ao2_iterator_init(q->members, 0);
05623             while ((cur = ao2_iterator_next(&mem_iter))) {
05624                if (! cur->delme) {
05625                   ao2_ref(cur, -1);
05626                   continue;
05627                }
05628                q->membercount--;
05629                ao2_unlink(q->members, cur);
05630                remove_from_interfaces(cur->interface, 0);
05631                ao2_ref(cur, -1);
05632             }
05633 
05634             if (new) {
05635                ao2_link(queues, q);
05636             } else 
05637                ao2_unlock(q);
05638             queue_unref(q);
05639          }
05640       }
05641    }
05642    ast_config_destroy(cfg);
05643    queue_iter = ao2_iterator_init(queues, 0);
05644    while ((q = ao2_iterator_next(&queue_iter))) {
05645       if (q->dead) {
05646          ao2_unlink(queues, q);
05647       } else {
05648          ao2_lock(q);
05649          mem_iter = ao2_iterator_init(q->members, 0);
05650          while ((cur = ao2_iterator_next(&mem_iter))) {
05651             if (cur->dynamic)
05652                q->membercount++;
05653             cur->status = ast_device_state(cur->state_interface);
05654             ao2_ref(cur, -1);
05655          }
05656          ao2_unlock(q);
05657       }
05658       queue_unref(q);
05659    }
05660    ao2_unlock(queues);
05661    return 1;
05662 }
05663 
05664 /*! \brief direct ouput to manager or cli with proper terminator */
05665 static void do_print(struct mansession *s, int fd, const char *str)
05666 {
05667    if (s)
05668       astman_append(s, "%s\r\n", str);
05669    else
05670       ast_cli(fd, "%s\n", str);
05671 }
05672 
05673 /*! 
05674  * \brief Show queue(s) status and statistics 
05675  * 
05676  * List the queues strategy, calls processed, members logged in,
05677  * other queue statistics such as avg hold time.
05678 */
05679 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
05680 {
05681    struct call_queue *q;
05682    struct ast_str *out = ast_str_alloca(240);
05683    int found = 0;
05684    time_t now = time(NULL);
05685    struct ao2_iterator queue_iter;
05686    struct ao2_iterator mem_iter;
05687 
05688    if (argc != 2 && argc != 3)
05689       return CLI_SHOWUSAGE;
05690 
05691    if (argc == 3) { /* specific queue */
05692       if ((q = load_realtime_queue(argv[2]))) {
05693          queue_unref(q);
05694       }
05695    } else if (ast_check_realtime("queues")) {
05696       /* This block is to find any queues which are defined in realtime but
05697        * which have not yet been added to the in-core container
05698        */
05699       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05700       char *queuename;
05701       if (cfg) {
05702          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05703             if ((q = load_realtime_queue(queuename))) {
05704                queue_unref(q);
05705             }
05706          }
05707          ast_config_destroy(cfg);
05708       }
05709    }
05710 
05711    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05712    ao2_lock(queues);
05713    while ((q = ao2_iterator_next(&queue_iter))) {
05714       float sl;
05715       struct call_queue *realtime_queue = NULL;
05716 
05717       ao2_lock(q);
05718       /* This check is to make sure we don't print information for realtime
05719        * queues which have been deleted from realtime but which have not yet
05720        * been deleted from the in-core container
05721        */
05722       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05723          ao2_unlock(q);
05724          queue_unref(q);
05725          continue;
05726       } else if (q->realtime) {
05727          queue_unref(realtime_queue);
05728       }
05729       if (argc == 3 && strcasecmp(q->name, argv[2])) {
05730          ao2_unlock(q);
05731          queue_unref(q);
05732          continue;
05733       }
05734       found = 1;
05735 
05736       ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05737       if (q->maxlen)
05738          ast_str_append(&out, 0, "%d", q->maxlen);
05739       else
05740          ast_str_append(&out, 0, "unlimited");
05741       sl = 0;
05742       if (q->callscompleted > 0)
05743          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05744       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05745          int2strat(q->strategy), q->holdtime, q->weight,
05746          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05747       do_print(s, fd, out->str);
05748       if (!ao2_container_count(q->members))
05749          do_print(s, fd, "   No Members");
05750       else {
05751          struct member *mem;
05752 
05753          do_print(s, fd, "   Members: ");
05754          mem_iter = ao2_iterator_init(q->members, 0);
05755          while ((mem = ao2_iterator_next(&mem_iter))) {
05756             ast_str_set(&out, 0, "      %s", mem->membername);
05757             if (strcasecmp(mem->membername, mem->interface)) {
05758                ast_str_append(&out, 0, " (%s)", mem->interface);
05759             }
05760             if (mem->penalty)
05761                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05762             ast_str_append(&out, 0, "%s%s%s (%s)",
05763                mem->dynamic ? " (dynamic)" : "",
05764                mem->realtime ? " (realtime)" : "",
05765                mem->paused ? " (paused)" : "",
05766                devstate2str(mem->status));
05767             if (mem->calls)
05768                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05769                   mem->calls, (long) (time(NULL) - mem->lastcall));
05770             else
05771                ast_str_append(&out, 0, " has taken no calls yet");
05772             do_print(s, fd, out->str);
05773             ao2_ref(mem, -1);
05774          }
05775       }
05776       if (!q->head)
05777          do_print(s, fd, "   No Callers");
05778       else {
05779          struct queue_ent *qe;
05780          int pos = 1;
05781 
05782          do_print(s, fd, "   Callers: ");
05783          for (qe = q->head; qe; qe = qe->next) {
05784             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
05785                pos++, qe->chan->name, (long) (now - qe->start) / 60,
05786                (long) (now - qe->start) % 60, qe->prio);
05787             do_print(s, fd, out->str);
05788          }
05789       }
05790       do_print(s, fd, ""); /* blank line between entries */
05791       ao2_unlock(q);
05792       queue_unref(q); /* Unref the iterator's reference */
05793    }
05794    ao2_unlock(queues);
05795    if (!found) {
05796       if (argc == 3)
05797          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05798       else
05799          ast_str_set(&out, 0, "No queues.");
05800       do_print(s, fd, out->str);
05801    }
05802    return CLI_SUCCESS;
05803 }
05804 
05805 static char *complete_queue(const char *line, const char *word, int pos, int state)
05806 {
05807    struct call_queue *q;
05808    char *ret = NULL;
05809    int which = 0;
05810    int wordlen = strlen(word);
05811    struct ao2_iterator queue_iter;
05812 
05813    queue_iter = ao2_iterator_init(queues, 0);
05814    while ((q = ao2_iterator_next(&queue_iter))) {
05815       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05816          ret = ast_strdup(q->name);
05817          queue_unref(q);
05818          break;
05819       }
05820       queue_unref(q);
05821    }
05822 
05823    return ret;
05824 }
05825 
05826 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
05827 {
05828    if (pos == 2)
05829       return complete_queue(line, word, pos, state);
05830    return NULL;
05831 }
05832 
05833 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
05834 {
05835    switch ( cmd ) {
05836    case CLI_INIT:
05837       e->command = "queue show";
05838       e->usage =
05839          "Usage: queue show\n"
05840          "       Provides summary information on a specified queue.\n";
05841       return NULL;
05842    case CLI_GENERATE:
05843       return complete_queue_show(a->line, a->word, a->pos, a->n); 
05844    }
05845 
05846    return __queues_show(NULL, a->fd, a->argc, a->argv);
05847 }
05848 
05849 /*!\brief callback to display queues status in manager
05850    \addtogroup Group_AMI
05851  */
05852 static int manager_queues_show(struct mansession *s, const struct message *m)
05853 {
05854    char *a[] = { "queue", "show" };
05855 
05856    __queues_show(s, -1, 2, a);
05857    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05858 
05859    return RESULT_SUCCESS;
05860 }
05861 
05862 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
05863 {
05864    const char *rule = astman_get_header(m, "Rule");
05865    struct rule_list *rl_iter;
05866    struct penalty_rule *pr_iter;
05867 
05868    AST_LIST_LOCK(&rule_lists);
05869    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05870       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05871          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05872          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05873             astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
05874          }
05875          if (!ast_strlen_zero(rule))
05876             break;
05877       }
05878    }
05879    AST_LIST_UNLOCK(&rule_lists);
05880 
05881    astman_append(s, "\r\n\r\n");
05882 
05883    return RESULT_SUCCESS;
05884 }
05885 
05886 /*! \brief Summary of queue info via the AMI */
05887 static int manager_queues_summary(struct mansession *s, const struct message *m)
05888 {
05889    time_t now;
05890    int qmemcount = 0;
05891    int qmemavail = 0;
05892    int qchancount = 0;
05893    int qlongestholdtime = 0;
05894    const char *id = astman_get_header(m, "ActionID");
05895    const char *queuefilter = astman_get_header(m, "Queue");
05896    char idText[256] = "";
05897    struct call_queue *q;
05898    struct queue_ent *qe;
05899    struct member *mem;
05900    struct ao2_iterator queue_iter;
05901    struct ao2_iterator mem_iter;
05902 
05903    astman_send_ack(s, m, "Queue summary will follow");
05904    time(&now);
05905    if (!ast_strlen_zero(id))
05906       snprintf(idText, 256, "ActionID: %s\r\n", id);
05907    queue_iter = ao2_iterator_init(queues, 0);
05908    while ((q = ao2_iterator_next(&queue_iter))) {
05909       ao2_lock(q);
05910 
05911       /* List queue properties */
05912       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05913          /* Reset the necessary local variables if no queuefilter is set*/
05914          qmemcount = 0;
05915          qmemavail = 0;
05916          qchancount = 0;
05917          qlongestholdtime = 0;
05918 
05919          /* List Queue Members */
05920          mem_iter = ao2_iterator_init(q->members, 0);
05921          while ((mem = ao2_iterator_next(&mem_iter))) {
05922             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05923                ++qmemcount;
05924                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05925                   ++qmemavail;
05926                }
05927             }
05928             ao2_ref(mem, -1);
05929          }
05930          for (qe = q->head; qe; qe = qe->next) {
05931             if ((now - qe->start) > qlongestholdtime) {
05932                qlongestholdtime = now - qe->start;
05933             }
05934             ++qchancount;
05935          }
05936          astman_append(s, "Event: QueueSummary\r\n"
05937             "Queue: %s\r\n"
05938             "LoggedIn: %d\r\n"
05939             "Available: %d\r\n"
05940             "Callers: %d\r\n" 
05941             "HoldTime: %d\r\n"
05942             "LongestHoldTime: %d\r\n"
05943             "%s"
05944             "\r\n",
05945             q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05946       }
05947       ao2_unlock(q);
05948       queue_unref(q);
05949    }
05950    astman_append(s,
05951       "Event: QueueSummaryComplete\r\n"
05952       "%s"
05953       "\r\n", idText);
05954 
05955    return RESULT_SUCCESS;
05956 }
05957 
05958 /*! \brief Queue status info via AMI */
05959 static int manager_queues_status(struct mansession *s, const struct message *m)
05960 {
05961    time_t now;
05962    int pos;
05963    const char *id = astman_get_header(m,"ActionID");
05964    const char *queuefilter = astman_get_header(m,"Queue");
05965    const char *memberfilter = astman_get_header(m,"Member");
05966    char idText[256] = "";
05967    struct call_queue *q;
05968    struct queue_ent *qe;
05969    float sl = 0;
05970    struct member *mem;
05971    struct ao2_iterator queue_iter;
05972    struct ao2_iterator mem_iter;
05973 
05974    astman_send_ack(s, m, "Queue status will follow");
05975    time(&now);
05976    if (!ast_strlen_zero(id))
05977       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05978 
05979    queue_iter = ao2_iterator_init(queues, 0);
05980    while ((q = ao2_iterator_next(&queue_iter))) {
05981       ao2_lock(q);
05982 
05983       /* List queue properties */
05984       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05985          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05986          astman_append(s, "Event: QueueParams\r\n"
05987             "Queue: %s\r\n"
05988             "Max: %d\r\n"
05989             "Strategy: %s\r\n"
05990             "Calls: %d\r\n"
05991             "Holdtime: %d\r\n"
05992             "Completed: %d\r\n"
05993             "Abandoned: %d\r\n"
05994             "ServiceLevel: %d\r\n"
05995             "ServicelevelPerf: %2.1f\r\n"
05996             "Weight: %d\r\n"
05997             "%s"
05998             "\r\n",
05999             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06000             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06001          /* List Queue Members */
06002          mem_iter = ao2_iterator_init(q->members, 0);
06003          while ((mem = ao2_iterator_next(&mem_iter))) {
06004             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06005                astman_append(s, "Event: QueueMember\r\n"
06006                   "Queue: %s\r\n"
06007                   "Name: %s\r\n"
06008                   "Location: %s\r\n"
06009                   "Membership: %s\r\n"
06010                   "Penalty: %d\r\n"
06011                   "CallsTaken: %d\r\n"
06012                   "LastCall: %d\r\n"
06013                   "Status: %d\r\n"
06014                   "Paused: %d\r\n"
06015                   "%s"
06016                   "\r\n",
06017                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06018                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06019             }
06020             ao2_ref(mem, -1);
06021          }
06022          /* List Queue Entries */
06023          pos = 1;
06024          for (qe = q->head; qe; qe = qe->next) {
06025             astman_append(s, "Event: QueueEntry\r\n"
06026                "Queue: %s\r\n"
06027                "Position: %d\r\n"
06028                "Channel: %s\r\n"
06029                "CallerIDNum: %s\r\n"
06030                "CallerIDName: %s\r\n"
06031                "Wait: %ld\r\n"
06032                "%s"
06033                "\r\n",
06034                q->name, pos++, qe->chan->name,
06035                S_OR(qe->chan->cid.cid_num, "unknown"),
06036                S_OR(qe->chan->cid.cid_name, "unknown"),
06037                (long) (now - qe->start), idText);
06038          }
06039       }
06040       ao2_unlock(q);
06041       queue_unref(q);
06042    }
06043 
06044    astman_append(s,
06045       "Event: QueueStatusComplete\r\n"
06046       "%s"
06047       "\r\n",idText);
06048 
06049    return RESULT_SUCCESS;
06050 }
06051 
06052 static int manager_add_queue_member(struct mansession *s, const struct message *m)
06053 {
06054    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06055    int paused, penalty = 0;
06056 
06057    queuename = astman_get_header(m, "Queue");
06058    interface = astman_get_header(m, "Interface");
06059    penalty_s = astman_get_header(m, "Penalty");
06060    paused_s = astman_get_header(m, "Paused");
06061    membername = astman_get_header(m, "MemberName");
06062    state_interface = astman_get_header(m, "StateInterface");
06063 
06064    if (ast_strlen_zero(queuename)) {
06065       astman_send_error(s, m, "'Queue' not specified.");
06066       return 0;
06067    }
06068 
06069    if (ast_strlen_zero(interface)) {
06070       astman_send_error(s, m, "'Interface' not specified.");
06071       return 0;
06072    }
06073 
06074    if (ast_strlen_zero(penalty_s))
06075       penalty = 0;
06076    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
06077       penalty = 0;
06078 
06079    if (ast_strlen_zero(paused_s))
06080       paused = 0;
06081    else
06082       paused = abs(ast_true(paused_s));
06083 
06084    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06085    case RES_OKAY:
06086       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06087       astman_send_ack(s, m, "Added interface to queue");
06088       break;
06089    case RES_EXISTS:
06090       astman_send_error(s, m, "Unable to add interface: Already there");
06091       break;
06092    case RES_NOSUCHQUEUE:
06093       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06094       break;
06095    case RES_OUTOFMEMORY:
06096       astman_send_error(s, m, "Out of memory");
06097       break;
06098    }
06099 
06100    return 0;
06101 }
06102 
06103 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
06104 {
06105    const char *queuename, *interface;
06106 
06107    queuename = astman_get_header(m, "Queue");
06108    interface = astman_get_header(m, "Interface");
06109 
06110    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06111       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06112       return 0;
06113    }
06114 
06115    switch (remove_from_queue(queuename, interface)) {
06116    case RES_OKAY:
06117       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06118       astman_send_ack(s, m, "Removed interface from queue");
06119       break;
06120    case RES_EXISTS:
06121       astman_send_error(s, m, "Unable to remove interface: Not there");
06122       break;
06123    case RES_NOSUCHQUEUE:
06124       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06125       break;
06126    case RES_OUTOFMEMORY:
06127       astman_send_error(s, m, "Out of memory");
06128       break;
06129    case RES_NOT_DYNAMIC:
06130       astman_send_error(s, m, "Member not dynamic");
06131       break;
06132    }
06133 
06134    return 0;
06135 }
06136 
06137 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
06138 {
06139    const char *queuename, *interface, *paused_s, *reason;
06140    int paused;
06141 
06142    interface = astman_get_header(m, "Interface");
06143    paused_s = astman_get_header(m, "Paused");
06144    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06145    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06146 
06147    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06148       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06149       return 0;
06150    }
06151 
06152    paused = abs(ast_true(paused_s));
06153 
06154    if (set_member_paused(queuename, interface, reason, paused))
06155       astman_send_error(s, m, "Interface not found");
06156    else
06157       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06158    return 0;
06159 }
06160 
06161 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
06162 {
06163    const char *queuename, *event, *message, *interface, *uniqueid;
06164 
06165    queuename = astman_get_header(m, "Queue");
06166    uniqueid = astman_get_header(m, "UniqueId");
06167    interface = astman_get_header(m, "Interface");
06168    event = astman_get_header(m, "Event");
06169    message = astman_get_header(m, "Message");
06170 
06171    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06172       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06173       return 0;
06174    }
06175 
06176    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06177    astman_send_ack(s, m, "Event added successfully");
06178 
06179    return 0;
06180 }
06181 
06182 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
06183 {
06184    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06185    switch (pos) {
06186    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06187       return NULL;
06188    case 4: /* only one possible match, "to" */
06189       return state == 0 ? ast_strdup("to") : NULL;
06190    case 5: /* <queue> */
06191       return complete_queue(line, word, pos, state);
06192    case 6: /* only one possible match, "penalty" */
06193       return state == 0 ? ast_strdup("penalty") : NULL;
06194    case 7:
06195       if (state < 100) {      /* 0-99 */
06196          char *num;
06197          if ((num = ast_malloc(3))) {
06198             sprintf(num, "%d", state);
06199          }
06200          return num;
06201       } else {
06202          return NULL;
06203       }
06204    case 8: /* only one possible match, "as" */
06205       return state == 0 ? ast_strdup("as") : NULL;
06206    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06207       return NULL;
06208    default:
06209       return NULL;
06210    }
06211 }
06212 
06213 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
06214 {
06215    const char *queuename, *interface, *penalty_s;
06216    int penalty;
06217 
06218    interface = astman_get_header(m, "Interface");
06219    penalty_s = astman_get_header(m, "Penalty");
06220    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06221    queuename = astman_get_header(m, "Queue");
06222 
06223    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06224       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06225       return 0;
06226    }
06227  
06228    penalty = atoi(penalty_s);
06229 
06230    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06231       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06232    else
06233       astman_send_ack(s, m, "Interface penalty set successfully");
06234 
06235    return 0;
06236 }
06237 
06238 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06239 {
06240    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06241    int penalty;
06242 
06243    switch ( cmd ) {
06244    case CLI_INIT:
06245       e->command = "queue add member";
06246       e->usage =
06247          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06248          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
06249       return NULL;
06250    case CLI_GENERATE:
06251       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06252    }
06253 
06254    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06255       return CLI_SHOWUSAGE;
06256    } else if (strcmp(a->argv[4], "to")) {
06257       return CLI_SHOWUSAGE;
06258    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06259       return CLI_SHOWUSAGE;
06260    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06261       return CLI_SHOWUSAGE;
06262    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06263       return CLI_SHOWUSAGE;
06264    }
06265 
06266    queuename = a->argv[5];
06267    interface = a->argv[3];
06268    if (a->argc >= 8) {
06269       if (sscanf(a->argv[7], "%d", &penalty) == 1) {
06270          if (penalty < 0) {
06271             ast_cli(a->fd, "Penalty must be >= 0\n");
06272             penalty = 0;
06273          }
06274       } else {
06275          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06276          penalty = 0;
06277       }
06278    } else {
06279       penalty = 0;
06280    }
06281 
06282    if (a->argc >= 10) {
06283       membername = a->argv[9];
06284    }
06285 
06286    if (a->argc >= 12) {
06287       state_interface = a->argv[11];
06288    }
06289 
06290    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06291    case RES_OKAY:
06292       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06293       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06294       return CLI_SUCCESS;
06295    case RES_EXISTS:
06296       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06297       return CLI_FAILURE;
06298    case RES_NOSUCHQUEUE:
06299       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06300       return CLI_FAILURE;
06301    case RES_OUTOFMEMORY:
06302       ast_cli(a->fd, "Out of memory\n");
06303       return CLI_FAILURE;
06304    case RES_NOT_DYNAMIC:
06305       ast_cli(a->fd, "Member not dynamic\n");
06306       return CLI_FAILURE;
06307    default:
06308       return CLI_FAILURE;
06309    }
06310 }
06311 
06312 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
06313 {
06314    int which = 0;
06315    struct call_queue *q;
06316    struct member *m;
06317    struct ao2_iterator queue_iter;
06318    struct ao2_iterator mem_iter;
06319    int wordlen = strlen(word);
06320 
06321    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06322    if (pos > 5 || pos < 3)
06323       return NULL;
06324    if (pos == 4)   /* only one possible match, 'from' */
06325       return (state == 0 ? ast_strdup("from") : NULL);
06326 
06327    if (pos == 5)   /* No need to duplicate code */
06328       return complete_queue(line, word, pos, state);
06329 
06330    /* here is the case for 3, <member> */
06331    queue_iter = ao2_iterator_init(queues, 0);
06332    while ((q = ao2_iterator_next(&queue_iter))) {
06333       ao2_lock(q);
06334       mem_iter = ao2_iterator_init(q->members, 0);
06335       while ((m = ao2_iterator_next(&mem_iter))) {
06336          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06337             char *tmp;
06338             ao2_unlock(q);
06339             tmp = ast_strdup(m->interface);
06340             ao2_ref(m, -1);
06341             queue_unref(q);
06342             return tmp;
06343          }
06344          ao2_ref(m, -1);
06345       }
06346       ao2_unlock(q);
06347       queue_unref(q);
06348    }
06349 
06350    return NULL;
06351 }
06352 
06353 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06354 {
06355    char *queuename, *interface;
06356 
06357    switch (cmd) {
06358    case CLI_INIT:
06359       e->command = "queue remove member";
06360       e->usage = 
06361          "Usage: queue remove member <channel> from <queue>\n"
06362          "       Remove a specific channel from a queue.\n";
06363       return NULL;
06364    case CLI_GENERATE:
06365       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06366    }
06367 
06368    if (a->argc != 6) {
06369       return CLI_SHOWUSAGE;
06370    } else if (strcmp(a->argv[4], "from")) {
06371       return CLI_SHOWUSAGE;
06372    }
06373 
06374    queuename = a->argv[5];
06375    interface = a->argv[3];
06376 
06377    switch (remove_from_queue(queuename, interface)) {
06378    case RES_OKAY:
06379       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06380       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06381       return CLI_SUCCESS;
06382    case RES_EXISTS:
06383       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06384       return CLI_FAILURE;
06385    case RES_NOSUCHQUEUE:
06386       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06387       return CLI_FAILURE;
06388    case RES_OUTOFMEMORY:
06389       ast_cli(a->fd, "Out of memory\n");
06390       return CLI_FAILURE;
06391    default:
06392       return CLI_FAILURE;
06393    }
06394 }
06395 
06396 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
06397 {
06398    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06399    switch (pos) {
06400    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06401       return NULL;
06402    case 4:  /* only one possible match, "queue" */
06403       return state == 0 ? ast_strdup("queue") : NULL;
06404    case 5:  /* <queue> */
06405       return complete_queue(line, word, pos, state);
06406    case 6: /* "reason" */
06407       return state == 0 ? ast_strdup("reason") : NULL;
06408    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06409       return NULL;
06410    default:
06411       return NULL;
06412    }
06413 }
06414 
06415 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06416 {
06417    char *queuename, *interface, *reason;
06418    int paused;
06419 
06420    switch (cmd) {
06421    case CLI_INIT:
06422       e->command = "queue {pause|unpause} member";
06423       e->usage = 
06424          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06425          "  Pause or unpause a queue member. Not specifying a particular queue\n"
06426          "  will pause or unpause a member across all queues to which the member\n"
06427          "  belongs.\n";
06428       return NULL;
06429    case CLI_GENERATE:
06430       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06431    }
06432 
06433    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06434       return CLI_SHOWUSAGE;
06435    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06436       return CLI_SHOWUSAGE;
06437    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06438       return CLI_SHOWUSAGE;
06439    }
06440 
06441 
06442    interface = a->argv[3];
06443    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06444    reason = a->argc == 8 ? a->argv[7] : NULL;
06445    paused = !strcasecmp(a->argv[1], "pause");
06446 
06447    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06448       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06449       if (!ast_strlen_zero(queuename))
06450          ast_cli(a->fd, " in queue '%s'", queuename);
06451       if (!ast_strlen_zero(reason))
06452          ast_cli(a->fd, " for reason '%s'", reason);
06453       ast_cli(a->fd, "\n");
06454       return CLI_SUCCESS;
06455    } else {
06456       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06457       if (!ast_strlen_zero(queuename))
06458          ast_cli(a->fd, " in queue '%s'", queuename);
06459       if (!ast_strlen_zero(reason))
06460          ast_cli(a->fd, " for reason '%s'", reason);
06461       ast_cli(a->fd, "\n");
06462       return CLI_FAILURE;
06463    }
06464 }
06465 
06466 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
06467 {
06468    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06469    switch (pos) {
06470    case 4:
06471       if (state == 0) {
06472          return ast_strdup("on");
06473       } else {
06474          return NULL;
06475       }
06476    case 6:
06477       if (state == 0) {
06478          return ast_strdup("in");
06479       } else {
06480          return NULL;
06481       }
06482    case 7:
06483       return complete_queue(line, word, pos, state);
06484    default:
06485       return NULL;
06486    }
06487 }
06488  
06489 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06490 {
06491    char *queuename = NULL, *interface;
06492    int penalty = 0;
06493 
06494    switch (cmd) {
06495    case CLI_INIT:
06496       e->command = "queue set penalty";
06497       e->usage = 
06498       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06499       "  Set a member's penalty in the queue specified. If no queue is specified\n"
06500       "  then that interface's penalty is set in all queues to which that interface is a member\n";
06501       return NULL;
06502    case CLI_GENERATE:
06503       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06504    }
06505 
06506    if (a->argc != 6 && a->argc != 8) {
06507       return CLI_SHOWUSAGE;
06508    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06509       return CLI_SHOWUSAGE;
06510    }
06511 
06512    if (a->argc == 8)
06513       queuename = a->argv[7];
06514    interface = a->argv[5];
06515    penalty = atoi(a->argv[3]);
06516 
06517    switch (set_member_penalty(queuename, interface, penalty)) {
06518    case RESULT_SUCCESS:
06519       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06520       return CLI_SUCCESS;
06521    case RESULT_FAILURE:
06522       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06523       return CLI_FAILURE;
06524    default:
06525       return CLI_FAILURE;
06526    }
06527 }
06528 
06529 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state) 
06530 {
06531    int which = 0;
06532    struct rule_list *rl_iter;
06533    int wordlen = strlen(word);
06534    char *ret = NULL;
06535    if (pos != 3) /* Wha? */ {
06536       return NULL;
06537    }
06538 
06539    AST_LIST_LOCK(&rule_lists);
06540    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06541       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06542          ret = ast_strdup(rl_iter->name);
06543          break;
06544       }
06545    }
06546    AST_LIST_UNLOCK(&rule_lists);
06547 
06548    return ret;
06549 }
06550 
06551 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06552 {
06553    char *rule;
06554    struct rule_list *rl_iter;
06555    struct penalty_rule *pr_iter;
06556    switch (cmd) {
06557    case CLI_INIT:
06558       e->command = "queue rules show";
06559       e->usage =
06560       "Usage: queue rules show [rulename]\n"
06561       "  Show the list of rules associated with rulename. If no\n"
06562       "  rulename is specified, list all rules defined in queuerules.conf\n";
06563       return NULL;
06564    case CLI_GENERATE:
06565       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06566    }
06567 
06568    if (a->argc != 3 && a->argc != 4)
06569       return CLI_SHOWUSAGE;
06570 
06571    rule = a->argc == 4 ? a->argv[3] : "";
06572    AST_LIST_LOCK(&rule_lists);
06573    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06574       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06575          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06576          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06577             ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
06578          }
06579       }
06580    }
06581    AST_LIST_UNLOCK(&rule_lists);
06582    return CLI_SUCCESS; 
06583 }
06584 
06585 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06586 {
06587    switch (cmd) {
06588       case CLI_INIT:
06589          e->command = "queue rules reload";
06590          e->usage = 
06591             "Usage: queue rules reload\n"
06592             "  Reloads rules defined in queuerules.conf\n";
06593          return NULL;
06594       case CLI_GENERATE:
06595          return NULL;
06596    }
06597    reload_queue_rules(1);
06598    return CLI_SUCCESS;
06599 }
06600 
06601 static const char qpm_cmd_usage[] = 
06602 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
06603 
06604 static const char qum_cmd_usage[] =
06605 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
06606 
06607 static const char qsmp_cmd_usage[] =
06608 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
06609 
06610 static struct ast_cli_entry cli_queue[] = {
06611    AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
06612    AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
06613    AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
06614    AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
06615    AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
06616    AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
06617    AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
06618 };
06619 
06620 static int unload_module(void)
06621 {
06622    int res;
06623    struct ast_context *con;
06624    struct ao2_iterator q_iter;
06625    struct call_queue *q = NULL;
06626 
06627    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06628    res = ast_manager_unregister("QueueStatus");
06629    res |= ast_manager_unregister("Queues");
06630    res |= ast_manager_unregister("QueueRule");
06631    res |= ast_manager_unregister("QueueSummary");
06632    res |= ast_manager_unregister("QueueAdd");
06633    res |= ast_manager_unregister("QueueRemove");
06634    res |= ast_manager_unregister("QueuePause");
06635    res |= ast_manager_unregister("QueueLog");
06636    res |= ast_manager_unregister("QueuePenalty");
06637    res |= ast_unregister_application(app_aqm);
06638    res |= ast_unregister_application(app_rqm);
06639    res |= ast_unregister_application(app_pqm);
06640    res |= ast_unregister_application(app_upqm);
06641    res |= ast_unregister_application(app_ql);
06642    res |= ast_unregister_application(app);
06643    res |= ast_custom_function_unregister(&queuevar_function);
06644    res |= ast_custom_function_unregister(&queuemembercount_function);
06645    res |= ast_custom_function_unregister(&queuemembercount_dep);
06646    res |= ast_custom_function_unregister(&queuememberlist_function);
06647    res |= ast_custom_function_unregister(&queuewaitingcount_function);
06648    res |= ast_custom_function_unregister(&queuememberpenalty_function);
06649 
06650    if (device_state_sub)
06651       ast_event_unsubscribe(device_state_sub);
06652 
06653    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06654       ast_context_remove_extension2(con, "s", 1, NULL, 0);
06655       ast_context_destroy(con, "app_queue"); /* leave no trace */
06656    }
06657 
06658    clear_and_free_interfaces();
06659 
06660    q_iter = ao2_iterator_init(queues, 0);
06661    while ((q = ao2_iterator_next(&q_iter))) {
06662       ao2_unlink(queues, q);
06663       queue_unref(q);
06664    }
06665    ao2_ref(queues, -1);
06666    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06667    ast_unload_realtime("queue_members");
06668    return res;
06669 }
06670 
06671 static int load_module(void)
06672 {
06673    int res;
06674    struct ast_context *con;
06675 
06676    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06677 
06678    if (!reload_queues(0))
06679       return AST_MODULE_LOAD_DECLINE;
06680 
06681    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06682    if (!con)
06683       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06684    else
06685       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06686 
06687    if (queue_persistent_members)
06688       reload_queue_members();
06689 
06690    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06691    res = ast_register_application(app, queue_exec, synopsis, descrip);
06692    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06693    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06694    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06695    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06696    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06697    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06698    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06699    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06700    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06701    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06702    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06703    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06704    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
06705    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06706    res |= ast_custom_function_register(&queuevar_function);
06707    res |= ast_custom_function_register(&queuemembercount_function);
06708    res |= ast_custom_function_register(&queuemembercount_dep);
06709    res |= ast_custom_function_register(&queuememberlist_function);
06710    res |= ast_custom_function_register(&queuewaitingcount_function);
06711    res |= ast_custom_function_register(&queuememberpenalty_function);
06712 
06713    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06714       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06715    }
06716 
06717    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06718       res = -1;
06719    }
06720 
06721    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06722 
06723    return res ? AST_MODULE_LOAD_DECLINE : 0;
06724 }
06725 
06726 static int reload(void)
06727 {
06728    ast_unload_realtime("queue_members");
06729    reload_queues(1);
06730    return 0;
06731 }
06732 
06733 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
06734       .load = load_module,
06735       .unload = unload_module,
06736       .reload = reload,
06737           );
06738 

Generated on Fri Jul 24 00:40:40 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7