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