Fri Aug 24 02:22:10 2007

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

Generated on Fri Aug 24 02:22:10 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1