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

Generated on Mon May 14 04:42:53 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1