Sun Aug 6 15:02:24 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:25 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2