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