Fri May 26 01:45:27 2006

Asterisk developer's documentation


app_queue.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief True call queues with optional send URL on answer
00022  *
00023  * \arg Config in \ref Config_qu queues.conf
00024  * 
00025  * \par Development notes
00026  * \note 2004-11-25: Persistent Dynamic Members added by:
00027  *             NetNation Communications (www.netnation.com)
00028  *             Kevin Lindsay <kevinl@netnation.com>
00029  * 
00030  *             Each dynamic agent in each queue is now stored in the astdb.
00031  *             When asterisk is restarted, each agent will be automatically
00032  *             readded into their recorded queues. This feature can be
00033  *             configured with the 'persistent_members=<1|0>' setting in the
00034  *             '[general]' category in queues.conf. The default is on.
00035  * 
00036  * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
00037  *
00038  * \note These features added by David C. Troy <dave@toad.net>:
00039  *    - Per-queue holdtime calculation
00040  *    - Estimated holdtime announcement
00041  *    - Position announcement
00042  *    - Abandoned/completed call counters
00043  *    - Failout timer passed as optional app parameter
00044  *    - Optional monitoring of calls, started when call is answered
00045  *
00046  * Patch Version 1.07 2003-12-24 01
00047  *
00048  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
00049  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
00050  *
00051  * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
00052  * by Matthew Enger <m.enger@xi.com.au>
00053  *
00054  * \ingroup applications
00055  */
00056 
00057 #include <stdlib.h>
00058 #include <errno.h>
00059 #include <unistd.h>
00060 #include <string.h>
00061 #include <stdlib.h>
00062 #include <stdio.h>
00063 #include <sys/time.h>
00064 #include <sys/signal.h>
00065 #include <netinet/in.h>
00066 
00067 #include "asterisk.h"
00068 
00069 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 17335 $")
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 || q->realtime) {
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             }
02958             res = -1;
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                }
03056                res = -1;
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                ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03068                record_abandoned(&qe);
03069                reason = QUEUE_TIMEOUT;
03070                res = 0;
03071                break;
03072             }
03073             /* Since this is a priority queue and 
03074              * it is not sure that we are still at the head
03075              * of the queue, go and check for our turn again.
03076              */
03077             if (!is_our_turn(&qe)) {
03078                if (option_debug)
03079                   ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03080                         qe.chan->name);
03081                goto check_turns;
03082             }
03083          }
03084       }
03085       /* Don't allow return code > 0 */
03086       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03087          res = 0; 
03088          if (ringing) {
03089             ast_indicate(chan, -1);
03090          } else {
03091             ast_moh_stop(chan);
03092          }        
03093          ast_stopstream(chan);
03094       }
03095       leave_queue(&qe);
03096       if (reason != QUEUE_UNKNOWN)
03097          set_queue_result(chan, reason);
03098    } else {
03099       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
03100       set_queue_result(chan, reason);
03101       res = 0;
03102    }
03103    LOCAL_USER_REMOVE(u);
03104    return res;
03105 }
03106 
03107 static char *queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03108 {
03109    int count = 0;
03110    struct ast_call_queue *q;
03111    struct localuser *u;
03112    struct member *m;
03113 
03114    LOCAL_USER_ACF_ADD(u);
03115 
03116    ast_copy_string(buf, "0", len);
03117    
03118    if (ast_strlen_zero(data)) {
03119       ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
03120       LOCAL_USER_REMOVE(u);
03121       return buf;
03122    }
03123 
03124    ast_mutex_lock(&qlock);
03125 
03126    /* Find the right queue */
03127    for (q = queues; q; q = q->next) {
03128       if (!strcasecmp(q->name, data)) {
03129          ast_mutex_lock(&q->lock);
03130          break;
03131       }
03132    }
03133 
03134    ast_mutex_unlock(&qlock);
03135 
03136    if (q) {
03137       for (m = q->members; m; m = m->next) {
03138          /* Count the agents who are logged in and presently answering calls */
03139          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03140             count++;
03141          }
03142       }
03143       ast_mutex_unlock(&q->lock);
03144    }
03145 
03146    snprintf(buf, len, "%d", count);
03147    LOCAL_USER_REMOVE(u);
03148    return buf;
03149 }
03150 
03151 static struct ast_custom_function queueagentcount_function = {
03152    .name = "QUEUEAGENTCOUNT",
03153    .synopsis = "Count number of agents answering a queue",
03154    .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03155    .read = queue_function_qac,
03156 };
03157 
03158 static void reload_queues(void)
03159 {
03160    struct ast_call_queue *q, *ql, *qn;
03161    struct ast_config *cfg;
03162    char *cat, *tmp;
03163    struct ast_variable *var;
03164    struct member *prev, *cur;
03165    int new;
03166    char *general_val = NULL;
03167    char interface[80];
03168    int penalty;
03169    
03170    cfg = ast_config_load("queues.conf");
03171    if (!cfg) {
03172       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03173       return;
03174    }
03175    memset(interface, 0, sizeof(interface));
03176    ast_mutex_lock(&qlock);
03177    use_weight=0;
03178    /* Mark all queues as dead for the moment */
03179    q = queues;
03180    while(q) {
03181       q->dead = 1;
03182       q = q->next;
03183    }
03184    /* Chug through config file */
03185    cat = ast_category_browse(cfg, NULL);
03186    while(cat) {
03187       if (!strcasecmp(cat, "general")) {  
03188          /* Initialize global settings */
03189          queue_persistent_members = 0;
03190          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03191             queue_persistent_members = ast_true(general_val);
03192       } else { /* Define queue */
03193          /* Look for an existing one */
03194          q = queues;
03195          while(q) {
03196             if (!strcmp(q->name, cat))
03197                break;
03198             q = q->next;
03199          }
03200          if (!q) {
03201             /* Make one then */
03202             q = alloc_queue(cat);
03203             new = 1;
03204          } else
03205             new = 0;
03206          if (q) {
03207             if (!new)
03208                ast_mutex_lock(&q->lock);
03209             /* Re-initialize the queue, and clear statistics */
03210             init_queue(q);
03211             clear_queue(q);
03212             free_members(q, 0);
03213             prev = q->members;
03214             if (prev) {
03215                /* find the end of any dynamic members */
03216                while(prev->next)
03217                   prev = prev->next;
03218             }
03219             var = ast_variable_browse(cfg, cat);
03220             while(var) {
03221                if (!strcasecmp(var->name, "member")) {
03222                   /* Add a new member */
03223                   ast_copy_string(interface, var->value, sizeof(interface));
03224                   if ((tmp = strchr(interface, ','))) {
03225                      *tmp = '\0';
03226                      tmp++;
03227                      penalty = atoi(tmp);
03228                      if (penalty < 0) {
03229                         penalty = 0;
03230                      }
03231                   } else
03232                      penalty = 0;
03233                   cur = create_queue_member(interface, penalty, 0);
03234                   if (cur) {
03235                      if (prev)
03236                         prev->next = cur;
03237                      else
03238                         q->members = cur;
03239                      prev = cur;
03240                   }
03241                } else {
03242                   queue_set_param(q, var->name, var->value, var->lineno, 1);
03243                }
03244                var = var->next;
03245             }
03246             if (!new) 
03247                ast_mutex_unlock(&q->lock);
03248             if (new) {
03249                q->next = queues;
03250                queues = q;
03251             }
03252          }
03253       }
03254       cat = ast_category_browse(cfg, cat);
03255    }
03256    ast_config_destroy(cfg);
03257    q = queues;
03258    ql = NULL;
03259    while(q) {
03260       qn = q->next;
03261       if (q->dead) {
03262          if (ql)
03263             ql->next = q->next;
03264          else
03265             queues = q->next;
03266          if (!q->count) {
03267             destroy_queue(q);
03268          } else
03269             ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
03270       } else {
03271          for (cur = q->members; cur; cur = cur->next)
03272             cur->status = ast_device_state(cur->interface);
03273          ql = q;
03274       }
03275       q = qn;
03276    }
03277    ast_mutex_unlock(&qlock);
03278 }
03279 
03280 static int __queues_show(int manager, int fd, int argc, char **argv, int queue_show)
03281 {
03282    struct ast_call_queue *q;
03283    struct queue_ent *qe;
03284    struct member *mem;
03285    int pos;
03286    time_t now;
03287    char max_buf[80];
03288    char *max;
03289    size_t max_left;
03290    float sl = 0;
03291    char *term = manager ? "\r\n" : "\n";
03292 
03293    time(&now);
03294    if ((!queue_show && argc != 2) || (queue_show && argc != 3))
03295       return RESULT_SHOWUSAGE;
03296 
03297    /* We only want to load realtime queues when a specific queue is asked for. */
03298    if (queue_show)
03299       load_realtime_queue(argv[2]);
03300 
03301    ast_mutex_lock(&qlock);
03302 
03303    q = queues;
03304    if (!q) {   
03305       ast_mutex_unlock(&qlock);
03306       if (queue_show)
03307          ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03308       else
03309          ast_cli(fd, "No queues.%s", term);
03310       return RESULT_SUCCESS;
03311    }
03312    while (q) {
03313       ast_mutex_lock(&q->lock);
03314       if (queue_show) {
03315          if (strcasecmp(q->name, argv[2]) != 0) {
03316             ast_mutex_unlock(&q->lock);
03317             q = q->next;
03318             if (!q) {
03319                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03320                break;
03321             }
03322             continue;
03323          }
03324       }
03325       max_buf[0] = '\0';
03326       max = max_buf;
03327       max_left = sizeof(max_buf);
03328       if (q->maxlen)
03329          ast_build_string(&max, &max_left, "%d", q->maxlen);
03330       else
03331          ast_build_string(&max, &max_left, "unlimited");
03332       sl = 0;
03333       if(q->callscompleted > 0)
03334          sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03335       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",
03336          q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03337       if (q->members) {
03338          ast_cli(fd, "   Members: %s", term);
03339          for (mem = q->members; mem; mem = mem->next) {
03340             max_buf[0] = '\0';
03341             max = max_buf;
03342             max_left = sizeof(max_buf);
03343             if (mem->penalty)
03344                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
03345             if (mem->dynamic)
03346                ast_build_string(&max, &max_left, " (dynamic)");
03347             if (mem->paused)
03348                ast_build_string(&max, &max_left, " (paused)");
03349             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
03350             if (mem->calls) {
03351                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
03352                       mem->calls, (long)(time(NULL) - mem->lastcall));
03353             } else
03354                ast_build_string(&max, &max_left, " has taken no calls yet");
03355             ast_cli(fd, "      %s%s%s", mem->interface, max_buf, term);
03356          }
03357       } else
03358          ast_cli(fd, "   No Members%s", term);
03359       if (q->head) {
03360          pos = 1;
03361          ast_cli(fd, "   Callers: %s", term);
03362          for (qe = q->head; qe; qe = qe->next) 
03363             ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, qe->chan->name,
03364                (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio, term);
03365       } else
03366          ast_cli(fd, "   No Callers%s", term);
03367       ast_cli(fd, "%s", term);
03368       ast_mutex_unlock(&q->lock);
03369       q = q->next;
03370       if (queue_show)
03371          break;
03372    }
03373    ast_mutex_unlock(&qlock);
03374    return RESULT_SUCCESS;
03375 }
03376 
03377 static int queues_show(int fd, int argc, char **argv)
03378 {
03379    return __queues_show(0, fd, argc, argv, 0);
03380 }
03381 
03382 static int queue_show(int fd, int argc, char **argv)
03383 {
03384    return __queues_show(0, fd, argc, argv, 1);
03385 }
03386 
03387 static char *complete_queue(char *line, char *word, int pos, int state)
03388 {
03389    struct ast_call_queue *q;
03390    int which=0;
03391    
03392    ast_mutex_lock(&qlock);
03393    for (q = queues; q; q = q->next) {
03394       if (!strncasecmp(word, q->name, strlen(word))) {
03395          if (++which > state)
03396             break;
03397       }
03398    }
03399    ast_mutex_unlock(&qlock);
03400    return q ? strdup(q->name) : NULL;
03401 }
03402 
03403 /*!\brief callback to display queues status in manager 
03404    \addtogroup Group_AMI 
03405  */
03406 static int manager_queues_show( struct mansession *s, struct message *m )
03407 {
03408    char *a[] = { "show", "queues" };
03409    __queues_show(1, s->fd, 2, a, 0);
03410    ast_cli(s->fd, "\r\n\r\n");   /* Properly terminate Manager output */
03411 
03412    return RESULT_SUCCESS;
03413 } 
03414 
03415 /* Dump queue status */
03416 static int manager_queues_status( struct mansession *s, struct message *m )
03417 {
03418    time_t now;
03419    int pos;
03420    char *id = astman_get_header(m,"ActionID");
03421    char *queuefilter = astman_get_header(m,"Queue");
03422    char *memberfilter = astman_get_header(m,"Member");
03423    char idText[256] = "";
03424    struct ast_call_queue *q;
03425    struct queue_ent *qe;
03426    float sl = 0;
03427    struct member *mem;
03428 
03429    astman_send_ack(s, m, "Queue status will follow");
03430    time(&now);
03431    ast_mutex_lock(&qlock);
03432    if (!ast_strlen_zero(id)) {
03433       snprintf(idText,256,"ActionID: %s\r\n",id);
03434    }
03435    for (q = queues; q; q = q->next) {
03436       ast_mutex_lock(&q->lock);
03437 
03438       /* List queue properties */
03439       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
03440          if(q->callscompleted > 0)
03441             sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03442          ast_cli(s->fd, "Event: QueueParams\r\n"
03443                   "Queue: %s\r\n"
03444                   "Max: %d\r\n"
03445                   "Calls: %d\r\n"
03446                   "Holdtime: %d\r\n"
03447                   "Completed: %d\r\n"
03448                   "Abandoned: %d\r\n"
03449                   "ServiceLevel: %d\r\n"
03450                   "ServicelevelPerf: %2.1f\r\n"
03451                   "Weight: %d\r\n"
03452                   "%s"
03453                   "\r\n",
03454                      q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
03455                      q->callsabandoned, q->servicelevel, sl, q->weight, idText);
03456          /* List Queue Members */
03457          for (mem = q->members; mem; mem = mem->next) {
03458             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
03459                ast_cli(s->fd, "Event: QueueMember\r\n"
03460                   "Queue: %s\r\n"
03461                   "Location: %s\r\n"
03462                   "Membership: %s\r\n"
03463                   "Penalty: %d\r\n"
03464                   "CallsTaken: %d\r\n"
03465                   "LastCall: %d\r\n"
03466                   "Status: %d\r\n"
03467                   "Paused: %d\r\n"
03468                   "%s"
03469                   "\r\n",
03470                      q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
03471                      mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
03472             }
03473          }
03474          /* List Queue Entries */
03475          pos = 1;
03476          for (qe = q->head; qe; qe = qe->next) {
03477             ast_cli(s->fd, "Event: QueueEntry\r\n"
03478                "Queue: %s\r\n"
03479                "Position: %d\r\n"
03480                "Channel: %s\r\n"
03481                "CallerID: %s\r\n"
03482                "CallerIDName: %s\r\n"
03483                "Wait: %ld\r\n"
03484                "%s"
03485                "\r\n", 
03486                   q->name, pos++, qe->chan->name, 
03487                   qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
03488                   qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
03489                   (long)(now - qe->start), idText);
03490          }
03491       }
03492       ast_mutex_unlock(&q->lock);
03493    }
03494    ast_mutex_unlock(&qlock);
03495 
03496    ast_cli(s->fd,
03497       "Event: QueueStatusComplete\r\n"
03498       "%s"
03499       "\r\n",idText);
03500 
03501 
03502    return RESULT_SUCCESS;
03503 }
03504 
03505 static int manager_add_queue_member(struct mansession *s, struct message *m)
03506 {
03507    char *queuename, *interface, *penalty_s, *paused_s;
03508    int paused, penalty = 0;
03509 
03510    queuename = astman_get_header(m, "Queue");
03511    interface = astman_get_header(m, "Interface");
03512    penalty_s = astman_get_header(m, "Penalty");
03513    paused_s = astman_get_header(m, "Paused");
03514 
03515    if (ast_strlen_zero(queuename)) {
03516       astman_send_error(s, m, "'Queue' not specified.");
03517       return 0;
03518    }
03519 
03520    if (ast_strlen_zero(interface)) {
03521       astman_send_error(s, m, "'Interface' not specified.");
03522       return 0;
03523    }
03524 
03525    if (ast_strlen_zero(penalty_s))
03526       penalty = 0;
03527    else if (sscanf(penalty_s, "%d", &penalty) != 1) {
03528       penalty = 0;
03529    }
03530 
03531    if (ast_strlen_zero(paused_s))
03532       paused = 0;
03533    else
03534       paused = abs(ast_true(paused_s));
03535 
03536    switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) {
03537    case RES_OKAY:
03538       astman_send_ack(s, m, "Added interface to queue");
03539       break;
03540    case RES_EXISTS:
03541       astman_send_error(s, m, "Unable to add interface: Already there");
03542       break;
03543    case RES_NOSUCHQUEUE:
03544       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
03545       break;
03546    case RES_OUTOFMEMORY:
03547       astman_send_error(s, m, "Out of memory");
03548       break;
03549    }
03550    return 0;
03551 }
03552 
03553 static int manager_remove_queue_member(struct mansession *s, struct message *m)
03554 {
03555    char *queuename, *interface;
03556 
03557    queuename = astman_get_header(m, "Queue");
03558    interface = astman_get_header(m, "Interface");
03559 
03560    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
03561       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
03562       return 0;
03563    }
03564 
03565    switch (remove_from_queue(queuename, interface)) {
03566    case RES_OKAY:
03567       astman_send_ack(s, m, "Removed interface from queue");
03568       break;
03569    case RES_EXISTS:
03570       astman_send_error(s, m, "Unable to remove interface: Not there");
03571       break;
03572    case RES_NOSUCHQUEUE:
03573       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
03574       break;
03575    case RES_OUTOFMEMORY:
03576       astman_send_error(s, m, "Out of memory");
03577       break;
03578    }
03579    return 0;
03580 }
03581 
03582 static int manager_pause_queue_member(struct mansession *s, struct message *m)
03583 {
03584    char *queuename, *interface, *paused_s;
03585    int paused;
03586 
03587    interface = astman_get_header(m, "Interface");
03588    paused_s = astman_get_header(m, "Paused");
03589    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
03590 
03591    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
03592       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
03593       return 0;
03594    }
03595 
03596    paused = abs(ast_true(paused_s));
03597 
03598    if (set_member_paused(queuename, interface, paused))
03599       astman_send_error(s, m, "Interface not found");
03600    else
03601       if (paused)
03602          astman_send_ack(s, m, "Interface paused successfully");
03603       else
03604          astman_send_ack(s, m, "Interface unpaused successfully");
03605 
03606    return 0;
03607 }
03608 
03609 static int handle_add_queue_member(int fd, int argc, char *argv[])
03610 {
03611    char *queuename, *interface;
03612    int penalty;
03613 
03614    if ((argc != 6) && (argc != 8)) {
03615       return RESULT_SHOWUSAGE;
03616    } else if (strcmp(argv[4], "to")) {
03617       return RESULT_SHOWUSAGE;
03618    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
03619       return RESULT_SHOWUSAGE;
03620    }
03621 
03622    queuename = argv[5];
03623    interface = argv[3];
03624    if (argc == 8) {
03625       if (sscanf(argv[7], "%d", &penalty) == 1) {
03626          if (penalty < 0) {
03627             ast_cli(fd, "Penalty must be >= 0\n");
03628             penalty = 0;
03629          }
03630       } else {
03631          ast_cli(fd, "Penalty must be an integer >= 0\n");
03632          penalty = 0;
03633       }
03634    } else {
03635       penalty = 0;
03636    }
03637 
03638    switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
03639    case RES_OKAY:
03640       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
03641       return RESULT_SUCCESS;
03642    case RES_EXISTS:
03643       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
03644       return RESULT_FAILURE;
03645    case RES_NOSUCHQUEUE:
03646       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
03647       return RESULT_FAILURE;
03648    case RES_OUTOFMEMORY:
03649       ast_cli(fd, "Out of memory\n");
03650       return RESULT_FAILURE;
03651    default:
03652       return RESULT_FAILURE;
03653    }
03654 }
03655 
03656 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
03657 {
03658    /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
03659    switch (pos) {
03660    case 3:
03661       /* Don't attempt to complete name of member (infinite possibilities) */
03662       return NULL;
03663    case 4:
03664       if (state == 0) {
03665          return strdup("to");
03666       } else {
03667          return NULL;
03668       }
03669    case 5:
03670       /* No need to duplicate code */
03671       return complete_queue(line, word, pos, state);
03672    case 6:
03673       if (state == 0) {
03674          return strdup("penalty");
03675       } else {
03676          return NULL;
03677       }
03678    case 7:
03679       if (state < 100) {   /* 0-99 */
03680          char *num = malloc(3);
03681          if (num) {
03682             sprintf(num, "%d", state);
03683          }
03684          return num;
03685       } else {
03686          return NULL;
03687       }
03688    default:
03689       return NULL;
03690    }
03691 }
03692 
03693 static int handle_remove_queue_member(int fd, int argc, char *argv[])
03694 {
03695    char *queuename, *interface;
03696 
03697    if (argc != 6) {
03698       return RESULT_SHOWUSAGE;
03699    } else if (strcmp(argv[4], "from")) {
03700       return RESULT_SHOWUSAGE;
03701    }
03702 
03703    queuename = argv[5];
03704    interface = argv[3];
03705 
03706    switch (remove_from_queue(queuename, interface)) {
03707    case RES_OKAY:
03708       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
03709       return RESULT_SUCCESS;
03710    case RES_EXISTS:
03711       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
03712       return RESULT_FAILURE;
03713    case RES_NOSUCHQUEUE:
03714       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
03715       return RESULT_FAILURE;
03716    case RES_OUTOFMEMORY:
03717       ast_cli(fd, "Out of memory\n");
03718       return RESULT_FAILURE;
03719    default:
03720       return RESULT_FAILURE;
03721    }
03722 }
03723 
03724 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
03725 {
03726    int which = 0;
03727    struct ast_call_queue *q;
03728    struct member *m;
03729 
03730    /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
03731    if ((pos > 5) || (pos < 3)) {
03732       return NULL;
03733    }
03734    if (pos == 4) {
03735       if (state == 0) {
03736          return strdup("from");
03737       } else {
03738          return NULL;
03739       }
03740    }
03741 
03742    if (pos == 5) {
03743       /* No need to duplicate code */
03744       return complete_queue(line, word, pos, state);
03745    }
03746 
03747    if (queues != NULL) {
03748       for (q = queues ; q ; q = q->next) {
03749          ast_mutex_lock(&q->lock);
03750          for (m = q->members ; m ; m = m->next) {
03751             if (++which > state) {
03752                ast_mutex_unlock(&q->lock);
03753                return strdup(m->interface);
03754             }
03755          }
03756          ast_mutex_unlock(&q->lock);
03757       }
03758    }
03759    return NULL;
03760 }
03761 
03762 static char show_queues_usage[] = 
03763 "Usage: show queues\n"
03764 "       Provides summary information on call queues.\n";
03765 
03766 static struct ast_cli_entry cli_show_queues = {
03767    { "show", "queues", NULL }, queues_show, 
03768    "Show status of queues", show_queues_usage, NULL };
03769 
03770 static char show_queue_usage[] = 
03771 "Usage: show queue\n"
03772 "       Provides summary information on a specified queue.\n";
03773 
03774 static struct ast_cli_entry cli_show_queue = {
03775    { "show", "queue", NULL }, queue_show, 
03776    "Show status of a specified queue", show_queue_usage, complete_queue };
03777 
03778 static char aqm_cmd_usage[] =
03779 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
03780 
03781 static struct ast_cli_entry cli_add_queue_member = {
03782    { "add", "queue", "member", NULL }, handle_add_queue_member,
03783    "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
03784 
03785 static char rqm_cmd_usage[] =
03786 "Usage: remove queue member <channel> from <queue>\n";
03787 
03788 static struct ast_cli_entry cli_remove_queue_member = {
03789    { "remove", "queue", "member", NULL }, handle_remove_queue_member,
03790    "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
03791 
03792 int unload_module(void)
03793 {
03794    int res;
03795 
03796    res = ast_cli_unregister(&cli_show_queue);
03797    res |= ast_cli_unregister(&cli_show_queues);
03798    res |= ast_cli_unregister(&cli_add_queue_member);
03799    res |= ast_cli_unregister(&cli_remove_queue_member);
03800    res |= ast_manager_unregister("Queues");
03801    res |= ast_manager_unregister("QueueStatus");
03802    res |= ast_manager_unregister("QueueAdd");
03803    res |= ast_manager_unregister("QueueRemove");
03804    res |= ast_manager_unregister("QueuePause");
03805    ast_devstate_del(statechange_queue, NULL);
03806    res |= ast_unregister_application(app_aqm);
03807    res |= ast_unregister_application(app_rqm);
03808    res |= ast_unregister_application(app_pqm);
03809    res |= ast_unregister_application(app_upqm);
03810    res |= ast_custom_function_unregister(&queueagentcount_function);
03811    res |= ast_unregister_application(app);
03812 
03813    STANDARD_HANGUP_LOCALUSERS;
03814 
03815    return res;
03816 }
03817 
03818 int load_module(void)
03819 {
03820    int res;
03821    
03822    res = ast_register_application(app, queue_exec, synopsis, descrip);
03823    res |= ast_cli_register(&cli_show_queue);
03824    res |= ast_cli_register(&cli_show_queues);
03825    res |= ast_cli_register(&cli_add_queue_member);
03826    res |= ast_cli_register(&cli_remove_queue_member);
03827    res |= ast_devstate_add(statechange_queue, NULL);
03828    res |= ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
03829    res |= ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
03830    res |= ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
03831    res |= ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
03832    res |= ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" );
03833    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
03834    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
03835    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
03836    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
03837    res |= ast_custom_function_register(&queueagentcount_function);
03838 
03839    if (!res) { 
03840       reload_queues();
03841       if (queue_persistent_members)
03842          reload_queue_members();
03843    }
03844 
03845    return res;
03846 }
03847 
03848 
03849 int reload(void)
03850 {
03851    reload_queues();
03852    return 0;
03853 }
03854 
03855 char *description(void)
03856 {
03857    return tdesc;
03858 }
03859 
03860 int usecount(void)
03861 {
03862    int res;
03863    STANDARD_USECOUNT(res);
03864    return res;
03865 }
03866 
03867 char *key()
03868 {
03869    return ASTERISK_GPL_KEY;
03870 }

Generated on Fri May 26 01:45:27 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6