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