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