Sun Dec 18 20:55:36 2011

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

Generated on Sun Dec 18 20:55:37 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6