Mon Mar 31 07:37:55 2008

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

Generated on Mon Mar 31 07:37:55 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1