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: 35669 $")
00070
00071 #include "asterisk/lock.h"
00072 #include "asterisk/file.h"
00073 #include "asterisk/logger.h"
00074 #include "asterisk/channel.h"
00075 #include "asterisk/pbx.h"
00076 #include "asterisk/options.h"
00077 #include "asterisk/app.h"
00078 #include "asterisk/linkedlists.h"
00079 #include "asterisk/module.h"
00080 #include "asterisk/translate.h"
00081 #include "asterisk/say.h"
00082 #include "asterisk/features.h"
00083 #include "asterisk/musiconhold.h"
00084 #include "asterisk/cli.h"
00085 #include "asterisk/manager.h"
00086 #include "asterisk/config.h"
00087 #include "asterisk/monitor.h"
00088 #include "asterisk/utils.h"
00089 #include "asterisk/causes.h"
00090 #include "asterisk/astdb.h"
00091 #include "asterisk/devicestate.h"
00092
00093 #define QUEUE_STRATEGY_RINGALL 0
00094 #define QUEUE_STRATEGY_ROUNDROBIN 1
00095 #define QUEUE_STRATEGY_LEASTRECENT 2
00096 #define QUEUE_STRATEGY_FEWESTCALLS 3
00097 #define QUEUE_STRATEGY_RANDOM 4
00098 #define QUEUE_STRATEGY_RRMEMORY 5
00099
00100 static struct strategy {
00101 int strategy;
00102 char *name;
00103 } strategies[] = {
00104 { QUEUE_STRATEGY_RINGALL, "ringall" },
00105 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00106 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00107 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00108 { QUEUE_STRATEGY_RANDOM, "random" },
00109 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00110 };
00111
00112 #define DEFAULT_RETRY 5
00113 #define DEFAULT_TIMEOUT 15
00114 #define RECHECK 1
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 (*busies)++;
01522 return 0;
01523 } else if (status != tmp->oldstatus)
01524 update_dial_status(qe->parent, tmp->member, status);
01525
01526 tmp->chan->appl = "AppQueue";
01527 tmp->chan->data = "(Outgoing Line)";
01528 tmp->chan->whentohangup = 0;
01529 if (tmp->chan->cid.cid_num)
01530 free(tmp->chan->cid.cid_num);
01531 tmp->chan->cid.cid_num = NULL;
01532 if (tmp->chan->cid.cid_name)
01533 free(tmp->chan->cid.cid_name);
01534 tmp->chan->cid.cid_name = NULL;
01535 if (tmp->chan->cid.cid_ani)
01536 free(tmp->chan->cid.cid_ani);
01537 tmp->chan->cid.cid_ani = NULL;
01538 if (qe->chan->cid.cid_num)
01539 tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
01540 if (qe->chan->cid.cid_name)
01541 tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
01542 if (qe->chan->cid.cid_ani)
01543 tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
01544
01545
01546 ast_channel_inherit_variables(qe->chan, tmp->chan);
01547
01548
01549 tmp->chan->adsicpe = qe->chan->adsicpe;
01550
01551
01552 res = ast_call(tmp->chan, location, 0);
01553 if (res) {
01554
01555 if (option_debug)
01556 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01557 else if (option_verbose > 2)
01558 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01559 ast_hangup(tmp->chan);
01560 tmp->chan = NULL;
01561 tmp->stillgoing = 0;
01562 (*busies)++;
01563 return 0;
01564 } else {
01565 if (qe->parent->eventwhencalled) {
01566 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01567 "AgentCalled: %s\r\n"
01568 "ChannelCalling: %s\r\n"
01569 "CallerID: %s\r\n"
01570 "CallerIDName: %s\r\n"
01571 "Context: %s\r\n"
01572 "Extension: %s\r\n"
01573 "Priority: %d\r\n",
01574 tmp->interface, qe->chan->name,
01575 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01576 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01577 qe->chan->context, qe->chan->exten, qe->chan->priority);
01578 }
01579 if (option_verbose > 2)
01580 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01581 }
01582 return 1;
01583 }
01584
01585 static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
01586 {
01587 struct localuser *cur;
01588 struct localuser *best;
01589 int bestmetric=0;
01590
01591 do {
01592 best = NULL;
01593 cur = outgoing;
01594 while(cur) {
01595 if (cur->stillgoing &&
01596 !cur->chan &&
01597 (!best || (cur->metric < bestmetric))) {
01598 bestmetric = cur->metric;
01599 best = cur;
01600 }
01601 cur = cur->next;
01602 }
01603 if (best) {
01604 if (!qe->parent->strategy) {
01605
01606 cur = outgoing;
01607 while(cur) {
01608 if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
01609 if (option_debug)
01610 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01611 ring_entry(qe, cur, busies);
01612 }
01613 cur = cur->next;
01614 }
01615 } else {
01616
01617 if (option_debug)
01618 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01619 ring_entry(qe, best, busies);
01620 }
01621 }
01622 } while (best && !best->chan);
01623 if (!best) {
01624 if (option_debug)
01625 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01626 return 0;
01627 }
01628 return 1;
01629 }
01630
01631 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
01632 {
01633 struct localuser *cur;
01634 struct localuser *best;
01635 int bestmetric=0;
01636
01637 best = NULL;
01638 cur = outgoing;
01639 while(cur) {
01640 if (cur->stillgoing &&
01641 !cur->chan &&
01642 (!best || (cur->metric < bestmetric))) {
01643 bestmetric = cur->metric;
01644 best = cur;
01645 }
01646 cur = cur->next;
01647 }
01648 if (best) {
01649
01650 if (option_debug)
01651 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01652 qe->parent->rrpos = best->metric % 1000;
01653 } else {
01654
01655 if (qe->parent->wrapped) {
01656
01657 qe->parent->rrpos = 0;
01658 } else {
01659
01660 qe->parent->rrpos++;
01661 }
01662 }
01663 qe->parent->wrapped = 0;
01664 return 0;
01665 }
01666
01667 static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
01668 {
01669 int res;
01670
01671 ast_stopstream(chan);
01672 res = ast_streamfile(chan, filename, chan->language);
01673
01674 if (!res) {
01675
01676 res = ast_waitstream(chan, AST_DIGIT_ANY);
01677 if (res < 0 || !valid_exit(qe, res))
01678 res = 0;
01679
01680
01681 ast_stopstream(chan);
01682 } else {
01683 res = 0;
01684 }
01685
01686
01687
01688
01689
01690
01691 return res;
01692 }
01693
01694 static int say_periodic_announcement(struct queue_ent *qe)
01695 {
01696 int res = 0;
01697 time_t now;
01698
01699
01700 time(&now);
01701
01702
01703 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01704 return 0;
01705
01706
01707 ast_moh_stop(qe->chan);
01708
01709 if (option_verbose > 2)
01710 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01711
01712
01713 res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
01714
01715
01716 if (!res)
01717 ast_moh_start(qe->chan, qe->moh);
01718
01719
01720 qe->last_periodic_announce_time = now;
01721
01722 return res;
01723 }
01724
01725 static void record_abandoned(struct queue_ent *qe)
01726 {
01727 ast_mutex_lock(&qe->parent->lock);
01728 qe->parent->callsabandoned++;
01729 ast_mutex_unlock(&qe->parent->lock);
01730 }
01731
01732
01733 #define AST_MAX_WATCHERS 256
01734
01735 #define BUILD_WATCHERS do { \
01736 o = outgoing; \
01737 found = -1; \
01738 pos = 1; \
01739 numlines = 0; \
01740 watchers[0] = in; \
01741 while(o) { \
01742 \
01743 if (o->stillgoing) { \
01744 stillgoing = 1; \
01745 if (o->chan) { \
01746 watchers[pos++] = o->chan; \
01747 found = 1; \
01748 } \
01749 } \
01750 o = o->next; \
01751 numlines++; \
01752 } \
01753 } while(0)
01754
01755 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
01756 {
01757 char *queue = qe->parent->name;
01758 struct localuser *o;
01759 int found;
01760 int numlines;
01761 int status;
01762 int sentringing = 0;
01763 int numbusies = prebusies;
01764 int numnochan = 0;
01765 int stillgoing = 0;
01766 int orig = *to;
01767 struct ast_frame *f;
01768 struct localuser *peer = NULL;
01769 struct ast_channel *watchers[AST_MAX_WATCHERS];
01770 int pos;
01771 struct ast_channel *winner;
01772 struct ast_channel *in = qe->chan;
01773
01774 while(*to && !peer) {
01775 BUILD_WATCHERS;
01776 if ((found < 0) && stillgoing && !qe->parent->strategy) {
01777
01778
01779 ring_one(qe, outgoing, &numbusies);
01780 BUILD_WATCHERS;
01781 }
01782 if (found < 0) {
01783 if (numlines == (numbusies + numnochan)) {
01784 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
01785 } else {
01786 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
01787 }
01788 *to = 0;
01789 return NULL;
01790 }
01791 winner = ast_waitfor_n(watchers, pos, to);
01792 o = outgoing;
01793 while(o) {
01794 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
01795 if (!peer) {
01796 if (option_verbose > 2)
01797 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01798 peer = o;
01799 }
01800 } else if (o->chan && (o->chan == winner)) {
01801 if (!ast_strlen_zero(o->chan->call_forward)) {
01802 char tmpchan[256]="";
01803 char *stuff;
01804 char *tech;
01805 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
01806 if ((stuff = strchr(tmpchan, '/'))) {
01807 *stuff = '\0';
01808 stuff++;
01809 tech = tmpchan;
01810 } else {
01811 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
01812 stuff = tmpchan;
01813 tech = "Local";
01814 }
01815
01816 if (option_verbose > 2)
01817 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
01818
01819 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
01820 if (status != o->oldstatus)
01821 update_dial_status(qe->parent, o->member, status);
01822 if (!o->chan) {
01823 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
01824 o->stillgoing = 0;
01825 numnochan++;
01826 } else {
01827 if (o->chan->cid.cid_num)
01828 free(o->chan->cid.cid_num);
01829 o->chan->cid.cid_num = NULL;
01830 if (o->chan->cid.cid_name)
01831 free(o->chan->cid.cid_name);
01832 o->chan->cid.cid_name = NULL;
01833
01834 if (in->cid.cid_num) {
01835 o->chan->cid.cid_num = strdup(in->cid.cid_num);
01836 if (!o->chan->cid.cid_num)
01837 ast_log(LOG_WARNING, "Out of memory\n");
01838 }
01839 if (in->cid.cid_name) {
01840 o->chan->cid.cid_name = strdup(in->cid.cid_name);
01841 if (!o->chan->cid.cid_name)
01842 ast_log(LOG_WARNING, "Out of memory\n");
01843 }
01844 ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
01845 o->chan->cdrflags = in->cdrflags;
01846
01847 if (in->cid.cid_ani) {
01848 if (o->chan->cid.cid_ani)
01849 free(o->chan->cid.cid_ani);
01850 o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
01851 if (o->chan->cid.cid_ani)
01852 strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
01853 else
01854 ast_log(LOG_WARNING, "Out of memory\n");
01855 }
01856 if (o->chan->cid.cid_rdnis)
01857 free(o->chan->cid.cid_rdnis);
01858 if (!ast_strlen_zero(in->macroexten))
01859 o->chan->cid.cid_rdnis = strdup(in->macroexten);
01860 else
01861 o->chan->cid.cid_rdnis = strdup(in->exten);
01862 if (ast_call(o->chan, tmpchan, 0)) {
01863 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
01864 o->stillgoing = 0;
01865 ast_hangup(o->chan);
01866 o->chan = NULL;
01867 numnochan++;
01868 }
01869 }
01870
01871 ast_hangup(winner);
01872 continue;
01873 }
01874 f = ast_read(winner);
01875 if (f) {
01876 if (f->frametype == AST_FRAME_CONTROL) {
01877 switch(f->subclass) {
01878 case AST_CONTROL_ANSWER:
01879
01880 if (!peer) {
01881 if (option_verbose > 2)
01882 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01883 peer = o;
01884 }
01885 break;
01886 case AST_CONTROL_BUSY:
01887 if (option_verbose > 2)
01888 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
01889 o->stillgoing = 0;
01890 if (in->cdr)
01891 ast_cdr_busy(in->cdr);
01892 ast_hangup(o->chan);
01893 o->chan = NULL;
01894 if (qe->parent->strategy) {
01895 if (qe->parent->timeoutrestart)
01896 *to = orig;
01897 ring_one(qe, outgoing, &numbusies);
01898 }
01899 numbusies++;
01900 break;
01901 case AST_CONTROL_CONGESTION:
01902 if (option_verbose > 2)
01903 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
01904 o->stillgoing = 0;
01905 if (in->cdr)
01906 ast_cdr_busy(in->cdr);
01907 ast_hangup(o->chan);
01908 o->chan = NULL;
01909 if (qe->parent->strategy) {
01910 if (qe->parent->timeoutrestart)
01911 *to = orig;
01912 ring_one(qe, outgoing, &numbusies);
01913 }
01914 numbusies++;
01915 break;
01916 case AST_CONTROL_RINGING:
01917 if (option_verbose > 2)
01918 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
01919 if (!sentringing) {
01920 #if 0
01921 ast_indicate(in, AST_CONTROL_RINGING);
01922 #endif
01923 sentringing++;
01924 }
01925 break;
01926 case AST_CONTROL_OFFHOOK:
01927
01928 break;
01929 default:
01930 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
01931 }
01932 }
01933 ast_frfree(f);
01934 } else {
01935 o->stillgoing = 0;
01936 ast_hangup(o->chan);
01937 o->chan = NULL;
01938 if (qe->parent->strategy) {
01939 if (qe->parent->timeoutrestart)
01940 *to = orig;
01941 ring_one(qe, outgoing, &numbusies);
01942 }
01943 }
01944 }
01945 o = o->next;
01946 }
01947 if (winner == in) {
01948 f = ast_read(in);
01949 #if 0
01950 if (f && (f->frametype != AST_FRAME_VOICE))
01951 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
01952 else if (!f || (f->frametype != AST_FRAME_VOICE))
01953 printf("Hangup received on %s\n", in->name);
01954 #endif
01955 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
01956
01957 *to=-1;
01958 if (f)
01959 ast_frfree(f);
01960 return NULL;
01961 }
01962 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
01963 if (option_verbose > 3)
01964 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
01965 *to=0;
01966 ast_frfree(f);
01967 return NULL;
01968 }
01969 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
01970 if (option_verbose > 3)
01971 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
01972 *to=0;
01973 *digit=f->subclass;
01974 ast_frfree(f);
01975 return NULL;
01976 }
01977 ast_frfree(f);
01978 }
01979 if (!*to && (option_verbose > 2))
01980 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
01981 }
01982
01983 return peer;
01984
01985 }
01986
01987 static int is_our_turn(struct queue_ent *qe)
01988 {
01989 struct queue_ent *ch;
01990 int res;
01991
01992
01993 ch = qe->parent->head;
01994
01995 if (ch == qe) {
01996 if (option_debug)
01997 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
01998 res = 1;
01999 } else {
02000 if (option_debug)
02001 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02002 res = 0;
02003 }
02004 return res;
02005 }
02006
02007 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02008 {
02009 int res = 0;
02010
02011
02012 for (;;) {
02013 enum queue_member_status stat;
02014
02015 if (is_our_turn(qe))
02016 break;
02017
02018
02019 if (qe->expire && (time(NULL) > qe->expire)) {
02020 *reason = QUEUE_TIMEOUT;
02021 ast_queue_log(qe->parent->name, qe->chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe->pos);
02022 break;
02023 }
02024
02025 stat = get_member_status(qe->parent);
02026
02027
02028 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02029 *reason = QUEUE_LEAVEEMPTY;
02030 leave_queue(qe);
02031 break;
02032 }
02033
02034
02035 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02036 *reason = QUEUE_LEAVEUNAVAIL;
02037 leave_queue(qe);
02038 break;
02039 }
02040
02041
02042 if (qe->parent->announcefrequency && !ringing &&
02043 (res = say_position(qe)))
02044 break;
02045
02046
02047 if (qe->parent->periodicannouncefrequency && !ringing &&
02048 (res = say_periodic_announcement(qe)))
02049 break;
02050
02051
02052 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000)))
02053 break;
02054 }
02055 return res;
02056 }
02057
02058 static int update_queue(struct call_queue *q, struct member *member)
02059 {
02060 struct member *cur;
02061
02062
02063
02064 ast_mutex_lock(&q->lock);
02065 cur = q->members;
02066 while(cur) {
02067 if (member == cur) {
02068 time(&cur->lastcall);
02069 cur->calls++;
02070 break;
02071 }
02072 cur = cur->next;
02073 }
02074 q->callscompleted++;
02075 ast_mutex_unlock(&q->lock);
02076 return 0;
02077 }
02078
02079 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
02080 {
02081 switch (q->strategy) {
02082 case QUEUE_STRATEGY_RINGALL:
02083
02084 tmp->metric = mem->penalty * 1000000;
02085 break;
02086 case QUEUE_STRATEGY_ROUNDROBIN:
02087 if (!pos) {
02088 if (!q->wrapped) {
02089
02090 q->rrpos = 0;
02091 } else {
02092
02093 q->rrpos++;
02094 }
02095 q->wrapped = 0;
02096 }
02097
02098 case QUEUE_STRATEGY_RRMEMORY:
02099 if (pos < q->rrpos) {
02100 tmp->metric = 1000 + pos;
02101 } else {
02102 if (pos > q->rrpos)
02103
02104 q->wrapped = 1;
02105 tmp->metric = pos;
02106 }
02107 tmp->metric += mem->penalty * 1000000;
02108 break;
02109 case QUEUE_STRATEGY_RANDOM:
02110 tmp->metric = rand() % 1000;
02111 tmp->metric += mem->penalty * 1000000;
02112 break;
02113 case QUEUE_STRATEGY_FEWESTCALLS:
02114 tmp->metric = mem->calls;
02115 tmp->metric += mem->penalty * 1000000;
02116 break;
02117 case QUEUE_STRATEGY_LEASTRECENT:
02118 if (!mem->lastcall)
02119 tmp->metric = 0;
02120 else
02121 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02122 tmp->metric += mem->penalty * 1000000;
02123 break;
02124 default:
02125 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02126 break;
02127 }
02128 return 0;
02129 }
02130
02131 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
02132 {
02133 struct member *cur;
02134 struct localuser *outgoing=NULL, *tmp = NULL;
02135 int to;
02136 char restofit[AST_MAX_EXTENSION];
02137 char oldexten[AST_MAX_EXTENSION]="";
02138 char oldcontext[AST_MAX_CONTEXT]="";
02139 char queuename[256]="";
02140 char *newnum;
02141 char *monitorfilename;
02142 struct ast_channel *peer;
02143 struct ast_channel *which;
02144 struct localuser *lpeer;
02145 struct member *member;
02146 int res = 0, bridge = 0;
02147 int numbusies = 0;
02148 int x=0;
02149 char *announce = NULL;
02150 char digit = 0;
02151 time_t callstart;
02152 time_t now = time(NULL);
02153 struct ast_bridge_config bridge_config;
02154 char nondataquality = 1;
02155
02156 memset(&bridge_config, 0, sizeof(bridge_config));
02157 time(&now);
02158
02159 for (; options && *options; options++)
02160 switch (*options) {
02161 case 't':
02162 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02163 break;
02164 case 'T':
02165 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02166 break;
02167 case 'w':
02168 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02169 break;
02170 case 'W':
02171 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02172 break;
02173 case 'd':
02174 nondataquality = 0;
02175 break;
02176 case 'h':
02177 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02178 break;
02179 case 'H':
02180 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02181 break;
02182 case 'n':
02183 if ((now - qe->start >= qe->parent->timeout))
02184 *go_on = 1;
02185 break;
02186 }
02187
02188
02189 if (use_weight)
02190 ast_mutex_lock(&qlock);
02191 ast_mutex_lock(&qe->parent->lock);
02192 if (option_debug)
02193 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02194 qe->chan->name);
02195 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02196 cur = qe->parent->members;
02197 if (!ast_strlen_zero(qe->announce))
02198 announce = qe->announce;
02199 if (!ast_strlen_zero(announceoverride))
02200 announce = announceoverride;
02201
02202 while(cur) {
02203 tmp = malloc(sizeof(*tmp));
02204 if (!tmp) {
02205 ast_mutex_unlock(&qe->parent->lock);
02206 if (use_weight)
02207 ast_mutex_unlock(&qlock);
02208 ast_log(LOG_WARNING, "Out of memory\n");
02209 goto out;
02210 }
02211 memset(tmp, 0, sizeof(*tmp));
02212 tmp->stillgoing = -1;
02213 if (option_debug) {
02214 if (url)
02215 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
02216 else
02217 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
02218 }
02219
02220 tmp->member = cur;
02221 tmp->oldstatus = cur->status;
02222 tmp->lastcall = cur->lastcall;
02223 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02224
02225 if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
02226 newnum++;
02227 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
02228 snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
02229 if (option_debug)
02230 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
02231 }
02232
02233
02234 calc_metric(qe->parent, cur, x++, qe, tmp);
02235
02236
02237
02238 tmp->next = outgoing;
02239 outgoing = tmp;
02240
02241 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02242 break;
02243
02244 cur = cur->next;
02245 }
02246 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02247 to = (qe->expire - now) * 1000;
02248 else
02249 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02250 ring_one(qe, outgoing, &numbusies);
02251 ast_mutex_unlock(&qe->parent->lock);
02252 if (use_weight)
02253 ast_mutex_unlock(&qlock);
02254 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
02255 ast_mutex_lock(&qe->parent->lock);
02256 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02257 store_next(qe, outgoing);
02258 }
02259 ast_mutex_unlock(&qe->parent->lock);
02260 if (lpeer)
02261 peer = lpeer->chan;
02262 else
02263 peer = NULL;
02264 if (!peer) {
02265 if (to) {
02266
02267 res = -1;
02268 } else {
02269 res = digit;
02270 }
02271 if (option_debug)
02272 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02273 goto out;
02274 }
02275 if (peer) {
02276
02277
02278
02279 qe->handled++;
02280 if (!strcmp(qe->chan->type,"Zap"))
02281 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02282 if (!strcmp(peer->type,"Zap"))
02283 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02284
02285 recalc_holdtime(qe);
02286 member = lpeer->member;
02287 hangupcalls(outgoing, peer);
02288 outgoing = NULL;
02289 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02290 int res2;
02291 res2 = ast_autoservice_start(qe->chan);
02292 if (!res2) {
02293 if (qe->parent->memberdelay) {
02294 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02295 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02296 }
02297 if (!res2 && announce) {
02298 if (play_file(peer, announce))
02299 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
02300 }
02301 if (!res2 && qe->parent->reportholdtime) {
02302 if (!play_file(peer, qe->parent->sound_reporthold)) {
02303 int holdtime;
02304
02305 time(&now);
02306 holdtime = abs((now - qe->start) / 60);
02307 if (holdtime < 2) {
02308 play_file(peer, qe->parent->sound_lessthan);
02309 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02310 } else
02311 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02312 play_file(peer, qe->parent->sound_minutes);
02313 }
02314 }
02315 }
02316 res2 |= ast_autoservice_stop(qe->chan);
02317 if (peer->_softhangup) {
02318
02319 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
02320 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
02321 record_abandoned(qe);
02322 if (qe->parent->eventwhencalled) {
02323 manager_event(EVENT_FLAG_AGENT, "AgentDump",
02324 "Queue: %s\r\n"
02325 "Uniqueid: %s\r\n"
02326 "Channel: %s\r\n"
02327 "Member: %s\r\n",
02328 queuename, qe->chan->uniqueid, peer->name, member->interface);
02329 }
02330 ast_hangup(peer);
02331 goto out;
02332 } else if (res2) {
02333
02334 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02335 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02336 record_abandoned(qe);
02337 ast_hangup(peer);
02338 return -1;
02339 }
02340 }
02341
02342 ast_moh_stop(qe->chan);
02343
02344 if (qe->chan->cdr)
02345 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02346
02347 res = ast_channel_make_compatible(qe->chan, peer);
02348 if (res < 0) {
02349 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
02350 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02351 record_abandoned(qe);
02352 ast_hangup(peer);
02353 return -1;
02354 }
02355
02356 if (qe->parent->monfmt && *qe->parent->monfmt) {
02357 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02358 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02359 which = qe->chan;
02360 else
02361 which = peer;
02362 if (monitorfilename)
02363 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02364 else if (qe->chan->cdr)
02365 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02366 else {
02367
02368 char tmpid[256];
02369 snprintf(tmpid, sizeof(tmpid), "chan-%x", rand());
02370 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02371 }
02372 if (qe->parent->monjoin)
02373 ast_monitor_setjoinfiles(which, 1);
02374 }
02375
02376 leave_queue(qe);
02377 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02378 if (option_debug)
02379 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02380 ast_channel_sendurl(peer, url);
02381 }
02382 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
02383 if (qe->parent->eventwhencalled)
02384 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02385 "Queue: %s\r\n"
02386 "Uniqueid: %s\r\n"
02387 "Channel: %s\r\n"
02388 "Member: %s\r\n"
02389 "Holdtime: %ld\r\n",
02390 queuename, qe->chan->uniqueid, peer->name, member->interface,
02391 (long)time(NULL) - qe->start);
02392 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02393 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02394 time(&callstart);
02395
02396 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02397
02398 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02399 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
02400 } else if (qe->chan->_softhangup) {
02401 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
02402 (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02403 if (qe->parent->eventwhencalled)
02404 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02405 "Queue: %s\r\n"
02406 "Uniqueid: %s\r\n"
02407 "Channel: %s\r\n"
02408 "Member: %s\r\n"
02409 "HoldTime: %ld\r\n"
02410 "TalkTime: %ld\r\n"
02411 "Reason: caller\r\n",
02412 queuename, qe->chan->uniqueid, peer->name, member->interface,
02413 (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02414 } else {
02415 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02416 if (qe->parent->eventwhencalled)
02417 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02418 "Queue: %s\r\n"
02419 "Uniqueid: %s\r\n"
02420 "Channel: %s\r\n"
02421 "HoldTime: %ld\r\n"
02422 "TalkTime: %ld\r\n"
02423 "Reason: agent\r\n",
02424 queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
02425 (long)(time(NULL) - callstart));
02426 }
02427
02428 if (bridge != AST_PBX_NO_HANGUP_PEER)
02429 ast_hangup(peer);
02430 update_queue(qe->parent, member);
02431 res = bridge ? bridge : 1;
02432 }
02433 out:
02434 hangupcalls(outgoing, NULL);
02435 return res;
02436 }
02437
02438 static int wait_a_bit(struct queue_ent *qe)
02439 {
02440
02441 int retrywait = qe->parent->retry * 1000;
02442
02443 return ast_waitfordigit(qe->chan, retrywait);
02444 }
02445
02446 static struct member *interface_exists(struct call_queue *q, char *interface)
02447 {
02448 struct member *mem;
02449
02450 if (q)
02451 for (mem = q->members; mem; mem = mem->next)
02452 if (!strcasecmp(interface, mem->interface))
02453 return mem;
02454
02455 return NULL;
02456 }
02457
02458
02459
02460
02461
02462
02463
02464 static void dump_queue_members(struct call_queue *pm_queue)
02465 {
02466 struct member *cur_member;
02467 char value[PM_MAX_LEN];
02468 int value_len = 0;
02469 int res;
02470
02471 memset(value, 0, sizeof(value));
02472
02473 if (!pm_queue)
02474 return;
02475
02476 for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
02477 if (!cur_member->dynamic)
02478 continue;
02479
02480 res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
02481 cur_member->interface, cur_member->penalty, cur_member->paused,
02482 cur_member->next ? "|" : "");
02483 if (res != strlen(value + value_len)) {
02484 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
02485 break;
02486 }
02487 value_len += res;
02488 }
02489
02490 if (value_len && !cur_member) {
02491 if (ast_db_put(pm_family, pm_queue->name, value))
02492 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
02493 } else
02494
02495 ast_db_del(pm_family, pm_queue->name);
02496 }
02497
02498 static int remove_from_queue(char *queuename, char *interface)
02499 {
02500 struct call_queue *q;
02501 struct member *last_member, *look;
02502 int res = RES_NOSUCHQUEUE;
02503
02504 ast_mutex_lock(&qlock);
02505 for (q = queues ; q ; q = q->next) {
02506 ast_mutex_lock(&q->lock);
02507 if (!strcmp(q->name, queuename)) {
02508 if ((last_member = interface_exists(q, interface))) {
02509 if ((look = q->members) == last_member) {
02510 q->members = last_member->next;
02511 } else {
02512 while (look != NULL) {
02513 if (look->next == last_member) {
02514 look->next = last_member->next;
02515 break;
02516 } else {
02517 look = look->next;
02518 }
02519 }
02520 }
02521 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
02522 "Queue: %s\r\n"
02523 "Location: %s\r\n",
02524 q->name, last_member->interface);
02525 free(last_member);
02526
02527 if (queue_persistent_members)
02528 dump_queue_members(q);
02529
02530 res = RES_OKAY;
02531 } else {
02532 res = RES_EXISTS;
02533 }
02534 ast_mutex_unlock(&q->lock);
02535 break;
02536 }
02537 ast_mutex_unlock(&q->lock);
02538 }
02539 if (res == RES_OKAY)
02540 remove_from_interfaces(interface);
02541 ast_mutex_unlock(&qlock);
02542 return res;
02543 }
02544
02545 static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
02546 {
02547 struct call_queue *q;
02548 struct member *new_member;
02549 int res = RES_NOSUCHQUEUE;
02550
02551
02552
02553 q = load_realtime_queue(queuename);
02554
02555 ast_mutex_lock(&qlock);
02556
02557 if (q) {
02558 ast_mutex_lock(&q->lock);
02559 if (interface_exists(q, interface) == NULL) {
02560
02561 add_to_interfaces(interface);
02562
02563 new_member = create_queue_member(interface, penalty, paused);
02564
02565 if (new_member != NULL) {
02566 new_member->dynamic = 1;
02567 new_member->next = q->members;
02568 q->members = new_member;
02569 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
02570 "Queue: %s\r\n"
02571 "Location: %s\r\n"
02572 "Membership: %s\r\n"
02573 "Penalty: %d\r\n"
02574 "CallsTaken: %d\r\n"
02575 "LastCall: %d\r\n"
02576 "Status: %d\r\n"
02577 "Paused: %d\r\n",
02578 q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
02579 new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused);
02580
02581 if (dump)
02582 dump_queue_members(q);
02583
02584 res = RES_OKAY;
02585 } else {
02586 res = RES_OUTOFMEMORY;
02587 }
02588 } else {
02589 res = RES_EXISTS;
02590 }
02591 ast_mutex_unlock(&q->lock);
02592 }
02593 ast_mutex_unlock(&qlock);
02594 return res;
02595 }
02596
02597 static int set_member_paused(char *queuename, char *interface, int paused)
02598 {
02599 int found = 0;
02600 struct call_queue *q;
02601 struct member *mem;
02602
02603
02604
02605 if (ast_strlen_zero(queuename))
02606 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
02607
02608 ast_mutex_lock(&qlock);
02609 for (q = queues ; q ; q = q->next) {
02610 ast_mutex_lock(&q->lock);
02611 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
02612 if ((mem = interface_exists(q, interface))) {
02613 found++;
02614 if (mem->paused == paused)
02615 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
02616 mem->paused = paused;
02617
02618 if (queue_persistent_members)
02619 dump_queue_members(q);
02620
02621 ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
02622
02623 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
02624 "Queue: %s\r\n"
02625 "Location: %s\r\n"
02626 "Paused: %d\r\n",
02627 q->name, mem->interface, paused);
02628 }
02629 }
02630 ast_mutex_unlock(&q->lock);
02631 }
02632 ast_mutex_unlock(&qlock);
02633
02634 if (found)
02635 return RESULT_SUCCESS;
02636 else
02637 return RESULT_FAILURE;
02638 }
02639
02640
02641 static void reload_queue_members(void)
02642 {
02643 char *cur_ptr;
02644 char *queue_name;
02645 char *member;
02646 char *interface;
02647 char *penalty_tok;
02648 int penalty = 0;
02649 char *paused_tok;
02650 int paused = 0;
02651 struct ast_db_entry *db_tree;
02652 struct ast_db_entry *entry;
02653 struct call_queue *cur_queue;
02654 char queue_data[PM_MAX_LEN];
02655
02656 ast_mutex_lock(&qlock);
02657
02658
02659 db_tree = ast_db_gettree(pm_family, NULL);
02660 for (entry = db_tree; entry; entry = entry->next) {
02661
02662 queue_name = entry->key + strlen(pm_family) + 2;
02663
02664 cur_queue = queues;
02665 while (cur_queue) {
02666 ast_mutex_lock(&cur_queue->lock);
02667 if (!strcmp(queue_name, cur_queue->name))
02668 break;
02669 ast_mutex_unlock(&cur_queue->lock);
02670 cur_queue = cur_queue->next;
02671 }
02672
02673 if (!cur_queue) {
02674
02675
02676 ast_db_del(pm_family, queue_name);
02677 continue;
02678 } else
02679 ast_mutex_unlock(&cur_queue->lock);
02680
02681 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
02682 continue;
02683
02684 cur_ptr = queue_data;
02685 while ((member = strsep(&cur_ptr, "|"))) {
02686 if (ast_strlen_zero(member))
02687 continue;
02688
02689 interface = strsep(&member, ";");
02690 penalty_tok = strsep(&member, ";");
02691 paused_tok = strsep(&member, ";");
02692
02693 if (!penalty_tok) {
02694 ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
02695 break;
02696 }
02697 penalty = strtol(penalty_tok, NULL, 10);
02698 if (errno == ERANGE) {
02699 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
02700 break;
02701 }
02702
02703 if (!paused_tok) {
02704 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
02705 break;
02706 }
02707 paused = strtol(paused_tok, NULL, 10);
02708 if ((errno == ERANGE) || paused < 0 || paused > 1) {
02709 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
02710 break;
02711 }
02712
02713 if (option_debug)
02714 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused);
02715
02716 if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
02717 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
02718 break;
02719 }
02720 }
02721 }
02722
02723 ast_mutex_unlock(&qlock);
02724 if (db_tree) {
02725 ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
02726 ast_db_freetree(db_tree);
02727 }
02728 }
02729
02730 static int pqm_exec(struct ast_channel *chan, void *data)
02731 {
02732 struct localuser *u;
02733 char *parse;
02734 int priority_jump = 0;
02735 AST_DECLARE_APP_ARGS(args,
02736 AST_APP_ARG(queuename);
02737 AST_APP_ARG(interface);
02738 AST_APP_ARG(options);
02739 );
02740
02741 if (ast_strlen_zero(data)) {
02742 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02743 return -1;
02744 }
02745
02746 LOCAL_USER_ADD(u);
02747
02748 if (!(parse = ast_strdupa(data))) {
02749 ast_log(LOG_WARNING, "Memory Error!\n");
02750 LOCAL_USER_REMOVE(u);
02751 return -1;
02752 }
02753
02754 AST_STANDARD_APP_ARGS(args, parse);
02755
02756 if (args.options) {
02757 if (strchr(args.options, 'j'))
02758 priority_jump = 1;
02759 }
02760
02761 if (ast_strlen_zero(args.interface)) {
02762 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02763 LOCAL_USER_REMOVE(u);
02764 return -1;
02765 }
02766
02767 if (set_member_paused(args.queuename, args.interface, 1)) {
02768 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
02769 if (priority_jump || option_priority_jumping) {
02770 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02771 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02772 LOCAL_USER_REMOVE(u);
02773 return 0;
02774 }
02775 }
02776 LOCAL_USER_REMOVE(u);
02777 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02778 return -1;
02779 }
02780
02781 LOCAL_USER_REMOVE(u);
02782 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
02783 return 0;
02784 }
02785
02786 static int upqm_exec(struct ast_channel *chan, void *data)
02787 {
02788 struct localuser *u;
02789 char *parse;
02790 int priority_jump = 0;
02791 AST_DECLARE_APP_ARGS(args,
02792 AST_APP_ARG(queuename);
02793 AST_APP_ARG(interface);
02794 AST_APP_ARG(options);
02795 );
02796
02797 if (ast_strlen_zero(data)) {
02798 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02799 return -1;
02800 }
02801
02802 LOCAL_USER_ADD(u);
02803
02804 if (!(parse = ast_strdupa(data))) {
02805 ast_log(LOG_WARNING, "Memory Error!\n");
02806 LOCAL_USER_REMOVE(u);
02807 return -1;
02808 }
02809
02810 AST_STANDARD_APP_ARGS(args, parse);
02811
02812 if (args.options) {
02813 if (strchr(args.options, 'j'))
02814 priority_jump = 1;
02815 }
02816
02817 if (ast_strlen_zero(args.interface)) {
02818 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02819 LOCAL_USER_REMOVE(u);
02820 return -1;
02821 }
02822
02823 if (set_member_paused(args.queuename, args.interface, 0)) {
02824 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
02825 if (priority_jump || option_priority_jumping) {
02826 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02827 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02828 LOCAL_USER_REMOVE(u);
02829 return 0;
02830 }
02831 }
02832 LOCAL_USER_REMOVE(u);
02833 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02834 return -1;
02835 }
02836
02837 LOCAL_USER_REMOVE(u);
02838 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
02839 return 0;
02840 }
02841
02842 static int rqm_exec(struct ast_channel *chan, void *data)
02843 {
02844 int res=-1;
02845 struct localuser *u;
02846 char *parse, *temppos = NULL;
02847 int priority_jump = 0;
02848 AST_DECLARE_APP_ARGS(args,
02849 AST_APP_ARG(queuename);
02850 AST_APP_ARG(interface);
02851 AST_APP_ARG(options);
02852 );
02853
02854
02855 if (ast_strlen_zero(data)) {
02856 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
02857 return -1;
02858 }
02859
02860 LOCAL_USER_ADD(u);
02861
02862 if (!(parse = ast_strdupa(data))) {
02863 ast_log(LOG_WARNING, "Memory Error!\n");
02864 LOCAL_USER_REMOVE(u);
02865 return -1;
02866 }
02867
02868 AST_STANDARD_APP_ARGS(args, parse);
02869
02870 if (ast_strlen_zero(args.interface)) {
02871 args.interface = ast_strdupa(chan->name);
02872 temppos = strrchr(args.interface, '-');
02873 if (temppos)
02874 *temppos = '\0';
02875 }
02876
02877 if (args.options) {
02878 if (strchr(args.options, 'j'))
02879 priority_jump = 1;
02880 }
02881
02882 switch (remove_from_queue(args.queuename, args.interface)) {
02883 case RES_OKAY:
02884 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
02885 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
02886 res = 0;
02887 break;
02888 case RES_EXISTS:
02889 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
02890 if (priority_jump || option_priority_jumping)
02891 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02892 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
02893 res = 0;
02894 break;
02895 case RES_NOSUCHQUEUE:
02896 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
02897 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
02898 res = 0;
02899 break;
02900 case RES_OUTOFMEMORY:
02901 ast_log(LOG_ERROR, "Out of memory\n");
02902 break;
02903 }
02904
02905 LOCAL_USER_REMOVE(u);
02906 return res;
02907 }
02908
02909 static int aqm_exec(struct ast_channel *chan, void *data)
02910 {
02911 int res=-1;
02912 struct localuser *u;
02913 char *parse, *temppos = NULL;
02914 int priority_jump = 0;
02915 AST_DECLARE_APP_ARGS(args,
02916 AST_APP_ARG(queuename);
02917 AST_APP_ARG(interface);
02918 AST_APP_ARG(penalty);
02919 AST_APP_ARG(options);
02920 );
02921 int penalty = 0;
02922
02923 if (ast_strlen_zero(data)) {
02924 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n");
02925 return -1;
02926 }
02927
02928 LOCAL_USER_ADD(u);
02929
02930 if (!(parse = ast_strdupa(data))) {
02931 ast_log(LOG_WARNING, "Memory Error!\n");
02932 LOCAL_USER_REMOVE(u);
02933 return -1;
02934 }
02935
02936 AST_STANDARD_APP_ARGS(args, parse);
02937
02938 if (ast_strlen_zero(args.interface)) {
02939 args.interface = ast_strdupa(chan->name);
02940 temppos = strrchr(args.interface, '-');
02941 if (temppos)
02942 *temppos = '\0';
02943 }
02944
02945 if (!ast_strlen_zero(args.penalty)) {
02946 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
02947 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
02948 penalty = 0;
02949 }
02950 }
02951
02952 if (args.options) {
02953 if (strchr(args.options, 'j'))
02954 priority_jump = 1;
02955 }
02956
02957
02958 switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) {
02959 case RES_OKAY:
02960 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
02961 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
02962 res = 0;
02963 break;
02964 case RES_EXISTS:
02965 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
02966 if (priority_jump || option_priority_jumping)
02967 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02968 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
02969 res = 0;
02970 break;
02971 case RES_NOSUCHQUEUE:
02972 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
02973 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
02974 res = 0;
02975 break;
02976 case RES_OUTOFMEMORY:
02977 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
02978 break;
02979 }
02980
02981 LOCAL_USER_REMOVE(u);
02982 return res;
02983 }
02984
02985 static int queue_exec(struct ast_channel *chan, void *data)
02986 {
02987 int res=-1;
02988 int ringing=0;
02989 struct localuser *u;
02990 char *queuename;
02991 char info[512];
02992 char *info_ptr = info;
02993 char *options = NULL;
02994 char *url = NULL;
02995 char *announceoverride = NULL;
02996 char *user_priority;
02997 int prio;
02998 char *queuetimeoutstr = NULL;
02999 enum queue_result reason = QUEUE_UNKNOWN;
03000
03001
03002 int go_on = 0;
03003
03004
03005 struct queue_ent qe;
03006
03007 if (ast_strlen_zero(data)) {
03008 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
03009 return -1;
03010 }
03011
03012 LOCAL_USER_ADD(u);
03013
03014
03015 memset(&qe, 0, sizeof(qe));
03016 qe.start = time(NULL);
03017
03018
03019 ast_copy_string(info, (char *) data, sizeof(info));
03020 queuename = strsep(&info_ptr, "|");
03021 options = strsep(&info_ptr, "|");
03022 url = strsep(&info_ptr, "|");
03023 announceoverride = strsep(&info_ptr, "|");
03024 queuetimeoutstr = info_ptr;
03025
03026
03027 if (!ast_strlen_zero(queuetimeoutstr))
03028 qe.expire = qe.start + atoi(queuetimeoutstr);
03029 else
03030 qe.expire = 0;
03031
03032
03033 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03034 if (user_priority) {
03035 if (sscanf(user_priority, "%d", &prio) == 1) {
03036 if (option_debug)
03037 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03038 chan->name, prio);
03039 } else {
03040 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03041 user_priority, chan->name);
03042 prio = 0;
03043 }
03044 } else {
03045 if (option_debug > 2)
03046 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03047 prio = 0;
03048 }
03049
03050 if (options && (strchr(options, 'r')))
03051 ringing = 1;
03052
03053 if (option_debug)
03054 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03055 queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
03056
03057 qe.chan = chan;
03058 qe.prio = (int)prio;
03059 qe.last_pos_said = 0;
03060 qe.last_pos = 0;
03061 qe.last_periodic_announce_time = time(NULL);
03062 if (!join_queue(queuename, &qe, &reason)) {
03063 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "",
03064 chan->cid.cid_num ? chan->cid.cid_num : "");
03065 check_turns:
03066 if (ringing) {
03067 ast_indicate(chan, AST_CONTROL_RINGING);
03068 } else {
03069 ast_moh_start(chan, qe.moh);
03070 }
03071 for (;;) {
03072
03073
03074 res = wait_our_turn(&qe, ringing, &reason);
03075
03076 if (res < 0) {
03077
03078 record_abandoned(&qe);
03079 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03080 if (option_verbose > 2) {
03081 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename);
03082 }
03083 res = -1;
03084 break;
03085 }
03086 if (!res)
03087 break;
03088 if (valid_exit(&qe, res)) {
03089 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03090 break;
03091 }
03092 }
03093 if (!res) {
03094 int makeannouncement = 0;
03095 for (;;) {
03096
03097
03098
03099
03100
03101 enum queue_member_status stat;
03102
03103
03104 if (qe.expire && (time(NULL) > qe.expire)) {
03105 record_abandoned(&qe);
03106 reason = QUEUE_TIMEOUT;
03107 res = 0;
03108 ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03109 break;
03110 }
03111
03112 if (makeannouncement) {
03113
03114 if (qe.parent->announcefrequency && !ringing &&
03115 (res = say_position(&qe))) {
03116 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03117 break;
03118 }
03119
03120 }
03121 makeannouncement = 1;
03122
03123
03124 if (qe.parent->periodicannouncefrequency && !ringing &&
03125 (res = say_periodic_announcement(&qe))) {
03126 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
03127 break;
03128 }
03129
03130
03131 res = try_calling(&qe, options, announceoverride, url, &go_on);
03132 if (res) {
03133 if (res < 0) {
03134 if (!qe.handled) {
03135 record_abandoned(&qe);
03136 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03137 }
03138 } else if (valid_exit(&qe, res)) {
03139 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03140 }
03141 break;
03142 }
03143
03144 stat = get_member_status(qe.parent);
03145
03146
03147 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03148 record_abandoned(&qe);
03149 reason = QUEUE_LEAVEEMPTY;
03150 res = 0;
03151 break;
03152 }
03153
03154
03155 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03156 record_abandoned(&qe);
03157 reason = QUEUE_LEAVEUNAVAIL;
03158 res = 0;
03159 break;
03160 }
03161
03162
03163 if (qe.expire && (time(NULL) > qe.expire)) {
03164 record_abandoned(&qe);
03165 reason = QUEUE_TIMEOUT;
03166 res = 0;
03167 ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03168 break;
03169 }
03170
03171
03172 res = wait_a_bit(&qe);
03173 if (res < 0) {
03174 record_abandoned(&qe);
03175 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03176 if (option_verbose > 2) {
03177 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename);
03178 }
03179 res = -1;
03180 break;
03181 }
03182 if (res && valid_exit(&qe, res)) {
03183 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03184 break;
03185 }
03186
03187 if (go_on) {
03188 if (option_verbose > 2)
03189 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03190 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03191 record_abandoned(&qe);
03192 reason = QUEUE_TIMEOUT;
03193 res = 0;
03194 break;
03195 }
03196
03197
03198
03199
03200 if (!is_our_turn(&qe)) {
03201 if (option_debug)
03202 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03203 qe.chan->name);
03204 goto check_turns;
03205 }
03206 }
03207 }
03208
03209 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03210 res = 0;
03211 if (ringing) {
03212 ast_indicate(chan, -1);
03213 } else {
03214 ast_moh_stop(chan);
03215 }
03216 ast_stopstream(chan);
03217 }
03218 leave_queue(&qe);
03219 if (reason != QUEUE_UNKNOWN)
03220 set_queue_result(chan, reason);
03221 } else {
03222 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
03223 set_queue_result(chan, reason);
03224 res = 0;
03225 }
03226 LOCAL_USER_REMOVE(u);
03227 return res;
03228 }
03229
03230 static char *queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03231 {
03232 int count = 0;
03233 struct call_queue *q;
03234 struct localuser *u;
03235 struct member *m;
03236
03237 LOCAL_USER_ACF_ADD(u);
03238
03239 ast_copy_string(buf, "0", len);
03240
03241 if (ast_strlen_zero(data)) {
03242 ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
03243 LOCAL_USER_REMOVE(u);
03244 return buf;
03245 }
03246
03247 ast_mutex_lock(&qlock);
03248
03249
03250 for (q = queues; q; q = q->next) {
03251 if (!strcasecmp(q->name, data)) {
03252 ast_mutex_lock(&q->lock);
03253 break;
03254 }
03255 }
03256
03257 ast_mutex_unlock(&qlock);
03258
03259 if (q) {
03260 for (m = q->members; m; m = m->next) {
03261
03262 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03263 count++;
03264 }
03265 }
03266 ast_mutex_unlock(&q->lock);
03267 }
03268
03269 snprintf(buf, len, "%d", count);
03270 LOCAL_USER_REMOVE(u);
03271 return buf;
03272 }
03273
03274 static struct ast_custom_function queueagentcount_function = {
03275 .name = "QUEUEAGENTCOUNT",
03276 .synopsis = "Count number of agents answering a queue",
03277 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03278 .read = queue_function_qac,
03279 };
03280
03281 static void reload_queues(void)
03282 {
03283 struct call_queue *q, *ql, *qn;
03284 struct ast_config *cfg;
03285 char *cat, *tmp;
03286 struct ast_variable *var;
03287 struct member *prev, *cur, *newm;
03288 int new;
03289 char *general_val = NULL;
03290 char interface[80];
03291 int penalty;
03292
03293 cfg = ast_config_load("queues.conf");
03294 if (!cfg) {
03295 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03296 return;
03297 }
03298 memset(interface, 0, sizeof(interface));
03299 ast_mutex_lock(&qlock);
03300 use_weight=0;
03301
03302 q = queues;
03303 while(q) {
03304 q->dead = 1;
03305 q = q->next;
03306 }
03307
03308 cat = ast_category_browse(cfg, NULL);
03309 while(cat) {
03310 if (!strcasecmp(cat, "general")) {
03311
03312 queue_persistent_members = 0;
03313 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03314 queue_persistent_members = ast_true(general_val);
03315 } else {
03316
03317 q = queues;
03318 while(q) {
03319 if (!strcmp(q->name, cat))
03320 break;
03321 q = q->next;
03322 }
03323 if (!q) {
03324
03325 q = alloc_queue(cat);
03326 new = 1;
03327 } else
03328 new = 0;
03329 if (q) {
03330 if (!new)
03331 ast_mutex_lock(&q->lock);
03332
03333 init_queue(q);
03334 clear_queue(q);
03335 for (cur = q->members; cur; cur = cur->next) {
03336 if (!cur->dynamic) {
03337 cur->delme = 1;
03338 }
03339 }
03340 var = ast_variable_browse(cfg, cat);
03341 while (var) {
03342 if (!strcasecmp(var->name, "member")) {
03343
03344 ast_copy_string(interface, var->value, sizeof(interface));
03345 if ((tmp = strchr(interface, ','))) {
03346 *tmp = '\0';
03347 tmp++;
03348 penalty = atoi(tmp);
03349 if (penalty < 0) {
03350 penalty = 0;
03351 }
03352 } else
03353 penalty = 0;
03354
03355
03356 for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
03357 if (!strcmp(cur->interface, interface)) {
03358 break;
03359 }
03360 }
03361
03362 newm = create_queue_member(interface, penalty, cur ? cur->paused : 0);
03363
03364 if (cur) {
03365
03366 newm->next = cur->next;
03367 if (prev) {
03368 prev->next = newm;
03369 } else {
03370 q->members = newm;
03371 }
03372 free(cur);
03373 } else {
03374
03375 add_to_interfaces(interface);
03376 newm->next = q->members;
03377 q->members = newm;
03378 }
03379 } else {
03380 queue_set_param(q, var->name, var->value, var->lineno, 1);
03381 }
03382 var = var->next;
03383 }
03384
03385
03386 for (prev = NULL, newm = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
03387 if (newm) {
03388 free(newm);
03389 newm = NULL;
03390 }
03391
03392 if (cur->delme) {
03393 if (prev) {
03394 prev->next = cur->next;
03395 newm = cur;
03396 } else {
03397 q->members = cur->next;
03398 newm = cur;
03399 }
03400 remove_from_interfaces(cur->interface);
03401 }
03402 }
03403 if (!new)
03404 ast_mutex_unlock(&q->lock);
03405 if (new) {
03406 q->next = queues;
03407 queues = q;
03408 }
03409 }
03410 }
03411 cat = ast_category_browse(cfg, cat);
03412 }
03413 ast_config_destroy(cfg);
03414 q = queues;
03415 ql = NULL;
03416 while(q) {
03417 qn = q->next;
03418 if (q->dead) {
03419 if (ql)
03420 ql->next = q->next;
03421 else
03422 queues = q->next;
03423 if (!q->count) {
03424 destroy_queue(q);
03425 } else
03426 ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
03427 } else {
03428 ast_mutex_lock(&q->lock);
03429 for (cur = q->members; cur; cur = cur->next)
03430 cur->status = ast_device_state(cur->interface);
03431 ql = q;
03432 ast_mutex_unlock(&q->lock);
03433 }
03434 q = qn;
03435 }
03436 ast_mutex_unlock(&qlock);
03437 }
03438
03439 static int __queues_show(int manager, int fd, int argc, char **argv, int queue_show)
03440 {
03441 struct call_queue *q;
03442 struct queue_ent *qe;
03443 struct member *mem;
03444 int pos;
03445 time_t now;
03446 char max_buf[80];
03447 char *max;
03448 size_t max_left;
03449 float sl = 0;
03450 char *term = manager ? "\r\n" : "\n";
03451
03452 time(&now);
03453 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
03454 return RESULT_SHOWUSAGE;
03455
03456
03457 if (queue_show)
03458 load_realtime_queue(argv[2]);
03459
03460 ast_mutex_lock(&qlock);
03461
03462 q = queues;
03463 if (!q) {
03464 ast_mutex_unlock(&qlock);
03465 if (queue_show)
03466 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03467 else
03468 ast_cli(fd, "No queues.%s", term);
03469 return RESULT_SUCCESS;
03470 }
03471 while (q) {
03472 ast_mutex_lock(&q->lock);
03473 if (queue_show) {
03474 if (strcasecmp(q->name, argv[2]) != 0) {
03475 ast_mutex_unlock(&q->lock);
03476 q = q->next;
03477 if (!q) {
03478 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03479 break;
03480 }
03481 continue;
03482 }
03483 }
03484 max_buf[0] = '\0';
03485 max = max_buf;
03486 max_left = sizeof(max_buf);
03487 if (q->maxlen)
03488 ast_build_string(&max, &max_left, "%d", q->maxlen);
03489 else
03490 ast_build_string(&max, &max_left, "unlimited");
03491 sl = 0;
03492 if(q->callscompleted > 0)
03493 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03494 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
03495 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03496 if (q->members) {
03497 ast_cli(fd, " Members: %s", term);
03498 for (mem = q->members; mem; mem = mem->next) {
03499 max_buf[0] = '\0';
03500 max = max_buf;
03501 max_left = sizeof(max_buf);
03502 if (mem->penalty)
03503 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
03504 if (mem->dynamic)
03505 ast_build_string(&max, &max_left, " (dynamic)");
03506 if (mem->paused)
03507 ast_build_string(&max, &max_left, " (paused)");
03508 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
03509 if (mem->calls) {
03510 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
03511 mem->calls, (long)(time(NULL) - mem->lastcall));
03512 } else
03513 ast_build_string(&max, &max_left, " has taken no calls yet");
03514 ast_cli(fd, " %s%s%s", mem->interface, max_buf, term);
03515 }
03516 } else
03517 ast_cli(fd, " No Members%s", term);
03518 if (q->head) {
03519 pos = 1;
03520 ast_cli(fd, " Callers: %s", term);
03521 for (qe = q->head; qe; qe = qe->next)
03522 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, qe->chan->name,
03523 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio, term);
03524 } else
03525 ast_cli(fd, " No Callers%s", term);
03526 ast_cli(fd, "%s", term);
03527 ast_mutex_unlock(&q->lock);
03528 q = q->next;
03529 if (queue_show)
03530 break;
03531 }
03532 ast_mutex_unlock(&qlock);
03533 return RESULT_SUCCESS;
03534 }
03535
03536 static int queues_show(int fd, int argc, char **argv)
03537 {
03538 return __queues_show(0, fd, argc, argv, 0);
03539 }
03540
03541 static int queue_show(int fd, int argc, char **argv)
03542 {
03543 return __queues_show(0, fd, argc, argv, 1);
03544 }
03545
03546 static char *complete_queue(char *line, char *word, int pos, int state)
03547 {
03548 struct call_queue *q;
03549 int which=0;
03550
03551 ast_mutex_lock(&qlock);
03552 for (q = queues; q; q = q->next) {
03553 if (!strncasecmp(word, q->name, strlen(word))) {
03554 if (++which > state)
03555 break;
03556 }
03557 }
03558 ast_mutex_unlock(&qlock);
03559 return q ? strdup(q->name) : NULL;
03560 }
03561
03562
03563
03564
03565 static int manager_queues_show( struct mansession *s, struct message *m )
03566 {
03567 char *a[] = { "show", "queues" };
03568 __queues_show(1, s->fd, 2, a, 0);
03569 ast_cli(s->fd, "\r\n\r\n");
03570
03571 return RESULT_SUCCESS;
03572 }
03573
03574
03575 static int manager_queues_status( struct mansession *s, struct message *m )
03576 {
03577 time_t now;
03578 int pos;
03579 char *id = astman_get_header(m,"ActionID");
03580 char *queuefilter = astman_get_header(m,"Queue");
03581 char *memberfilter = astman_get_header(m,"Member");
03582 char idText[256] = "";
03583 struct call_queue *q;
03584 struct queue_ent *qe;
03585 float sl = 0;
03586 struct member *mem;
03587
03588 astman_send_ack(s, m, "Queue status will follow");
03589 time(&now);
03590 ast_mutex_lock(&qlock);
03591 if (!ast_strlen_zero(id)) {
03592 snprintf(idText,256,"ActionID: %s\r\n",id);
03593 }
03594 for (q = queues; q; q = q->next) {
03595 ast_mutex_lock(&q->lock);
03596
03597
03598 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
03599 if(q->callscompleted > 0)
03600 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03601 ast_cli(s->fd, "Event: QueueParams\r\n"
03602 "Queue: %s\r\n"
03603 "Max: %d\r\n"
03604 "Calls: %d\r\n"
03605 "Holdtime: %d\r\n"
03606 "Completed: %d\r\n"
03607 "Abandoned: %d\r\n"
03608 "ServiceLevel: %d\r\n"
03609 "ServicelevelPerf: %2.1f\r\n"
03610 "Weight: %d\r\n"
03611 "%s"
03612 "\r\n",
03613 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
03614 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
03615
03616 for (mem = q->members; mem; mem = mem->next) {
03617 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
03618 ast_cli(s->fd, "Event: QueueMember\r\n"
03619 "Queue: %s\r\n"
03620 "Location: %s\r\n"
03621 "Membership: %s\r\n"
03622 "Penalty: %d\r\n"
03623 "CallsTaken: %d\r\n"
03624 "LastCall: %d\r\n"
03625 "Status: %d\r\n"
03626 "Paused: %d\r\n"
03627 "%s"
03628 "\r\n",
03629 q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
03630 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
03631 }
03632 }
03633
03634 pos = 1;
03635 for (qe = q->head; qe; qe = qe->next) {
03636 ast_cli(s->fd, "Event: QueueEntry\r\n"
03637 "Queue: %s\r\n"
03638 "Position: %d\r\n"
03639 "Channel: %s\r\n"
03640 "CallerID: %s\r\n"
03641 "CallerIDName: %s\r\n"
03642 "Wait: %ld\r\n"
03643 "%s"
03644 "\r\n",
03645 q->name, pos++, qe->chan->name,
03646 qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
03647 qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
03648 (long)(now - qe->start), idText);
03649 }
03650 }
03651 ast_mutex_unlock(&q->lock);
03652 }
03653
03654 ast_cli(s->fd,
03655 "Event: QueueStatusComplete\r\n"
03656 "%s"
03657 "\r\n",idText);
03658
03659 ast_mutex_unlock(&qlock);
03660
03661 return RESULT_SUCCESS;
03662 }
03663
03664 static int manager_add_queue_member(struct mansession *s, struct message *m)
03665 {
03666 char *queuename, *interface, *penalty_s, *paused_s;
03667 int paused, penalty = 0;
03668
03669 queuename = astman_get_header(m, "Queue");
03670 interface = astman_get_header(m, "Interface");
03671 penalty_s = astman_get_header(m, "Penalty");
03672 paused_s = astman_get_header(m, "Paused");
03673
03674 if (ast_strlen_zero(queuename)) {
03675 astman_send_error(s, m, "'Queue' not specified.");
03676 return 0;
03677 }
03678
03679 if (ast_strlen_zero(interface)) {
03680 astman_send_error(s, m, "'Interface' not specified.");
03681 return 0;
03682 }
03683
03684 if (ast_strlen_zero(penalty_s))
03685 penalty = 0;
03686 else if (sscanf(penalty_s, "%d", &penalty) != 1) {
03687 penalty = 0;
03688 }
03689
03690 if (ast_strlen_zero(paused_s))
03691 paused = 0;
03692 else
03693 paused = abs(ast_true(paused_s));
03694
03695 switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) {
03696 case RES_OKAY:
03697 astman_send_ack(s, m, "Added interface to queue");
03698 break;
03699 case RES_EXISTS:
03700 astman_send_error(s, m, "Unable to add interface: Already there");
03701 break;
03702 case RES_NOSUCHQUEUE:
03703 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
03704 break;
03705 case RES_OUTOFMEMORY:
03706 astman_send_error(s, m, "Out of memory");
03707 break;
03708 }
03709 return 0;
03710 }
03711
03712 static int manager_remove_queue_member(struct mansession *s, struct message *m)
03713 {
03714 char *queuename, *interface;
03715
03716 queuename = astman_get_header(m, "Queue");
03717 interface = astman_get_header(m, "Interface");
03718
03719 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
03720 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
03721 return 0;
03722 }
03723
03724 switch (remove_from_queue(queuename, interface)) {
03725 case RES_OKAY:
03726 astman_send_ack(s, m, "Removed interface from queue");
03727 break;
03728 case RES_EXISTS:
03729 astman_send_error(s, m, "Unable to remove interface: Not there");
03730 break;
03731 case RES_NOSUCHQUEUE:
03732 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
03733 break;
03734 case RES_OUTOFMEMORY:
03735 astman_send_error(s, m, "Out of memory");
03736 break;
03737 }
03738 return 0;
03739 }
03740
03741 static int manager_pause_queue_member(struct mansession *s, struct message *m)
03742 {
03743 char *queuename, *interface, *paused_s;
03744 int paused;
03745
03746 interface = astman_get_header(m, "Interface");
03747 paused_s = astman_get_header(m, "Paused");
03748 queuename = astman_get_header(m, "Queue");
03749
03750 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
03751 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
03752 return 0;
03753 }
03754
03755 paused = abs(ast_true(paused_s));
03756
03757 if (set_member_paused(queuename, interface, paused))
03758 astman_send_error(s, m, "Interface not found");
03759 else
03760 if (paused)
03761 astman_send_ack(s, m, "Interface paused successfully");
03762 else
03763 astman_send_ack(s, m, "Interface unpaused successfully");
03764
03765 return 0;
03766 }
03767
03768 static int handle_add_queue_member(int fd, int argc, char *argv[])
03769 {
03770 char *queuename, *interface;
03771 int penalty;
03772
03773 if ((argc != 6) && (argc != 8)) {
03774 return RESULT_SHOWUSAGE;
03775 } else if (strcmp(argv[4], "to")) {
03776 return RESULT_SHOWUSAGE;
03777 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
03778 return RESULT_SHOWUSAGE;
03779 }
03780
03781 queuename = argv[5];
03782 interface = argv[3];
03783 if (argc == 8) {
03784 if (sscanf(argv[7], "%d", &penalty) == 1) {
03785 if (penalty < 0) {
03786 ast_cli(fd, "Penalty must be >= 0\n");
03787 penalty = 0;
03788 }
03789 } else {
03790 ast_cli(fd, "Penalty must be an integer >= 0\n");
03791 penalty = 0;
03792 }
03793 } else {
03794 penalty = 0;
03795 }
03796
03797 switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
03798 case RES_OKAY:
03799 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
03800 return RESULT_SUCCESS;
03801 case RES_EXISTS:
03802 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
03803 return RESULT_FAILURE;
03804 case RES_NOSUCHQUEUE:
03805 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
03806 return RESULT_FAILURE;
03807 case RES_OUTOFMEMORY:
03808 ast_cli(fd, "Out of memory\n");
03809 return RESULT_FAILURE;
03810 default:
03811 return RESULT_FAILURE;
03812 }
03813 }
03814
03815 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
03816 {
03817
03818 switch (pos) {
03819 case 3:
03820
03821 return NULL;
03822 case 4:
03823 if (state == 0) {
03824 return strdup("to");
03825 } else {
03826 return NULL;
03827 }
03828 case 5:
03829
03830 return complete_queue(line, word, pos, state);
03831 case 6:
03832 if (state == 0) {
03833 return strdup("penalty");
03834 } else {
03835 return NULL;
03836 }
03837 case 7:
03838 if (state < 100) {
03839 char *num = malloc(3);
03840 if (num) {
03841 sprintf(num, "%d", state);
03842 }
03843 return num;
03844 } else {
03845 return NULL;
03846 }
03847 default:
03848 return NULL;
03849 }
03850 }
03851
03852 static int handle_remove_queue_member(int fd, int argc, char *argv[])
03853 {
03854 char *queuename, *interface;
03855
03856 if (argc != 6) {
03857 return RESULT_SHOWUSAGE;
03858 } else if (strcmp(argv[4], "from")) {
03859 return RESULT_SHOWUSAGE;
03860 }
03861
03862 queuename = argv[5];
03863 interface = argv[3];
03864
03865 switch (remove_from_queue(queuename, interface)) {
03866 case RES_OKAY:
03867 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
03868 return RESULT_SUCCESS;
03869 case RES_EXISTS:
03870 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
03871 return RESULT_FAILURE;
03872 case RES_NOSUCHQUEUE:
03873 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
03874 return RESULT_FAILURE;
03875 case RES_OUTOFMEMORY:
03876 ast_cli(fd, "Out of memory\n");
03877 return RESULT_FAILURE;
03878 default:
03879 return RESULT_FAILURE;
03880 }
03881 }
03882
03883 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
03884 {
03885 int which = 0;
03886 struct call_queue *q;
03887 struct member *m;
03888
03889
03890 if ((pos > 5) || (pos < 3)) {
03891 return NULL;
03892 }
03893 if (pos == 4) {
03894 if (state == 0) {
03895 return strdup("from");
03896 } else {
03897 return NULL;
03898 }
03899 }
03900
03901 if (pos == 5) {
03902
03903 return complete_queue(line, word, pos, state);
03904 }
03905
03906 if (queues != NULL) {
03907 for (q = queues ; q ; q = q->next) {
03908 ast_mutex_lock(&q->lock);
03909 for (m = q->members ; m ; m = m->next) {
03910 if (++which > state) {
03911 ast_mutex_unlock(&q->lock);
03912 return strdup(m->interface);
03913 }
03914 }
03915 ast_mutex_unlock(&q->lock);
03916 }
03917 }
03918 return NULL;
03919 }
03920
03921 static char show_queues_usage[] =
03922 "Usage: show queues\n"
03923 " Provides summary information on call queues.\n";
03924
03925 static struct ast_cli_entry cli_show_queues = {
03926 { "show", "queues", NULL }, queues_show,
03927 "Show status of queues", show_queues_usage, NULL };
03928
03929 static char show_queue_usage[] =
03930 "Usage: show queue\n"
03931 " Provides summary information on a specified queue.\n";
03932
03933 static struct ast_cli_entry cli_show_queue = {
03934 { "show", "queue", NULL }, queue_show,
03935 "Show status of a specified queue", show_queue_usage, complete_queue };
03936
03937 static char aqm_cmd_usage[] =
03938 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
03939
03940 static struct ast_cli_entry cli_add_queue_member = {
03941 { "add", "queue", "member", NULL }, handle_add_queue_member,
03942 "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
03943
03944 static char rqm_cmd_usage[] =
03945 "Usage: remove queue member <channel> from <queue>\n";
03946
03947 static struct ast_cli_entry cli_remove_queue_member = {
03948 { "remove", "queue", "member", NULL }, handle_remove_queue_member,
03949 "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
03950
03951 int unload_module(void)
03952 {
03953 int res;
03954
03955 clear_and_free_interfaces();
03956 res = ast_cli_unregister(&cli_show_queue);
03957 res |= ast_cli_unregister(&cli_show_queues);
03958 res |= ast_cli_unregister(&cli_add_queue_member);
03959 res |= ast_cli_unregister(&cli_remove_queue_member);
03960 res |= ast_manager_unregister("Queues");
03961 res |= ast_manager_unregister("QueueStatus");
03962 res |= ast_manager_unregister("QueueAdd");
03963 res |= ast_manager_unregister("QueueRemove");
03964 res |= ast_manager_unregister("QueuePause");
03965 ast_devstate_del(statechange_queue, NULL);
03966 res |= ast_unregister_application(app_aqm);
03967 res |= ast_unregister_application(app_rqm);
03968 res |= ast_unregister_application(app_pqm);
03969 res |= ast_unregister_application(app_upqm);
03970 res |= ast_custom_function_unregister(&queueagentcount_function);
03971 res |= ast_unregister_application(app);
03972
03973 STANDARD_HANGUP_LOCALUSERS;
03974
03975 return res;
03976 }
03977
03978 int load_module(void)
03979 {
03980 int res;
03981
03982 res = ast_register_application(app, queue_exec, synopsis, descrip);
03983 res |= ast_cli_register(&cli_show_queue);
03984 res |= ast_cli_register(&cli_show_queues);
03985 res |= ast_cli_register(&cli_add_queue_member);
03986 res |= ast_cli_register(&cli_remove_queue_member);
03987 res |= ast_devstate_add(statechange_queue, NULL);
03988 res |= ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
03989 res |= ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
03990 res |= ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
03991 res |= ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
03992 res |= ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" );
03993 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
03994 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
03995 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
03996 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
03997 res |= ast_custom_function_register(&queueagentcount_function);
03998
03999 if (!res) {
04000 reload_queues();
04001 if (queue_persistent_members)
04002 reload_queue_members();
04003 }
04004
04005 return res;
04006 }
04007
04008
04009 int reload(void)
04010 {
04011 reload_queues();
04012 return 0;
04013 }
04014
04015 char *description(void)
04016 {
04017 return tdesc;
04018 }
04019
04020 int usecount(void)
04021 {
04022 int res;
04023 STANDARD_USECOUNT(res);
04024 return res;
04025 }
04026
04027 char *key()
04028 {
04029 return ASTERISK_GPL_KEY;
04030 }