Sat Nov 25 00:45:28 2006

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

Generated on Sat Nov 25 00:45:29 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6