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