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

Generated on Mon Mar 20 08:25:36 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1