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