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: 188473 $")
00066
00067 #include <sys/time.h>
00068 #include <sys/signal.h>
00069 #include <netinet/in.h>
00070 #include <ctype.h>
00071
00072 #include "asterisk/lock.h"
00073 #include "asterisk/file.h"
00074 #include "asterisk/channel.h"
00075 #include "asterisk/pbx.h"
00076 #include "asterisk/app.h"
00077 #include "asterisk/linkedlists.h"
00078 #include "asterisk/module.h"
00079 #include "asterisk/translate.h"
00080 #include "asterisk/say.h"
00081 #include "asterisk/features.h"
00082 #include "asterisk/musiconhold.h"
00083 #include "asterisk/cli.h"
00084 #include "asterisk/manager.h"
00085 #include "asterisk/config.h"
00086 #include "asterisk/monitor.h"
00087 #include "asterisk/utils.h"
00088 #include "asterisk/causes.h"
00089 #include "asterisk/astdb.h"
00090 #include "asterisk/devicestate.h"
00091 #include "asterisk/stringfields.h"
00092 #include "asterisk/event.h"
00093 #include "asterisk/astobj2.h"
00094 #include "asterisk/strings.h"
00095 #include "asterisk/global_datastores.h"
00096 #include "asterisk/taskprocessor.h"
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112 enum {
00113 QUEUE_STRATEGY_RINGALL = 0,
00114 QUEUE_STRATEGY_LEASTRECENT,
00115 QUEUE_STRATEGY_FEWESTCALLS,
00116 QUEUE_STRATEGY_RANDOM,
00117 QUEUE_STRATEGY_RRMEMORY,
00118 QUEUE_STRATEGY_LINEAR,
00119 QUEUE_STRATEGY_WRANDOM
00120 };
00121
00122 static const struct strategy {
00123 int strategy;
00124 const char *name;
00125 } strategies[] = {
00126 { QUEUE_STRATEGY_RINGALL, "ringall" },
00127 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00128 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00129 { QUEUE_STRATEGY_RANDOM, "random" },
00130 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00131 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
00132 { QUEUE_STRATEGY_LINEAR, "linear" },
00133 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
00134 };
00135
00136 static struct ast_taskprocessor *devicestate_tps;
00137
00138 #define DEFAULT_RETRY 5
00139 #define DEFAULT_TIMEOUT 15
00140 #define RECHECK 1
00141 #define MAX_PERIODIC_ANNOUNCEMENTS 10
00142 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
00143
00144 #define MAX_QUEUE_BUCKETS 53
00145
00146 #define RES_OKAY 0
00147 #define RES_EXISTS (-1)
00148 #define RES_OUTOFMEMORY (-2)
00149 #define RES_NOSUCHQUEUE (-3)
00150 #define RES_NOT_DYNAMIC (-4)
00151
00152 static char *app = "Queue";
00153
00154 static char *synopsis = "Queue a call for a call queue";
00155
00156 static char *descrip =
00157 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
00158 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00159 "This application will return to the dialplan if the queue does not exist, or\n"
00160 "any of the join options cause the caller to not enter the queue.\n"
00161 "The option string may contain zero or more of the following characters:\n"
00162 " 'c' -- continue in the dialplan if the callee hangs up.\n"
00163 " 'd' -- data-quality (modem) call (minimum delay).\n"
00164 " 'h' -- allow callee to hang up by hitting '*', or whatver disconnect sequence\n"
00165 " that is defined in the featuremap section in features.conf.\n"
00166 " 'H' -- allow caller to hang up by hitting '*', or whatever disconnect sequence\n"
00167 " that is defined in the featuremap section in features.conf.\n"
00168 " 'n' -- no retries on the timeout; will exit this application and \n"
00169 " go to the next step.\n"
00170 " 'i' -- ignore call forward requests from queue members and do nothing\n"
00171 " when they are requested.\n"
00172 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
00173 " 't' -- allow the called user transfer the calling user by pressing '#' or\n"
00174 " whatever blindxfer sequence defined in the featuremap section in\n"
00175 " features.conf\n"
00176 " 'T' -- to allow the calling user to transfer the call by pressing '#' or\n"
00177 " whatever blindxfer sequence defined in the featuremap section in\n"
00178 " features.conf\n"
00179 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
00180 " by pressing the automon sequence defined in the featuremap section in\n"
00181 " features.conf\n"
00182 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00183 " by pressing the automon sequence defined in the featuremap section in\n"
00184 " features.conf\n"
00185 " 'k' -- Allow the called party to enable parking of the call by sending\n"
00186 " the DTMF sequence defined for call parking in features.conf.\n"
00187 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
00188 " the DTMF sequence defined for call parking in features.conf.\n"
00189 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
00190 " by pressing the automixmon sequence defined in the featuremap section in\n"
00191 " features.conf\n"
00192 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
00193 " by pressing the automixmon sequence defined in the featuremap section in\n"
00194 " features.conf\n"
00195 " The optional URL will be sent to the called party if the channel supports\n"
00196 "it.\n"
00197 " The optional AGI parameter will setup an AGI script to be executed on the \n"
00198 "calling party's channel once they are connected to a queue member.\n"
00199 " The optional macro parameter will run a macro on the \n"
00200 "calling party's channel once they are connected to a queue member.\n"
00201 " The optional gosub parameter will run a gosub on the \n"
00202 "calling party's channel once they are connected to a queue member.\n"
00203 " The optional rule parameter will cause the queue's defaultrule to be\n"
00204 "overridden by the rule specified.\n"
00205 " The timeout will cause the queue to fail out after a specified number of\n"
00206 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00207 " This application sets the following channel variable upon completion:\n"
00208 " QUEUESTATUS The status of the call as a text string, one of\n"
00209 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
00210
00211 static char *app_aqm = "AddQueueMember" ;
00212 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00213 static char *app_aqm_descrip =
00214 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
00215 "Dynamically adds interface to an existing queue.\n"
00216 "If the interface is already in the queue it will return an error.\n"
00217 " This application sets the following channel variable upon completion:\n"
00218 " AQMSTATUS The status of the attempt to add a queue member as a \n"
00219 " text string, one of\n"
00220 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00221 "Example: AddQueueMember(techsupport,SIP/3000)\n"
00222 "";
00223
00224 static char *app_rqm = "RemoveQueueMember" ;
00225 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00226 static char *app_rqm_descrip =
00227 " RemoveQueueMember(queuename[,interface[,options]]):\n"
00228 "Dynamically removes interface to an existing queue\n"
00229 "If the interface is NOT in the queue it will return an error.\n"
00230 " This application sets the following channel variable upon completion:\n"
00231 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
00232 " text string, one of\n"
00233 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00234 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
00235 "";
00236
00237 static char *app_pqm = "PauseQueueMember" ;
00238 static char *app_pqm_synopsis = "Pauses a queue member" ;
00239 static char *app_pqm_descrip =
00240 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
00241 "Pauses (blocks calls for) a queue member.\n"
00242 "The given interface will be paused in the given queue. This prevents\n"
00243 "any calls from being sent from the queue to the interface until it is\n"
00244 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
00245 "queuename is given, the interface is paused in every queue it is a\n"
00246 "member of. The application will fail if the interface is not found.\n"
00247 "The reason string is entirely optional and is used to add extra information\n"
00248 "to the appropriate queue_log entries and manager events.\n"
00249 " This application sets the following channel variable upon completion:\n"
00250 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
00251 " text string, one of\n"
00252 " PAUSED | NOTFOUND\n"
00253 "Example: PauseQueueMember(,SIP/3000)\n";
00254
00255 static char *app_upqm = "UnpauseQueueMember" ;
00256 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00257 static char *app_upqm_descrip =
00258 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
00259 "Unpauses (resumes calls to) a queue member.\n"
00260 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00261 "same way, except it unpauses instead of pausing the given interface.\n"
00262 "The reason string is entirely optional and is used to add extra information\n"
00263 "to the appropriate queue_log entries and manager events.\n"
00264 " This application sets the following channel variable upon completion:\n"
00265 " UPQMSTATUS The status of the attempt to unpause a queue \n"
00266 " member as a text string, one of\n"
00267 " UNPAUSED | NOTFOUND\n"
00268 "Example: UnpauseQueueMember(,SIP/3000)\n";
00269
00270 static char *app_ql = "QueueLog" ;
00271 static char *app_ql_synopsis = "Writes to the queue_log" ;
00272 static char *app_ql_descrip =
00273 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
00274 "Allows you to write your own events into the queue log\n"
00275 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
00276
00277
00278 static const char *pm_family = "Queue/PersistentMembers";
00279
00280 #define PM_MAX_LEN 8192
00281
00282
00283 static int queue_keep_stats = 0;
00284
00285
00286 static int queue_persistent_members = 0;
00287
00288
00289 static int use_weight = 0;
00290
00291
00292 static int autofill_default = 0;
00293
00294
00295 static int montype_default = 0;
00296
00297
00298 static int shared_lastcall = 0;
00299
00300
00301 static struct ast_event_sub *device_state_sub;
00302
00303
00304 static int update_cdr = 0;
00305
00306 enum queue_result {
00307 QUEUE_UNKNOWN = 0,
00308 QUEUE_TIMEOUT = 1,
00309 QUEUE_JOINEMPTY = 2,
00310 QUEUE_LEAVEEMPTY = 3,
00311 QUEUE_JOINUNAVAIL = 4,
00312 QUEUE_LEAVEUNAVAIL = 5,
00313 QUEUE_FULL = 6,
00314 QUEUE_CONTINUE = 7,
00315 };
00316
00317 const struct {
00318 enum queue_result id;
00319 char *text;
00320 } queue_results[] = {
00321 { QUEUE_UNKNOWN, "UNKNOWN" },
00322 { QUEUE_TIMEOUT, "TIMEOUT" },
00323 { QUEUE_JOINEMPTY,"JOINEMPTY" },
00324 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00325 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00326 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00327 { QUEUE_FULL, "FULL" },
00328 { QUEUE_CONTINUE, "CONTINUE" },
00329 };
00330
00331 enum queue_timeout_priority {
00332 TIMEOUT_PRIORITY_APP,
00333 TIMEOUT_PRIORITY_CONF,
00334 };
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348 struct callattempt {
00349 struct callattempt *q_next;
00350 struct callattempt *call_next;
00351 struct ast_channel *chan;
00352 char interface[256];
00353 int stillgoing;
00354 int metric;
00355 int oldstatus;
00356 time_t lastcall;
00357 struct call_queue *lastqueue;
00358 struct member *member;
00359 };
00360
00361
00362 struct queue_ent {
00363 struct call_queue *parent;
00364 char moh[80];
00365 char announce[80];
00366 char context[AST_MAX_CONTEXT];
00367 char digits[AST_MAX_EXTENSION];
00368 int valid_digits;
00369 int pos;
00370 int prio;
00371 int last_pos_said;
00372 time_t last_periodic_announce_time;
00373 int last_periodic_announce_sound;
00374 time_t last_pos;
00375 int opos;
00376 int handled;
00377 int pending;
00378 int max_penalty;
00379 int min_penalty;
00380 int linpos;
00381 int linwrapped;
00382 time_t start;
00383 time_t expire;
00384 struct ast_channel *chan;
00385 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules;
00386 struct penalty_rule *pr;
00387 struct queue_ent *next;
00388 };
00389
00390 struct member {
00391 char interface[80];
00392 char state_interface[80];
00393 char membername[80];
00394 int penalty;
00395 int calls;
00396 int dynamic;
00397 int realtime;
00398 int status;
00399 int paused;
00400 time_t lastcall;
00401 struct call_queue *lastqueue;
00402 unsigned int dead:1;
00403 unsigned int delme:1;
00404 char rt_uniqueid[80];
00405 };
00406
00407 struct member_interface {
00408 char interface[80];
00409 AST_LIST_ENTRY(member_interface) list;
00410 };
00411
00412 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00413
00414
00415 #define QUEUE_EMPTY_NORMAL 1
00416 #define QUEUE_EMPTY_STRICT 2
00417 #define QUEUE_EMPTY_LOOSE 3
00418 #define ANNOUNCEHOLDTIME_ALWAYS 1
00419 #define ANNOUNCEHOLDTIME_ONCE 2
00420 #define QUEUE_EVENT_VARIABLES 3
00421
00422 struct penalty_rule {
00423 int time;
00424 int max_value;
00425 int min_value;
00426 int max_relative;
00427 int min_relative;
00428 AST_LIST_ENTRY(penalty_rule) list;
00429 };
00430
00431 #define ANNOUNCEPOSITION_YES 1
00432 #define ANNOUNCEPOSITION_NO 2
00433 #define ANNOUNCEPOSITION_MORE_THAN 3
00434 #define ANNOUNCEPOSITION_LIMIT 4
00435
00436 struct call_queue {
00437 AST_DECLARE_STRING_FIELDS(
00438
00439 AST_STRING_FIELD(name);
00440
00441 AST_STRING_FIELD(moh);
00442
00443 AST_STRING_FIELD(announce);
00444
00445 AST_STRING_FIELD(context);
00446
00447 AST_STRING_FIELD(membermacro);
00448
00449 AST_STRING_FIELD(membergosub);
00450
00451 AST_STRING_FIELD(defaultrule);
00452
00453 AST_STRING_FIELD(sound_next);
00454
00455 AST_STRING_FIELD(sound_thereare);
00456
00457 AST_STRING_FIELD(sound_calls);
00458
00459 AST_STRING_FIELD(queue_quantity1);
00460
00461 AST_STRING_FIELD(queue_quantity2);
00462
00463 AST_STRING_FIELD(sound_holdtime);
00464
00465 AST_STRING_FIELD(sound_minutes);
00466
00467 AST_STRING_FIELD(sound_minute);
00468
00469 AST_STRING_FIELD(sound_seconds);
00470
00471 AST_STRING_FIELD(sound_thanks);
00472
00473 AST_STRING_FIELD(sound_callerannounce);
00474
00475 AST_STRING_FIELD(sound_reporthold);
00476 );
00477
00478 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
00479 unsigned int dead:1;
00480 unsigned int joinempty:2;
00481 unsigned int eventwhencalled:2;
00482 unsigned int leavewhenempty:2;
00483 unsigned int ringinuse:1;
00484 unsigned int setinterfacevar:1;
00485 unsigned int setqueuevar:1;
00486 unsigned int setqueueentryvar:1;
00487 unsigned int reportholdtime:1;
00488 unsigned int wrapped:1;
00489 unsigned int timeoutrestart:1;
00490 unsigned int announceholdtime:2;
00491 unsigned int announceposition:3;
00492 int strategy:4;
00493 unsigned int maskmemberstatus:1;
00494 unsigned int realtime:1;
00495 unsigned int found:1;
00496 int announcepositionlimit;
00497 int announcefrequency;
00498 int minannouncefrequency;
00499 int periodicannouncefrequency;
00500 int numperiodicannounce;
00501 int randomperiodicannounce;
00502 int roundingseconds;
00503 int holdtime;
00504 int callscompleted;
00505 int callsabandoned;
00506 int servicelevel;
00507 int callscompletedinsl;
00508 char monfmt[8];
00509 int montype;
00510 int count;
00511 int maxlen;
00512 int wrapuptime;
00513
00514 int retry;
00515 int timeout;
00516 int weight;
00517 int autopause;
00518 int timeoutpriority;
00519
00520
00521 int rrpos;
00522 int memberdelay;
00523 int autofill;
00524
00525 struct ao2_container *members;
00526
00527
00528
00529
00530
00531 int membercount;
00532 struct queue_ent *head;
00533 AST_LIST_ENTRY(call_queue) list;
00534 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules;
00535 };
00536
00537 struct rule_list {
00538 char name[80];
00539 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
00540 AST_LIST_ENTRY(rule_list) list;
00541 };
00542
00543 AST_LIST_HEAD_STATIC(rule_lists, rule_list);
00544
00545 static struct ao2_container *queues;
00546
00547 static void copy_rules(struct queue_ent *qe, const char *rulename);
00548 static void update_qe_rule(struct queue_ent *qe);
00549 static void update_realtime_members(struct call_queue *q);
00550 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
00551
00552 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00553
00554 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00555 {
00556 int i;
00557
00558 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
00559 if (queue_results[i].id == res) {
00560 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00561 return;
00562 }
00563 }
00564 }
00565
00566 static const char *int2strat(int strategy)
00567 {
00568 int x;
00569
00570 for (x = 0; x < ARRAY_LEN(strategies); x++) {
00571 if (strategy == strategies[x].strategy)
00572 return strategies[x].name;
00573 }
00574
00575 return "<unknown>";
00576 }
00577
00578 static int strat2int(const char *strategy)
00579 {
00580 int x;
00581
00582 for (x = 0; x < ARRAY_LEN(strategies); x++) {
00583 if (!strcasecmp(strategy, strategies[x].name))
00584 return strategies[x].strategy;
00585 }
00586
00587 return -1;
00588 }
00589
00590 static int queue_hash_cb(const void *obj, const int flags)
00591 {
00592 const struct call_queue *q = obj;
00593
00594 return ast_str_case_hash(q->name);
00595 }
00596
00597 static int queue_cmp_cb(void *obj, void *arg, int flags)
00598 {
00599 struct call_queue *q = obj, *q2 = arg;
00600 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
00601 }
00602
00603 static inline struct call_queue *queue_ref(struct call_queue *q)
00604 {
00605 ao2_ref(q, 1);
00606 return q;
00607 }
00608
00609 static inline struct call_queue *queue_unref(struct call_queue *q)
00610 {
00611 ao2_ref(q, -1);
00612 return q;
00613 }
00614
00615
00616 static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
00617 {
00618 char interfacevar[256]="";
00619 float sl = 0;
00620
00621 if (q->setqueuevar) {
00622 sl = 0;
00623 if (q->callscompleted > 0)
00624 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00625
00626 snprintf(interfacevar, sizeof(interfacevar),
00627 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00628 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
00629
00630 pbx_builtin_setvar_multiple(chan, interfacevar);
00631 }
00632 }
00633
00634
00635 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00636 {
00637 struct queue_ent *cur;
00638
00639 if (!q || !new)
00640 return;
00641 if (prev) {
00642 cur = prev->next;
00643 prev->next = new;
00644 } else {
00645 cur = q->head;
00646 q->head = new;
00647 }
00648 new->next = cur;
00649 new->parent = q;
00650 new->pos = ++(*pos);
00651 new->opos = *pos;
00652 }
00653
00654 enum queue_member_status {
00655 QUEUE_NO_MEMBERS,
00656 QUEUE_NO_REACHABLE_MEMBERS,
00657 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
00658 QUEUE_NORMAL
00659 };
00660
00661
00662
00663
00664
00665
00666
00667 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
00668 {
00669 struct member *member;
00670 struct ao2_iterator mem_iter;
00671 enum queue_member_status result = QUEUE_NO_MEMBERS;
00672
00673 ao2_lock(q);
00674 mem_iter = ao2_iterator_init(q->members, 0);
00675 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00676 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00677 continue;
00678
00679 switch (member->status) {
00680 case AST_DEVICE_INVALID:
00681
00682 break;
00683 case AST_DEVICE_UNAVAILABLE:
00684 if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
00685 result = QUEUE_NO_REACHABLE_MEMBERS;
00686 break;
00687 default:
00688 if (member->paused) {
00689 result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00690 } else {
00691 ao2_unlock(q);
00692 ao2_ref(member, -1);
00693 return QUEUE_NORMAL;
00694 }
00695 break;
00696 }
00697 }
00698
00699 ao2_unlock(q);
00700 return result;
00701 }
00702
00703 struct statechange {
00704 AST_LIST_ENTRY(statechange) entry;
00705 int state;
00706 char dev[0];
00707 };
00708
00709
00710
00711
00712
00713
00714 static int update_status(const char *interface, const int status)
00715 {
00716 struct member *cur;
00717 struct ao2_iterator mem_iter, queue_iter;
00718 struct call_queue *q;
00719
00720 queue_iter = ao2_iterator_init(queues, 0);
00721 while ((q = ao2_iterator_next(&queue_iter))) {
00722 ao2_lock(q);
00723 mem_iter = ao2_iterator_init(q->members, 0);
00724 while ((cur = ao2_iterator_next(&mem_iter))) {
00725 char *tmp_interface;
00726 char *slash_pos;
00727 tmp_interface = ast_strdupa(cur->state_interface);
00728 if ((slash_pos = strchr(tmp_interface, '/')))
00729 if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00730 *slash_pos = '\0';
00731
00732 if (strcasecmp(interface, tmp_interface)) {
00733 ao2_ref(cur, -1);
00734 continue;
00735 }
00736
00737 if (cur->status != status) {
00738 cur->status = status;
00739 if (q->maskmemberstatus) {
00740 ao2_ref(cur, -1);
00741 continue;
00742 }
00743
00744 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00745 "Queue: %s\r\n"
00746 "Location: %s\r\n"
00747 "MemberName: %s\r\n"
00748 "Membership: %s\r\n"
00749 "Penalty: %d\r\n"
00750 "CallsTaken: %d\r\n"
00751 "LastCall: %d\r\n"
00752 "Status: %d\r\n"
00753 "Paused: %d\r\n",
00754 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00755 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00756 }
00757 ao2_ref(cur, -1);
00758 }
00759 queue_unref(q);
00760 ao2_unlock(q);
00761 }
00762
00763 return 0;
00764 }
00765
00766
00767 static int handle_statechange(void *datap)
00768 {
00769 struct member_interface *curint;
00770 struct statechange *sc = datap;
00771
00772 AST_LIST_LOCK(&interfaces);
00773 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00774 char *interface;
00775 char *slash_pos;
00776 interface = ast_strdupa(curint->interface);
00777 if ((slash_pos = strchr(interface, '/')))
00778 if ((slash_pos = strchr(slash_pos + 1, '/')))
00779 *slash_pos = '\0';
00780
00781 if (!strcasecmp(interface, sc->dev))
00782 break;
00783 }
00784 AST_LIST_UNLOCK(&interfaces);
00785
00786 if (!curint) {
00787 ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, devstate2str(sc->state));
00788 ast_free(sc);
00789 return 0;
00790 }
00791
00792 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00793
00794 update_status(sc->dev, sc->state);
00795 ast_free(sc);
00796 return 0;
00797 }
00798
00799 static void device_state_cb(const struct ast_event *event, void *unused)
00800 {
00801 enum ast_device_state state;
00802 const char *device;
00803 struct statechange *sc;
00804 size_t datapsize;
00805
00806 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00807 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00808
00809 if (ast_strlen_zero(device)) {
00810 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00811 return;
00812 }
00813 datapsize = sizeof(*sc) + strlen(device) + 1;
00814 if (!(sc = ast_calloc(1, datapsize))) {
00815 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
00816 return;
00817 }
00818 sc->state = state;
00819 strcpy(sc->dev, device);
00820 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
00821 ast_free(sc);
00822 }
00823 }
00824
00825
00826 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
00827 {
00828 struct member *cur;
00829
00830 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00831 cur->penalty = penalty;
00832 cur->paused = paused;
00833 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00834 if (!ast_strlen_zero(state_interface))
00835 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00836 else
00837 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00838 if (!ast_strlen_zero(membername))
00839 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00840 else
00841 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00842 if (!strchr(cur->interface, '/'))
00843 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00844 cur->status = ast_device_state(cur->state_interface);
00845 }
00846
00847 return cur;
00848 }
00849
00850
00851 static int compress_char(const char c)
00852 {
00853 if (c < 32)
00854 return 0;
00855 else if (c > 96)
00856 return c - 64;
00857 else
00858 return c - 32;
00859 }
00860
00861 static int member_hash_fn(const void *obj, const int flags)
00862 {
00863 const struct member *mem = obj;
00864 const char *chname = strchr(mem->interface, '/');
00865 int ret = 0, i;
00866 if (!chname)
00867 chname = mem->interface;
00868 for (i = 0; i < 5 && chname[i]; i++)
00869 ret += compress_char(chname[i]) << (i * 6);
00870 return ret;
00871 }
00872
00873 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00874 {
00875 struct member *mem1 = obj1, *mem2 = obj2;
00876 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00877 }
00878
00879
00880
00881
00882
00883 static void init_queue(struct call_queue *q)
00884 {
00885 int i;
00886 struct penalty_rule *pr_iter;
00887
00888 q->dead = 0;
00889 q->retry = DEFAULT_RETRY;
00890 q->timeout = DEFAULT_TIMEOUT;
00891 q->maxlen = 0;
00892 q->announcefrequency = 0;
00893 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
00894 q->announceholdtime = 1;
00895 q->announcepositionlimit = 10;
00896 q->announceposition = ANNOUNCEPOSITION_YES;
00897 q->roundingseconds = 0;
00898 q->servicelevel = 0;
00899 q->ringinuse = 1;
00900 q->setinterfacevar = 0;
00901 q->setqueuevar = 0;
00902 q->setqueueentryvar = 0;
00903 q->autofill = autofill_default;
00904 q->montype = montype_default;
00905 q->monfmt[0] = '\0';
00906 q->reportholdtime = 0;
00907 q->wrapuptime = 0;
00908 q->joinempty = 0;
00909 q->leavewhenempty = 0;
00910 q->memberdelay = 0;
00911 q->maskmemberstatus = 0;
00912 q->eventwhencalled = 0;
00913 q->weight = 0;
00914 q->timeoutrestart = 0;
00915 q->periodicannouncefrequency = 0;
00916 q->randomperiodicannounce = 0;
00917 q->numperiodicannounce = 0;
00918 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
00919 if (!q->members) {
00920 if (q->strategy == QUEUE_STRATEGY_LINEAR)
00921
00922 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00923 else
00924 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00925 }
00926 q->membercount = 0;
00927 q->found = 1;
00928
00929 ast_string_field_set(q, sound_next, "queue-youarenext");
00930 ast_string_field_set(q, sound_thereare, "queue-thereare");
00931 ast_string_field_set(q, sound_calls, "queue-callswaiting");
00932 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
00933 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
00934 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
00935 ast_string_field_set(q, sound_minutes, "queue-minutes");
00936 ast_string_field_set(q, sound_minute, "queue-minute");
00937 ast_string_field_set(q, sound_seconds, "queue-seconds");
00938 ast_string_field_set(q, sound_thanks, "queue-thankyou");
00939 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
00940
00941 if ((q->sound_periodicannounce[0] = ast_str_create(32)))
00942 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
00943
00944 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00945 if (q->sound_periodicannounce[i])
00946 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
00947 }
00948
00949 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
00950 ast_free(pr_iter);
00951 }
00952
00953 static void clear_queue(struct call_queue *q)
00954 {
00955 q->holdtime = 0;
00956 q->callscompleted = 0;
00957 q->callsabandoned = 0;
00958 q->callscompletedinsl = 0;
00959 q->wrapuptime = 0;
00960 }
00961
00962 static int add_to_interfaces(const char *interface)
00963 {
00964 struct member_interface *curint;
00965
00966 AST_LIST_LOCK(&interfaces);
00967 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00968 if (!strcasecmp(curint->interface, interface))
00969 break;
00970 }
00971
00972 if (curint) {
00973 AST_LIST_UNLOCK(&interfaces);
00974 return 0;
00975 }
00976
00977 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00978
00979 if ((curint = ast_calloc(1, sizeof(*curint)))) {
00980 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00981 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00982 }
00983 AST_LIST_UNLOCK(&interfaces);
00984
00985 return 0;
00986 }
00987
00988 static int interface_exists_global(const char *interface, int lock_queue_container)
00989 {
00990 struct call_queue *q;
00991 struct member *mem, tmpmem;
00992 struct ao2_iterator queue_iter, mem_iter;
00993 int ret = 0;
00994
00995 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00996 queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK);
00997 while ((q = ao2_iterator_next(&queue_iter))) {
00998 ao2_lock(q);
00999 mem_iter = ao2_iterator_init(q->members, 0);
01000 while ((mem = ao2_iterator_next(&mem_iter))) {
01001 if (!strcasecmp(mem->state_interface, interface)) {
01002 ao2_ref(mem, -1);
01003 ret = 1;
01004 break;
01005 }
01006 }
01007 ao2_unlock(q);
01008 queue_unref(q);
01009 }
01010
01011 return ret;
01012 }
01013
01014 static int remove_from_interfaces(const char *interface, int lock_queue_container)
01015 {
01016 struct member_interface *curint;
01017
01018 if (interface_exists_global(interface, lock_queue_container))
01019 return 0;
01020
01021 AST_LIST_LOCK(&interfaces);
01022 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01023 if (!strcasecmp(curint->interface, interface)) {
01024 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01025 AST_LIST_REMOVE_CURRENT(list);
01026 ast_free(curint);
01027 break;
01028 }
01029 }
01030 AST_LIST_TRAVERSE_SAFE_END;
01031 AST_LIST_UNLOCK(&interfaces);
01032
01033 return 0;
01034 }
01035
01036 static void clear_and_free_interfaces(void)
01037 {
01038 struct member_interface *curint;
01039
01040 AST_LIST_LOCK(&interfaces);
01041 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01042 ast_free(curint);
01043 AST_LIST_UNLOCK(&interfaces);
01044 }
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
01056 {
01057 char *timestr, *maxstr, *minstr, *contentdup;
01058 struct penalty_rule *rule = NULL, *rule_iter;
01059 struct rule_list *rl_iter;
01060 int penaltychangetime, inserted = 0;
01061
01062 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01063 ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
01064 return -1;
01065 }
01066
01067 contentdup = ast_strdupa(content);
01068
01069 if (!(maxstr = strchr(contentdup, ','))) {
01070 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01071 ast_free(rule);
01072 return -1;
01073 }
01074
01075 *maxstr++ = '\0';
01076 timestr = contentdup;
01077
01078 if ((penaltychangetime = atoi(timestr)) < 0) {
01079 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01080 ast_free(rule);
01081 return -1;
01082 }
01083
01084 rule->time = penaltychangetime;
01085
01086 if ((minstr = strchr(maxstr,',')))
01087 *minstr++ = '\0';
01088
01089
01090
01091 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01092 rule->max_relative = 1;
01093 }
01094
01095 rule->max_value = atoi(maxstr);
01096
01097 if (!ast_strlen_zero(minstr)) {
01098 if (*minstr == '+' || *minstr == '-')
01099 rule->min_relative = 1;
01100 rule->min_value = atoi(minstr);
01101 } else
01102 rule->min_relative = 1;
01103
01104
01105 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01106 if (strcasecmp(rl_iter->name, list_name))
01107 continue;
01108
01109 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01110 if (rule->time < rule_iter->time) {
01111 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01112 inserted = 1;
01113 break;
01114 }
01115 }
01116 AST_LIST_TRAVERSE_SAFE_END;
01117
01118 if (!inserted) {
01119 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01120 }
01121 }
01122
01123 return 0;
01124 }
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
01135 {
01136 if (!strcasecmp(param, "musicclass") ||
01137 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01138 ast_string_field_set(q, moh, val);
01139 } else if (!strcasecmp(param, "announce")) {
01140 ast_string_field_set(q, announce, val);
01141 } else if (!strcasecmp(param, "context")) {
01142 ast_string_field_set(q, context, val);
01143 } else if (!strcasecmp(param, "timeout")) {
01144 q->timeout = atoi(val);
01145 if (q->timeout < 0)
01146 q->timeout = DEFAULT_TIMEOUT;
01147 } else if (!strcasecmp(param, "ringinuse")) {
01148 q->ringinuse = ast_true(val);
01149 } else if (!strcasecmp(param, "setinterfacevar")) {
01150 q->setinterfacevar = ast_true(val);
01151 } else if (!strcasecmp(param, "setqueuevar")) {
01152 q->setqueuevar = ast_true(val);
01153 } else if (!strcasecmp(param, "setqueueentryvar")) {
01154 q->setqueueentryvar = ast_true(val);
01155 } else if (!strcasecmp(param, "monitor-format")) {
01156 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01157 } else if (!strcasecmp(param, "membermacro")) {
01158 ast_string_field_set(q, membermacro, val);
01159 } else if (!strcasecmp(param, "membergosub")) {
01160 ast_string_field_set(q, membergosub, val);
01161 } else if (!strcasecmp(param, "queue-youarenext")) {
01162 ast_string_field_set(q, sound_next, val);
01163 } else if (!strcasecmp(param, "queue-thereare")) {
01164 ast_string_field_set(q, sound_thereare, val);
01165 } else if (!strcasecmp(param, "queue-callswaiting")) {
01166 ast_string_field_set(q, sound_calls, val);
01167 } else if (!strcasecmp(param, "queue-quantity1")) {
01168 ast_string_field_set(q, queue_quantity1, val);
01169 } else if (!strcasecmp(param, "queue-quantity2")) {
01170 ast_string_field_set(q, queue_quantity2, val);
01171 } else if (!strcasecmp(param, "queue-holdtime")) {
01172 ast_string_field_set(q, sound_holdtime, val);
01173 } else if (!strcasecmp(param, "queue-minutes")) {
01174 ast_string_field_set(q, sound_minutes, val);
01175 } else if (!strcasecmp(param, "queue-minute")) {
01176 ast_string_field_set(q, sound_minute, val);
01177 } else if (!strcasecmp(param, "queue-seconds")) {
01178 ast_string_field_set(q, sound_seconds, val);
01179 } else if (!strcasecmp(param, "queue-thankyou")) {
01180 ast_string_field_set(q, sound_thanks, val);
01181 } else if (!strcasecmp(param, "queue-callerannounce")) {
01182 ast_string_field_set(q, sound_callerannounce, val);
01183 } else if (!strcasecmp(param, "queue-reporthold")) {
01184 ast_string_field_set(q, sound_reporthold, val);
01185 } else if (!strcasecmp(param, "announce-frequency")) {
01186 q->announcefrequency = atoi(val);
01187 } else if (!strcasecmp(param, "min-announce-frequency")) {
01188 q->minannouncefrequency = atoi(val);
01189 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01190 } else if (!strcasecmp(param, "announce-round-seconds")) {
01191 q->roundingseconds = atoi(val);
01192
01193 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01194 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01195 if (linenum >= 0) {
01196 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01197 "using 0 instead for queue '%s' at line %d of queues.conf\n",
01198 val, param, q->name, linenum);
01199 } else {
01200 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01201 "using 0 instead for queue '%s'\n", val, param, q->name);
01202 }
01203 q->roundingseconds=0;
01204 }
01205 } else if (!strcasecmp(param, "announce-holdtime")) {
01206 if (!strcasecmp(val, "once"))
01207 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01208 else if (ast_true(val))
01209 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01210 else
01211 q->announceholdtime = 0;
01212 } else if (!strcasecmp(param, "announce-position")) {
01213 if (!strcasecmp(val, "limit"))
01214 q->announceposition = ANNOUNCEPOSITION_LIMIT;
01215 else if (!strcasecmp(val, "more"))
01216 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01217 else if (ast_true(val))
01218 q->announceposition = ANNOUNCEPOSITION_YES;
01219 else
01220 q->announceposition = ANNOUNCEPOSITION_NO;
01221 } else if (!strcasecmp(param, "announce-position-limit")) {
01222 q->announcepositionlimit = atoi(val);
01223 } else if (!strcasecmp(param, "periodic-announce")) {
01224 if (strchr(val, ',')) {
01225 char *s, *buf = ast_strdupa(val);
01226 unsigned int i = 0;
01227
01228 while ((s = strsep(&buf, ",|"))) {
01229 if (!q->sound_periodicannounce[i])
01230 q->sound_periodicannounce[i] = ast_str_create(16);
01231 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01232 i++;
01233 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01234 break;
01235 }
01236 q->numperiodicannounce = i;
01237 } else {
01238 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01239 q->numperiodicannounce = 1;
01240 }
01241 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01242 q->periodicannouncefrequency = atoi(val);
01243 } else if (!strcasecmp(param, "random-periodic-announce")) {
01244 q->randomperiodicannounce = ast_true(val);
01245 } else if (!strcasecmp(param, "retry")) {
01246 q->retry = atoi(val);
01247 if (q->retry <= 0)
01248 q->retry = DEFAULT_RETRY;
01249 } else if (!strcasecmp(param, "wrapuptime")) {
01250 q->wrapuptime = atoi(val);
01251 } else if (!strcasecmp(param, "autofill")) {
01252 q->autofill = ast_true(val);
01253 } else if (!strcasecmp(param, "monitor-type")) {
01254 if (!strcasecmp(val, "mixmonitor"))
01255 q->montype = 1;
01256 } else if (!strcasecmp(param, "autopause")) {
01257 q->autopause = ast_true(val);
01258 } else if (!strcasecmp(param, "maxlen")) {
01259 q->maxlen = atoi(val);
01260 if (q->maxlen < 0)
01261 q->maxlen = 0;
01262 } else if (!strcasecmp(param, "servicelevel")) {
01263 q->servicelevel= atoi(val);
01264 } else if (!strcasecmp(param, "strategy")) {
01265 int strategy;
01266
01267
01268 if (failunknown) {
01269 return;
01270 }
01271 strategy = strat2int(val);
01272 if (strategy < 0) {
01273 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01274 val, q->name);
01275 q->strategy = QUEUE_STRATEGY_RINGALL;
01276 }
01277 if (strategy == q->strategy) {
01278 return;
01279 }
01280 if (strategy == QUEUE_STRATEGY_LINEAR) {
01281 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01282 return;
01283 }
01284 q->strategy = strategy;
01285 } else if (!strcasecmp(param, "joinempty")) {
01286 if (!strcasecmp(val, "loose"))
01287 q->joinempty = QUEUE_EMPTY_LOOSE;
01288 else if (!strcasecmp(val, "strict"))
01289 q->joinempty = QUEUE_EMPTY_STRICT;
01290 else if (ast_true(val))
01291 q->joinempty = QUEUE_EMPTY_NORMAL;
01292 else
01293 q->joinempty = 0;
01294 } else if (!strcasecmp(param, "leavewhenempty")) {
01295 if (!strcasecmp(val, "loose"))
01296 q->leavewhenempty = QUEUE_EMPTY_LOOSE;
01297 else if (!strcasecmp(val, "strict"))
01298 q->leavewhenempty = QUEUE_EMPTY_STRICT;
01299 else if (ast_true(val))
01300 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01301 else
01302 q->leavewhenempty = 0;
01303 } else if (!strcasecmp(param, "eventmemberstatus")) {
01304 q->maskmemberstatus = !ast_true(val);
01305 } else if (!strcasecmp(param, "eventwhencalled")) {
01306 if (!strcasecmp(val, "vars")) {
01307 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01308 } else {
01309 q->eventwhencalled = ast_true(val) ? 1 : 0;
01310 }
01311 } else if (!strcasecmp(param, "reportholdtime")) {
01312 q->reportholdtime = ast_true(val);
01313 } else if (!strcasecmp(param, "memberdelay")) {
01314 q->memberdelay = atoi(val);
01315 } else if (!strcasecmp(param, "weight")) {
01316 q->weight = atoi(val);
01317 if (q->weight)
01318 use_weight++;
01319
01320
01321 } else if (!strcasecmp(param, "timeoutrestart")) {
01322 q->timeoutrestart = ast_true(val);
01323 } else if (!strcasecmp(param, "defaultrule")) {
01324 ast_string_field_set(q, defaultrule, val);
01325 } else if (!strcasecmp(param, "timeoutpriority")) {
01326 if (!strcasecmp(val, "conf")) {
01327 q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01328 } else {
01329 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01330 }
01331 } else if (failunknown) {
01332 if (linenum >= 0) {
01333 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01334 q->name, param, linenum);
01335 } else {
01336 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01337 }
01338 }
01339 }
01340
01341
01342
01343
01344
01345
01346
01347 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
01348 {
01349 struct member *m;
01350 struct ao2_iterator mem_iter;
01351 int penalty = 0;
01352 int paused = 0;
01353 int found = 0;
01354
01355 if (penalty_str) {
01356 penalty = atoi(penalty_str);
01357 if (penalty < 0)
01358 penalty = 0;
01359 }
01360
01361 if (paused_str) {
01362 paused = atoi(paused_str);
01363 if (paused < 0)
01364 paused = 0;
01365 }
01366
01367
01368 mem_iter = ao2_iterator_init(q->members, 0);
01369 while ((m = ao2_iterator_next(&mem_iter))) {
01370 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01371 m->dead = 0;
01372 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01373 if (paused_str)
01374 m->paused = paused;
01375 if (strcasecmp(state_interface, m->state_interface)) {
01376 remove_from_interfaces(m->state_interface, 0);
01377 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01378 add_to_interfaces(m->state_interface);
01379 }
01380 m->penalty = penalty;
01381 found = 1;
01382 ao2_ref(m, -1);
01383 break;
01384 }
01385 ao2_ref(m, -1);
01386 }
01387
01388
01389 if (!found) {
01390 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01391 m->dead = 0;
01392 m->realtime = 1;
01393 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01394 add_to_interfaces(m->state_interface);
01395 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01396 ao2_link(q->members, m);
01397 ao2_ref(m, -1);
01398 m = NULL;
01399 q->membercount++;
01400 }
01401 }
01402 }
01403
01404
01405 static void free_members(struct call_queue *q, int all)
01406 {
01407
01408 struct member *cur;
01409 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01410
01411 while ((cur = ao2_iterator_next(&mem_iter))) {
01412 if (all || !cur->dynamic) {
01413 ao2_unlink(q->members, cur);
01414 remove_from_interfaces(cur->state_interface, 1);
01415 q->membercount--;
01416 }
01417 ao2_ref(cur, -1);
01418 }
01419 }
01420
01421
01422 static void destroy_queue(void *obj)
01423 {
01424 struct call_queue *q = obj;
01425 int i;
01426
01427 free_members(q, 1);
01428 ast_string_field_free_memory(q);
01429 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01430 if (q->sound_periodicannounce[i])
01431 free(q->sound_periodicannounce[i]);
01432 }
01433 ao2_ref(q->members, -1);
01434 }
01435
01436 static struct call_queue *alloc_queue(const char *queuename)
01437 {
01438 struct call_queue *q;
01439
01440 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
01441 if (ast_string_field_init(q, 64)) {
01442 ao2_ref(q, -1);
01443 return NULL;
01444 }
01445 ast_string_field_set(q, name, queuename);
01446 }
01447 return q;
01448 }
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
01461 {
01462 struct ast_variable *v;
01463 struct call_queue *q, tmpq = {
01464 .name = queuename,
01465 };
01466 struct member *m;
01467 struct ao2_iterator mem_iter;
01468 char *interface = NULL;
01469 const char *tmp_name;
01470 char *tmp;
01471 char tmpbuf[64];
01472
01473
01474 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
01475 ao2_lock(q);
01476 if (!q->realtime) {
01477 if (q->dead) {
01478 ao2_unlock(q);
01479 queue_unref(q);
01480 return NULL;
01481 } else {
01482 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01483 ao2_unlock(q);
01484 return q;
01485 }
01486 }
01487 queue_unref(q);
01488 } else if (!member_config)
01489
01490 return NULL;
01491
01492
01493 if (!queue_vars) {
01494
01495 if (q) {
01496
01497
01498
01499 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
01500
01501 q->dead = 1;
01502
01503 ao2_unlink(queues, q);
01504 ao2_unlock(q);
01505 queue_unref(q);
01506 }
01507 return NULL;
01508 }
01509
01510
01511 if (!q) {
01512 struct ast_variable *tmpvar = NULL;
01513 if (!(q = alloc_queue(queuename)))
01514 return NULL;
01515 ao2_lock(q);
01516 clear_queue(q);
01517 q->realtime = 1;
01518
01519
01520
01521 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01522 if (!strcasecmp(tmpvar->name, "strategy")) {
01523 q->strategy = strat2int(tmpvar->value);
01524 if (q->strategy < 0) {
01525 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01526 tmpvar->value, q->name);
01527 q->strategy = QUEUE_STRATEGY_RINGALL;
01528 }
01529 break;
01530 }
01531 }
01532
01533 if (!tmpvar)
01534 q->strategy = QUEUE_STRATEGY_RINGALL;
01535 ao2_link(queues, q);
01536 }
01537 init_queue(q);
01538
01539 memset(tmpbuf, 0, sizeof(tmpbuf));
01540 for (v = queue_vars; v; v = v->next) {
01541
01542 if ((tmp = strchr(v->name, '_'))) {
01543 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01544 tmp_name = tmpbuf;
01545 tmp = tmpbuf;
01546 while ((tmp = strchr(tmp, '_')))
01547 *tmp++ = '-';
01548 } else
01549 tmp_name = v->name;
01550
01551 if (!ast_strlen_zero(v->value)) {
01552
01553 queue_set_param(q, tmp_name, v->value, -1, 0);
01554 }
01555 }
01556
01557
01558
01559 mem_iter = ao2_iterator_init(q->members, 0);
01560 while ((m = ao2_iterator_next(&mem_iter))) {
01561 q->membercount++;
01562 if (m->realtime)
01563 m->dead = 1;
01564 ao2_ref(m, -1);
01565 }
01566
01567 while ((interface = ast_category_browse(member_config, interface))) {
01568 rt_handle_member_record(q, interface,
01569 ast_variable_retrieve(member_config, interface, "uniqueid"),
01570 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
01571 ast_variable_retrieve(member_config, interface, "penalty"),
01572 ast_variable_retrieve(member_config, interface, "paused"),
01573 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01574 }
01575
01576
01577 mem_iter = ao2_iterator_init(q->members, 0);
01578 while ((m = ao2_iterator_next(&mem_iter))) {
01579 if (m->dead) {
01580 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01581 ao2_unlink(q->members, m);
01582 remove_from_interfaces(m->state_interface, 0);
01583 q->membercount--;
01584 }
01585 ao2_ref(m, -1);
01586 }
01587
01588 ao2_unlock(q);
01589
01590 return q;
01591 }
01592
01593 static struct call_queue *load_realtime_queue(const char *queuename)
01594 {
01595 struct ast_variable *queue_vars;
01596 struct ast_config *member_config = NULL;
01597 struct call_queue *q = NULL, tmpq = {
01598 .name = queuename,
01599 };
01600
01601
01602 q = ao2_find(queues, &tmpq, OBJ_POINTER);
01603
01604 if (!q || q->realtime) {
01605
01606
01607
01608
01609
01610
01611
01612
01613
01614 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01615 if (queue_vars) {
01616 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01617 if (!member_config) {
01618 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01619 ast_variables_destroy(queue_vars);
01620 return NULL;
01621 }
01622 }
01623
01624 ao2_lock(queues);
01625 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01626 if (member_config)
01627 ast_config_destroy(member_config);
01628 if (queue_vars)
01629 ast_variables_destroy(queue_vars);
01630 ao2_unlock(queues);
01631
01632 } else {
01633 update_realtime_members(q);
01634 }
01635 return q;
01636 }
01637
01638 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
01639 {
01640 int ret = -1;
01641
01642 if (ast_strlen_zero(mem->rt_uniqueid))
01643 return ret;
01644
01645 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01646 ret = 0;
01647
01648 return ret;
01649 }
01650
01651
01652 static void update_realtime_members(struct call_queue *q)
01653 {
01654 struct ast_config *member_config = NULL;
01655 struct member *m;
01656 char *interface = NULL;
01657 struct ao2_iterator mem_iter;
01658
01659 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01660
01661 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01662 return;
01663 }
01664
01665 ao2_lock(queues);
01666 ao2_lock(q);
01667
01668
01669 mem_iter = ao2_iterator_init(q->members, 0);
01670 while ((m = ao2_iterator_next(&mem_iter))) {
01671 if (m->realtime)
01672 m->dead = 1;
01673 ao2_ref(m, -1);
01674 }
01675
01676 while ((interface = ast_category_browse(member_config, interface))) {
01677 rt_handle_member_record(q, interface,
01678 ast_variable_retrieve(member_config, interface, "uniqueid"),
01679 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01680 ast_variable_retrieve(member_config, interface, "penalty"),
01681 ast_variable_retrieve(member_config, interface, "paused"),
01682 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01683 }
01684
01685
01686 mem_iter = ao2_iterator_init(q->members, 0);
01687 while ((m = ao2_iterator_next(&mem_iter))) {
01688 if (m->dead) {
01689 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01690 ao2_unlink(q->members, m);
01691 remove_from_interfaces(m->state_interface, 0);
01692 q->membercount--;
01693 }
01694 ao2_ref(m, -1);
01695 }
01696 ao2_unlock(q);
01697 ao2_unlock(queues);
01698 ast_config_destroy(member_config);
01699 }
01700
01701 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule)
01702 {
01703 struct call_queue *q;
01704 struct queue_ent *cur, *prev = NULL;
01705 int res = -1;
01706 int pos = 0;
01707 int inserted = 0;
01708 enum queue_member_status status;
01709 int exit = 0;
01710
01711 if (!(q = load_realtime_queue(queuename)))
01712 return res;
01713
01714 ao2_lock(queues);
01715 ao2_lock(q);
01716
01717 copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
01718 qe->pr = AST_LIST_FIRST(&qe->qe_rules);
01719
01720
01721 while (!exit) {
01722 status = get_member_status(q, qe->max_penalty, qe->min_penalty);
01723 if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
01724 *reason = QUEUE_JOINEMPTY;
01725 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01726 *reason = QUEUE_JOINUNAVAIL;
01727 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01728 *reason = QUEUE_JOINUNAVAIL;
01729 else if (q->maxlen && (q->count >= q->maxlen))
01730 *reason = QUEUE_FULL;
01731 else {
01732
01733
01734
01735 inserted = 0;
01736 prev = NULL;
01737 cur = q->head;
01738 while (cur) {
01739
01740
01741
01742 if ((!inserted) && (qe->prio > cur->prio)) {
01743 insert_entry(q, prev, qe, &pos);
01744 inserted = 1;
01745 }
01746 cur->pos = ++pos;
01747 prev = cur;
01748 cur = cur->next;
01749 }
01750
01751 if (!inserted)
01752 insert_entry(q, prev, qe, &pos);
01753 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01754 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01755 ast_copy_string(qe->context, q->context, sizeof(qe->context));
01756 q->count++;
01757 res = 0;
01758 manager_event(EVENT_FLAG_CALL, "Join",
01759 "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01760 qe->chan->name,
01761 S_OR(qe->chan->cid.cid_num, "unknown"),
01762 S_OR(qe->chan->cid.cid_name, "unknown"),
01763 q->name, qe->pos, q->count, qe->chan->uniqueid );
01764 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01765 }
01766 if (!exit && qe->pr && res) {
01767
01768
01769
01770 update_qe_rule(qe);
01771 } else {
01772 exit = 1;
01773 }
01774 }
01775 ao2_unlock(q);
01776 ao2_unlock(queues);
01777
01778 return res;
01779 }
01780
01781 static int play_file(struct ast_channel *chan, const char *filename)
01782 {
01783 int res;
01784
01785 if (ast_strlen_zero(filename)) {
01786 return 0;
01787 }
01788
01789 ast_stopstream(chan);
01790
01791 res = ast_streamfile(chan, filename, chan->language);
01792 if (!res)
01793 res = ast_waitstream(chan, AST_DIGIT_ANY);
01794
01795 ast_stopstream(chan);
01796
01797 return res;
01798 }
01799
01800
01801
01802
01803
01804
01805 static int valid_exit(struct queue_ent *qe, char digit)
01806 {
01807 int digitlen = strlen(qe->digits);
01808
01809
01810 if (digitlen < sizeof(qe->digits) - 2) {
01811 qe->digits[digitlen] = digit;
01812 qe->digits[digitlen + 1] = '\0';
01813 } else {
01814 qe->digits[0] = '\0';
01815 return 0;
01816 }
01817
01818
01819 if (ast_strlen_zero(qe->context))
01820 return 0;
01821
01822
01823 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01824 qe->digits[0] = '\0';
01825 return 0;
01826 }
01827
01828
01829 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01830 qe->valid_digits = 1;
01831
01832 return 1;
01833 }
01834
01835 return 0;
01836 }
01837
01838 static int say_position(struct queue_ent *qe, int ringing)
01839 {
01840 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
01841 int say_thanks = 1;
01842 time_t now;
01843
01844
01845 time(&now);
01846 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
01847 return 0;
01848
01849
01850 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01851 return 0;
01852
01853 if (ringing) {
01854 ast_indicate(qe->chan,-1);
01855 } else {
01856 ast_moh_stop(qe->chan);
01857 }
01858
01859 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
01860 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
01861 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
01862 qe->pos <= qe->parent->announcepositionlimit))
01863 announceposition = 1;
01864
01865
01866 if (announceposition == 1) {
01867
01868 if (qe->pos == 1) {
01869 res = play_file(qe->chan, qe->parent->sound_next);
01870 if (res)
01871 goto playout;
01872 else
01873 goto posout;
01874 } else {
01875 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01876
01877 res = play_file(qe->chan, qe->parent->queue_quantity1);
01878 if (res)
01879 goto playout;
01880 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL);
01881 if (res)
01882 goto playout;
01883 } else {
01884
01885 res = play_file(qe->chan, qe->parent->sound_thereare);
01886 if (res)
01887 goto playout;
01888 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL);
01889 if (res)
01890 goto playout;
01891 }
01892 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01893
01894 res = play_file(qe->chan, qe->parent->queue_quantity2);
01895 if (res)
01896 goto playout;
01897 } else {
01898 res = play_file(qe->chan, qe->parent->sound_calls);
01899 if (res)
01900 goto playout;
01901 }
01902 }
01903 }
01904
01905 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01906
01907
01908 if (qe->parent->roundingseconds) {
01909 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01910 avgholdsecs *= qe->parent->roundingseconds;
01911 } else {
01912 avgholdsecs = 0;
01913 }
01914
01915 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01916
01917
01918
01919 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01920 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01921 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01922 res = play_file(qe->chan, qe->parent->sound_holdtime);
01923 if (res)
01924 goto playout;
01925
01926 if (avgholdmins >= 1) {
01927 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01928 if (res)
01929 goto playout;
01930
01931 if (avgholdmins == 1) {
01932 res = play_file(qe->chan, qe->parent->sound_minute);
01933 if (res)
01934 goto playout;
01935 } else {
01936 res = play_file(qe->chan, qe->parent->sound_minutes);
01937 if (res)
01938 goto playout;
01939 }
01940 }
01941 if (avgholdsecs >= 1) {
01942 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01943 if (res)
01944 goto playout;
01945
01946 res = play_file(qe->chan, qe->parent->sound_seconds);
01947 if (res)
01948 goto playout;
01949 }
01950 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
01951 say_thanks = 0;
01952 }
01953
01954 posout:
01955 if (qe->parent->announceposition) {
01956 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
01957 qe->chan->name, qe->parent->name, qe->pos);
01958 }
01959 if (say_thanks) {
01960 res = play_file(qe->chan, qe->parent->sound_thanks);
01961 }
01962 playout:
01963 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01964 res = 0;
01965
01966
01967 qe->last_pos = now;
01968 qe->last_pos_said = qe->pos;
01969
01970
01971 if (!res) {
01972 if (ringing) {
01973 ast_indicate(qe->chan, AST_CONTROL_RINGING);
01974 } else {
01975 ast_moh_start(qe->chan, qe->moh, NULL);
01976 }
01977 }
01978 return res;
01979 }
01980
01981 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01982 {
01983 int oldvalue;
01984
01985
01986
01987
01988
01989 ao2_lock(qe->parent);
01990 oldvalue = qe->parent->holdtime;
01991 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01992 ao2_unlock(qe->parent);
01993 }
01994
01995
01996
01997
01998
01999
02000 static void leave_queue(struct queue_ent *qe)
02001 {
02002 struct call_queue *q;
02003 struct queue_ent *current, *prev = NULL;
02004 struct penalty_rule *pr_iter;
02005 int pos = 0;
02006
02007 if (!(q = qe->parent))
02008 return;
02009 queue_ref(q);
02010 ao2_lock(q);
02011
02012 prev = NULL;
02013 for (current = q->head; current; current = current->next) {
02014 if (current == qe) {
02015 q->count--;
02016
02017
02018 manager_event(EVENT_FLAG_CALL, "Leave",
02019 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02020 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
02021 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02022
02023 if (prev)
02024 prev->next = current->next;
02025 else
02026 q->head = current->next;
02027
02028 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02029 ast_free(pr_iter);
02030 } else {
02031
02032 current->pos = ++pos;
02033 prev = current;
02034 }
02035 }
02036 ao2_unlock(q);
02037
02038
02039 if (q->realtime) {
02040 struct ast_variable *var;
02041 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02042 q->dead = 1;
02043 } else {
02044 ast_variables_destroy(var);
02045 }
02046 }
02047
02048 if (q->dead) {
02049
02050 ao2_unlink(queues, q);
02051 }
02052
02053 queue_unref(q);
02054 }
02055
02056
02057 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
02058 {
02059 struct callattempt *oo;
02060
02061 while (outgoing) {
02062
02063 if (outgoing->chan && (outgoing->chan != exception))
02064 ast_hangup(outgoing->chan);
02065 oo = outgoing;
02066 outgoing = outgoing->q_next;
02067 if (oo->member)
02068 ao2_ref(oo->member, -1);
02069 ast_free(oo);
02070 }
02071 }
02072
02073
02074
02075
02076
02077
02078
02079
02080
02081 static int num_available_members(struct call_queue *q)
02082 {
02083 struct member *mem;
02084 int avl = 0;
02085 struct ao2_iterator mem_iter;
02086
02087 mem_iter = ao2_iterator_init(q->members, 0);
02088 while ((mem = ao2_iterator_next(&mem_iter))) {
02089 switch (mem->status) {
02090 case AST_DEVICE_INUSE:
02091 if (!q->ringinuse)
02092 break;
02093
02094 case AST_DEVICE_NOT_INUSE:
02095 case AST_DEVICE_UNKNOWN:
02096 if (!mem->paused) {
02097 avl++;
02098 }
02099 break;
02100 }
02101 ao2_ref(mem, -1);
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02114 break;
02115 }
02116 }
02117
02118 return avl;
02119 }
02120
02121
02122
02123 static int compare_weight(struct call_queue *rq, struct member *member)
02124 {
02125 struct call_queue *q;
02126 struct member *mem;
02127 int found = 0;
02128 struct ao2_iterator queue_iter;
02129
02130
02131
02132 queue_iter = ao2_iterator_init(queues, 0);
02133 while ((q = ao2_iterator_next(&queue_iter))) {
02134 if (q == rq) {
02135 queue_unref(q);
02136 continue;
02137 }
02138 ao2_lock(q);
02139 if (q->count && q->members) {
02140 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02141 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02142 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02143 ast_debug(1, "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);
02144 found = 1;
02145 }
02146 ao2_ref(mem, -1);
02147 }
02148 }
02149 ao2_unlock(q);
02150 queue_unref(q);
02151 if (found) {
02152 break;
02153 }
02154 }
02155 return found;
02156 }
02157
02158
02159 static void do_hang(struct callattempt *o)
02160 {
02161 o->stillgoing = 0;
02162 ast_hangup(o->chan);
02163 o->chan = NULL;
02164 }
02165
02166
02167 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
02168 {
02169 struct ast_str *buf = ast_str_alloca(len + 1);
02170 char *tmp;
02171
02172 if (pbx_builtin_serialize_variables(chan, &buf)) {
02173 int i, j;
02174
02175
02176 strcpy(vars, "Variable: ");
02177 tmp = buf->str;
02178
02179 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02180 vars[j] = tmp[i];
02181
02182 if (tmp[i + 1] == '\0')
02183 break;
02184 if (tmp[i] == '\n') {
02185 vars[j++] = '\r';
02186 vars[j++] = '\n';
02187
02188 ast_copy_string(&(vars[j]), "Variable: ", len - j);
02189 j += 9;
02190 }
02191 }
02192 if (j > len - 3)
02193 j = len - 3;
02194 vars[j++] = '\r';
02195 vars[j++] = '\n';
02196 vars[j] = '\0';
02197 } else {
02198
02199 *vars = '\0';
02200 }
02201 return vars;
02202 }
02203
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214
02215
02216
02217
02218 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
02219 {
02220 int res;
02221 int status;
02222 char tech[256];
02223 char *location;
02224 const char *macrocontext, *macroexten;
02225
02226
02227 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02228 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02229 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
02230 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02231 if (qe->chan->cdr)
02232 ast_cdr_busy(qe->chan->cdr);
02233 tmp->stillgoing = 0;
02234 (*busies)++;
02235 return 0;
02236 }
02237
02238 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02239 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02240 if (qe->chan->cdr)
02241 ast_cdr_busy(qe->chan->cdr);
02242 tmp->stillgoing = 0;
02243 return 0;
02244 }
02245
02246 if (tmp->member->paused) {
02247 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02248 if (qe->chan->cdr)
02249 ast_cdr_busy(qe->chan->cdr);
02250 tmp->stillgoing = 0;
02251 return 0;
02252 }
02253 if (use_weight && compare_weight(qe->parent,tmp->member)) {
02254 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02255 if (qe->chan->cdr)
02256 ast_cdr_busy(qe->chan->cdr);
02257 tmp->stillgoing = 0;
02258 (*busies)++;
02259 return 0;
02260 }
02261
02262 ast_copy_string(tech, tmp->interface, sizeof(tech));
02263 if ((location = strchr(tech, '/')))
02264 *location++ = '\0';
02265 else
02266 location = "";
02267
02268
02269 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02270 if (!tmp->chan) {
02271 if (qe->chan->cdr)
02272 ast_cdr_busy(qe->chan->cdr);
02273 tmp->stillgoing = 0;
02274
02275 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02276
02277 ao2_lock(qe->parent);
02278 qe->parent->rrpos++;
02279 qe->linpos++;
02280 ao2_unlock(qe->parent);
02281
02282
02283 (*busies)++;
02284 return 0;
02285 }
02286
02287 tmp->chan->appl = "AppQueue";
02288 tmp->chan->data = "(Outgoing Line)";
02289 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02290 if (tmp->chan->cid.cid_num)
02291 ast_free(tmp->chan->cid.cid_num);
02292 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02293 if (tmp->chan->cid.cid_name)
02294 ast_free(tmp->chan->cid.cid_name);
02295 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02296 if (tmp->chan->cid.cid_ani)
02297 ast_free(tmp->chan->cid.cid_ani);
02298 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02299
02300
02301 ast_channel_inherit_variables(qe->chan, tmp->chan);
02302
02303
02304 tmp->chan->adsicpe = qe->chan->adsicpe;
02305
02306
02307 ast_channel_lock(qe->chan);
02308 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02309 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02310 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02311 if (!ast_strlen_zero(macroexten))
02312 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02313 else
02314 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02315 ast_channel_unlock(qe->chan);
02316
02317
02318 if ((res = ast_call(tmp->chan, location, 0))) {
02319
02320 ast_debug(1, "ast call on peer returned %d\n", res);
02321 ast_verb(3, "Couldn't call %s\n", tmp->interface);
02322 do_hang(tmp);
02323 (*busies)++;
02324 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02325 return 0;
02326 } else if (qe->parent->eventwhencalled) {
02327 char vars[2048];
02328
02329 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02330 "Queue: %s\r\n"
02331 "AgentCalled: %s\r\n"
02332 "AgentName: %s\r\n"
02333 "ChannelCalling: %s\r\n"
02334 "DestinationChannel: %s\r\n"
02335 "CallerIDNum: %s\r\n"
02336 "CallerIDName: %s\r\n"
02337 "Context: %s\r\n"
02338 "Extension: %s\r\n"
02339 "Priority: %d\r\n"
02340 "Uniqueid: %s\r\n"
02341 "%s",
02342 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02343 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02344 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02345 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02346 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02347 ast_verb(3, "Called %s\n", tmp->interface);
02348 }
02349
02350 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02351 return 1;
02352 }
02353
02354
02355 static struct callattempt *find_best(struct callattempt *outgoing)
02356 {
02357 struct callattempt *best = NULL, *cur;
02358
02359 for (cur = outgoing; cur; cur = cur->q_next) {
02360 if (cur->stillgoing &&
02361 !cur->chan &&
02362 (!best || cur->metric < best->metric)) {
02363 best = cur;
02364 }
02365 }
02366
02367 return best;
02368 }
02369
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
02381 {
02382 int ret = 0;
02383
02384 while (ret == 0) {
02385 struct callattempt *best = find_best(outgoing);
02386 if (!best) {
02387 ast_debug(1, "Nobody left to try ringing in queue\n");
02388 break;
02389 }
02390 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02391 struct callattempt *cur;
02392
02393 for (cur = outgoing; cur; cur = cur->q_next) {
02394 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02395 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02396 ret |= ring_entry(qe, cur, busies);
02397 }
02398 }
02399 } else {
02400
02401 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02402 ret = ring_entry(qe, best, busies);
02403 }
02404
02405
02406 if (qe->expire && (time(NULL) >= qe->expire)) {
02407 ast_debug(1, "Queue timed out while ringing members.\n");
02408 ret = 0;
02409 break;
02410 }
02411 }
02412
02413 return ret;
02414 }
02415
02416
02417 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
02418 {
02419 struct callattempt *best = find_best(outgoing);
02420
02421 if (best) {
02422
02423 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02424 qe->parent->rrpos = best->metric % 1000;
02425 } else {
02426
02427 if (qe->parent->wrapped) {
02428
02429 qe->parent->rrpos = 0;
02430 } else {
02431
02432 qe->parent->rrpos++;
02433 }
02434 }
02435 qe->parent->wrapped = 0;
02436
02437 return 0;
02438 }
02439
02440
02441 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
02442 {
02443 struct callattempt *best = find_best(outgoing);
02444
02445 if (best) {
02446
02447 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02448 qe->linpos = best->metric % 1000;
02449 } else {
02450
02451 if (qe->linwrapped) {
02452
02453 qe->linpos = 0;
02454 } else {
02455
02456 qe->linpos++;
02457 }
02458 }
02459 qe->linwrapped = 0;
02460
02461 return 0;
02462 }
02463
02464
02465 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
02466 {
02467 int res = 0;
02468 time_t now;
02469
02470
02471 time(&now);
02472
02473
02474 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02475 return 0;
02476
02477
02478 if (ringing)
02479 ast_indicate(qe->chan,-1);
02480 else
02481 ast_moh_stop(qe->chan);
02482
02483 ast_verb(3, "Playing periodic announcement\n");
02484
02485 if (qe->parent->randomperiodicannounce) {
02486 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02487 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
02488 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02489 qe->last_periodic_announce_sound = 0;
02490 }
02491
02492
02493 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02494
02495 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02496 res = 0;
02497
02498
02499 if (!res) {
02500 if (ringing)
02501 ast_indicate(qe->chan, AST_CONTROL_RINGING);
02502 else
02503 ast_moh_start(qe->chan, qe->moh, NULL);
02504 }
02505
02506
02507 qe->last_periodic_announce_time = now;
02508
02509
02510 if (!qe->parent->randomperiodicannounce) {
02511 qe->last_periodic_announce_sound++;
02512 }
02513
02514 return res;
02515 }
02516
02517
02518 static void record_abandoned(struct queue_ent *qe)
02519 {
02520 ao2_lock(qe->parent);
02521 set_queue_variables(qe->parent, qe->chan);
02522 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02523 "Queue: %s\r\n"
02524 "Uniqueid: %s\r\n"
02525 "Position: %d\r\n"
02526 "OriginalPosition: %d\r\n"
02527 "HoldTime: %d\r\n",
02528 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02529
02530 qe->parent->callsabandoned++;
02531 ao2_unlock(qe->parent);
02532 }
02533
02534
02535 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
02536 {
02537 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02538 if (qe->parent->eventwhencalled)
02539 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02540 "Queue: %s\r\n"
02541 "Uniqueid: %s\r\n"
02542 "Channel: %s\r\n"
02543 "Member: %s\r\n"
02544 "MemberName: %s\r\n"
02545 "Ringtime: %d\r\n",
02546 qe->parent->name,
02547 qe->chan->uniqueid,
02548 qe->chan->name,
02549 interface,
02550 membername,
02551 rnatime);
02552 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02553 if (qe->parent->autopause && pause) {
02554 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02555 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02556 } else {
02557 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02558 }
02559 }
02560 return;
02561 }
02562
02563 #define AST_MAX_WATCHERS 256
02564
02565
02566
02567
02568
02569
02570
02571
02572
02573
02574 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02575 {
02576 const char *queue = qe->parent->name;
02577 struct callattempt *o, *start = NULL, *prev = NULL;
02578 int status;
02579 int numbusies = prebusies;
02580 int numnochan = 0;
02581 int stillgoing = 0;
02582 int orig = *to;
02583 struct ast_frame *f;
02584 struct callattempt *peer = NULL;
02585 struct ast_channel *winner;
02586 struct ast_channel *in = qe->chan;
02587 char on[80] = "";
02588 char membername[80] = "";
02589 long starttime = 0;
02590 long endtime = 0;
02591 #ifdef HAVE_EPOLL
02592 struct callattempt *epollo;
02593 #endif
02594
02595 starttime = (long) time(NULL);
02596 #ifdef HAVE_EPOLL
02597 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02598 if (epollo->chan)
02599 ast_poll_channel_add(in, epollo->chan);
02600 }
02601 #endif
02602
02603 while (*to && !peer) {
02604 int numlines, retry, pos = 1;
02605 struct ast_channel *watchers[AST_MAX_WATCHERS];
02606 watchers[0] = in;
02607 start = NULL;
02608
02609 for (retry = 0; retry < 2; retry++) {
02610 numlines = 0;
02611 for (o = outgoing; o; o = o->q_next) {
02612 if (o->stillgoing) {
02613 stillgoing = 1;
02614 if (o->chan) {
02615 watchers[pos++] = o->chan;
02616 if (!start)
02617 start = o;
02618 else
02619 prev->call_next = o;
02620 prev = o;
02621 }
02622 }
02623 numlines++;
02624 }
02625 if (pos > 1 || !stillgoing ||
02626 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
02627 break;
02628
02629
02630 ring_one(qe, outgoing, &numbusies);
02631
02632 }
02633 if (pos == 1 ) {
02634 if (numlines == (numbusies + numnochan)) {
02635 ast_debug(1, "Everyone is busy at this time\n");
02636 } else {
02637 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02638 }
02639 *to = 0;
02640 return NULL;
02641 }
02642 winner = ast_waitfor_n(watchers, pos, to);
02643 for (o = start; o; o = o->call_next) {
02644 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
02645 if (!peer) {
02646 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02647 peer = o;
02648 }
02649 } else if (o->chan && (o->chan == winner)) {
02650
02651 ast_copy_string(on, o->member->interface, sizeof(on));
02652 ast_copy_string(membername, o->member->membername, sizeof(membername));
02653
02654 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02655 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02656 numnochan++;
02657 do_hang(o);
02658 winner = NULL;
02659 continue;
02660 } else if (!ast_strlen_zero(o->chan->call_forward)) {
02661 char tmpchan[256];
02662 char *stuff;
02663 char *tech;
02664
02665 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02666 if ((stuff = strchr(tmpchan, '/'))) {
02667 *stuff++ = '\0';
02668 tech = tmpchan;
02669 } else {
02670 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02671 stuff = tmpchan;
02672 tech = "Local";
02673 }
02674
02675 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02676
02677 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02678 if (!o->chan) {
02679 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02680 o->stillgoing = 0;
02681 numnochan++;
02682 } else {
02683 ast_channel_inherit_variables(in, o->chan);
02684 ast_channel_datastore_inherit(in, o->chan);
02685 if (o->chan->cid.cid_num)
02686 ast_free(o->chan->cid.cid_num);
02687 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02688
02689 if (o->chan->cid.cid_name)
02690 ast_free(o->chan->cid.cid_name);
02691 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02692
02693 ast_string_field_set(o->chan, accountcode, in->accountcode);
02694 o->chan->cdrflags = in->cdrflags;
02695
02696 if (in->cid.cid_ani) {
02697 if (o->chan->cid.cid_ani)
02698 ast_free(o->chan->cid.cid_ani);
02699 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02700 }
02701 if (o->chan->cid.cid_rdnis)
02702 ast_free(o->chan->cid.cid_rdnis);
02703 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02704 if (ast_call(o->chan, tmpchan, 0)) {
02705 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02706 do_hang(o);
02707 numnochan++;
02708 }
02709 }
02710
02711 ast_hangup(winner);
02712 continue;
02713 }
02714 f = ast_read(winner);
02715 if (f) {
02716 if (f->frametype == AST_FRAME_CONTROL) {
02717 switch (f->subclass) {
02718 case AST_CONTROL_ANSWER:
02719
02720 if (!peer) {
02721 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02722 peer = o;
02723 }
02724 break;
02725 case AST_CONTROL_BUSY:
02726 ast_verb(3, "%s is busy\n", o->chan->name);
02727 if (in->cdr)
02728 ast_cdr_busy(in->cdr);
02729 do_hang(o);
02730 endtime = (long) time(NULL);
02731 endtime -= starttime;
02732 rna(endtime * 1000, qe, on, membername, 0);
02733 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02734 if (qe->parent->timeoutrestart)
02735 *to = orig;
02736 ring_one(qe, outgoing, &numbusies);
02737 }
02738 numbusies++;
02739 break;
02740 case AST_CONTROL_CONGESTION:
02741 ast_verb(3, "%s is circuit-busy\n", o->chan->name);
02742 if (in->cdr)
02743 ast_cdr_busy(in->cdr);
02744 endtime = (long) time(NULL);
02745 endtime -= starttime;
02746 rna(endtime * 1000, qe, on, membername, 0);
02747 do_hang(o);
02748 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02749 if (qe->parent->timeoutrestart)
02750 *to = orig;
02751 ring_one(qe, outgoing, &numbusies);
02752 }
02753 numbusies++;
02754 break;
02755 case AST_CONTROL_RINGING:
02756 ast_verb(3, "%s is ringing\n", o->chan->name);
02757 break;
02758 case AST_CONTROL_OFFHOOK:
02759
02760 break;
02761 default:
02762 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
02763 }
02764 }
02765 ast_frfree(f);
02766 } else {
02767 endtime = (long) time(NULL) - starttime;
02768 rna(endtime * 1000, qe, on, membername, 1);
02769 do_hang(o);
02770 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02771 if (qe->parent->timeoutrestart)
02772 *to = orig;
02773 ring_one(qe, outgoing, &numbusies);
02774 }
02775 }
02776 }
02777 }
02778 if (winner == in) {
02779 f = ast_read(in);
02780 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02781
02782 *to = -1;
02783 if (f) {
02784 if (f->data.uint32) {
02785 in->hangupcause = f->data.uint32;
02786 }
02787 ast_frfree(f);
02788 }
02789 return NULL;
02790 }
02791 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02792 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
02793 *to = 0;
02794 ast_frfree(f);
02795 return NULL;
02796 }
02797 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02798 ast_verb(3, "User pressed digit: %c\n", f->subclass);
02799 *to = 0;
02800 *digit = f->subclass;
02801 ast_frfree(f);
02802 return NULL;
02803 }
02804 ast_frfree(f);
02805 }
02806 if (!*to) {
02807 for (o = start; o; o = o->call_next)
02808 rna(orig, qe, o->interface, o->member->membername, 1);
02809 }
02810 }
02811
02812 #ifdef HAVE_EPOLL
02813 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02814 if (epollo->chan)
02815 ast_poll_channel_del(in, epollo->chan);
02816 }
02817 #endif
02818
02819 return peer;
02820 }
02821
02822
02823
02824
02825
02826
02827
02828
02829
02830
02831
02832
02833 static int is_our_turn(struct queue_ent *qe)
02834 {
02835 struct queue_ent *ch;
02836 int res;
02837 int avl;
02838 int idx = 0;
02839
02840 ao2_lock(qe->parent);
02841
02842 avl = num_available_members(qe->parent);
02843
02844 ch = qe->parent->head;
02845
02846 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02847
02848 while ((idx < avl) && (ch) && (ch != qe)) {
02849 if (!ch->pending)
02850 idx++;
02851 ch = ch->next;
02852 }
02853
02854 ao2_unlock(qe->parent);
02855
02856
02857 if (ch && idx < avl) {
02858 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02859 res = 1;
02860 } else {
02861 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02862 res = 0;
02863 }
02864
02865 return res;
02866 }
02867
02868
02869
02870
02871
02872
02873
02874 static void update_qe_rule(struct queue_ent *qe)
02875 {
02876 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02877 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02878 char max_penalty_str[20], min_penalty_str[20];
02879
02880 if (max_penalty < 0)
02881 max_penalty = 0;
02882 if (min_penalty < 0)
02883 min_penalty = 0;
02884 if (min_penalty > max_penalty)
02885 min_penalty = max_penalty;
02886 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02887 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02888 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02889 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02890 qe->max_penalty = max_penalty;
02891 qe->min_penalty = min_penalty;
02892 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
02893 qe->pr = AST_LIST_NEXT(qe->pr, list);
02894 }
02895
02896
02897
02898
02899
02900
02901
02902
02903
02904
02905
02906 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02907 {
02908 int res = 0;
02909
02910
02911 for (;;) {
02912 enum queue_member_status status = QUEUE_NORMAL;
02913 int exit = 0;
02914
02915 if (is_our_turn(qe))
02916 break;
02917
02918
02919 if (qe->expire && (time(NULL) >= qe->expire)) {
02920 *reason = QUEUE_TIMEOUT;
02921 break;
02922 }
02923
02924
02925
02926
02927
02928 for (; !exit || qe->pr; update_qe_rule(qe)) {
02929 status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02930
02931 if (!qe->pr || status == QUEUE_NORMAL) {
02932 break;
02933 }
02934
02935
02936 if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
02937 ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02938 ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
02939 continue;
02940 } else {
02941 exit = 1;
02942 }
02943 }
02944
02945 if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
02946 *reason = QUEUE_LEAVEEMPTY;
02947 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02948 leave_queue(qe);
02949 break;
02950 }
02951
02952
02953 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02954 *reason = QUEUE_LEAVEUNAVAIL;
02955 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02956 leave_queue(qe);
02957 break;
02958 }
02959 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
02960 *reason = QUEUE_LEAVEUNAVAIL;
02961 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02962 leave_queue(qe);
02963 break;
02964 }
02965
02966
02967 if (qe->parent->announcefrequency &&
02968 (res = say_position(qe,ringing)))
02969 break;
02970
02971
02972 if (qe->expire && (time(NULL) >= qe->expire)) {
02973 *reason = QUEUE_TIMEOUT;
02974 break;
02975 }
02976
02977
02978 if (qe->parent->periodicannouncefrequency &&
02979 (res = say_periodic_announcement(qe,ringing)))
02980 break;
02981
02982
02983 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
02984 update_qe_rule(qe);
02985 }
02986
02987
02988 if (qe->expire && (time(NULL) >= qe->expire)) {
02989 *reason = QUEUE_TIMEOUT;
02990 break;
02991 }
02992
02993
02994 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02995 if (res > 0 && !valid_exit(qe, res))
02996 res = 0;
02997 else
02998 break;
02999 }
03000
03001
03002 if (qe->expire && (time(NULL) >= qe->expire)) {
03003 *reason = QUEUE_TIMEOUT;
03004 break;
03005 }
03006 }
03007
03008 return res;
03009 }
03010
03011
03012
03013
03014
03015 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
03016 {
03017 struct member *mem;
03018 struct call_queue *qtmp;
03019 struct ao2_iterator queue_iter;
03020
03021 if (shared_lastcall) {
03022 queue_iter = ao2_iterator_init(queues, 0);
03023 while ((qtmp = ao2_iterator_next(&queue_iter))) {
03024 ao2_lock(qtmp);
03025 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03026 time(&mem->lastcall);
03027 mem->calls++;
03028 mem->lastqueue = q;
03029 ao2_ref(mem, -1);
03030 }
03031 ao2_unlock(qtmp);
03032 ao2_ref(qtmp, -1);
03033 }
03034 } else {
03035 ao2_lock(q);
03036 time(&member->lastcall);
03037 member->calls++;
03038 member->lastqueue = q;
03039 ao2_unlock(q);
03040 }
03041 ao2_lock(q);
03042 q->callscompleted++;
03043 if (callcompletedinsl)
03044 q->callscompletedinsl++;
03045 ao2_unlock(q);
03046 return 0;
03047 }
03048
03049
03050
03051
03052
03053
03054
03055
03056
03057 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
03058 {
03059 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03060 return -1;
03061
03062 switch (q->strategy) {
03063 case QUEUE_STRATEGY_RINGALL:
03064
03065 tmp->metric = mem->penalty * 1000000;
03066 break;
03067 case QUEUE_STRATEGY_LINEAR:
03068 if (pos < qe->linpos) {
03069 tmp->metric = 1000 + pos;
03070 } else {
03071 if (pos > qe->linpos)
03072
03073 qe->linwrapped = 1;
03074 tmp->metric = pos;
03075 }
03076 tmp->metric += mem->penalty * 1000000;
03077 break;
03078 case QUEUE_STRATEGY_RRMEMORY:
03079 if (pos < q->rrpos) {
03080 tmp->metric = 1000 + pos;
03081 } else {
03082 if (pos > q->rrpos)
03083
03084 q->wrapped = 1;
03085 tmp->metric = pos;
03086 }
03087 tmp->metric += mem->penalty * 1000000;
03088 break;
03089 case QUEUE_STRATEGY_RANDOM:
03090 tmp->metric = ast_random() % 1000;
03091 tmp->metric += mem->penalty * 1000000;
03092 break;
03093 case QUEUE_STRATEGY_WRANDOM:
03094 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03095 break;
03096 case QUEUE_STRATEGY_FEWESTCALLS:
03097 tmp->metric = mem->calls;
03098 tmp->metric += mem->penalty * 1000000;
03099 break;
03100 case QUEUE_STRATEGY_LEASTRECENT:
03101 if (!mem->lastcall)
03102 tmp->metric = 0;
03103 else
03104 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03105 tmp->metric += mem->penalty * 1000000;
03106 break;
03107 default:
03108 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03109 break;
03110 }
03111 return 0;
03112 }
03113
03114 enum agent_complete_reason {
03115 CALLER,
03116 AGENT,
03117 TRANSFER
03118 };
03119
03120
03121 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
03122 const struct ast_channel *peer, const struct member *member, time_t callstart,
03123 char *vars, size_t vars_len, enum agent_complete_reason rsn)
03124 {
03125 const char *reason = NULL;
03126
03127 if (!qe->parent->eventwhencalled)
03128 return;
03129
03130 switch (rsn) {
03131 case CALLER:
03132 reason = "caller";
03133 break;
03134 case AGENT:
03135 reason = "agent";
03136 break;
03137 case TRANSFER:
03138 reason = "transfer";
03139 break;
03140 }
03141
03142 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03143 "Queue: %s\r\n"
03144 "Uniqueid: %s\r\n"
03145 "Channel: %s\r\n"
03146 "Member: %s\r\n"
03147 "MemberName: %s\r\n"
03148 "HoldTime: %ld\r\n"
03149 "TalkTime: %ld\r\n"
03150 "Reason: %s\r\n"
03151 "%s",
03152 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03153 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03154 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03155 }
03156
03157 struct queue_transfer_ds {
03158 struct queue_ent *qe;
03159 struct member *member;
03160 time_t starttime;
03161 int callcompletedinsl;
03162 };
03163
03164 static void queue_transfer_destroy(void *data)
03165 {
03166 struct queue_transfer_ds *qtds = data;
03167 ast_free(qtds);
03168 }
03169
03170
03171
03172 static const struct ast_datastore_info queue_transfer_info = {
03173 .type = "queue_transfer",
03174 .chan_fixup = queue_transfer_fixup,
03175 .destroy = queue_transfer_destroy,
03176 };
03177
03178
03179
03180
03181
03182
03183
03184
03185
03186
03187 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
03188 {
03189 struct queue_transfer_ds *qtds = data;
03190 struct queue_ent *qe = qtds->qe;
03191 struct member *member = qtds->member;
03192 time_t callstart = qtds->starttime;
03193 int callcompletedinsl = qtds->callcompletedinsl;
03194 struct ast_datastore *datastore;
03195
03196 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03197 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03198 (long) (time(NULL) - callstart), qe->opos);
03199
03200 update_queue(qe->parent, member, callcompletedinsl);
03201
03202
03203 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03204 ast_channel_datastore_remove(old_chan, datastore);
03205 } else {
03206 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03207 }
03208 }
03209
03210
03211
03212
03213
03214
03215
03216
03217
03218 static int attended_transfer_occurred(struct ast_channel *chan)
03219 {
03220 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03221 }
03222
03223
03224
03225 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
03226 {
03227 struct ast_datastore *ds;
03228 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03229
03230 if (!qtds) {
03231 ast_log(LOG_WARNING, "Memory allocation error!\n");
03232 return NULL;
03233 }
03234
03235 ast_channel_lock(qe->chan);
03236 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03237 ast_channel_unlock(qe->chan);
03238 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03239 return NULL;
03240 }
03241
03242 qtds->qe = qe;
03243
03244 qtds->member = member;
03245 qtds->starttime = starttime;
03246 qtds->callcompletedinsl = callcompletedinsl;
03247 ds->data = qtds;
03248 ast_channel_datastore_add(qe->chan, ds);
03249 ast_channel_unlock(qe->chan);
03250 return ds;
03251 }
03252
03253 struct queue_end_bridge {
03254 struct call_queue *q;
03255 struct ast_channel *chan;
03256 };
03257
03258 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
03259 {
03260 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03261 ao2_ref(qeb, +1);
03262 qeb->chan = originator;
03263 }
03264
03265 static void end_bridge_callback(void *data)
03266 {
03267 struct queue_end_bridge *qeb = data;
03268 struct call_queue *q = qeb->q;
03269 struct ast_channel *chan = qeb->chan;
03270
03271 if (ao2_ref(qeb, -1) == 1) {
03272 ao2_lock(q);
03273 set_queue_variables(q, chan);
03274 ao2_unlock(q);
03275
03276 queue_unref(q);
03277 }
03278 }
03279
03280
03281
03282
03283
03284
03285
03286
03287
03288
03289
03290
03291
03292
03293
03294
03295
03296
03297
03298
03299
03300
03301
03302
03303
03304
03305
03306
03307 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
03308 {
03309 struct member *cur;
03310 struct callattempt *outgoing = NULL;
03311 int to, orig;
03312 char oldexten[AST_MAX_EXTENSION]="";
03313 char oldcontext[AST_MAX_CONTEXT]="";
03314 char queuename[256]="";
03315 char interfacevar[256]="";
03316 struct ast_channel *peer;
03317 struct ast_channel *which;
03318 struct callattempt *lpeer;
03319 struct member *member;
03320 struct ast_app *application;
03321 int res = 0, bridge = 0;
03322 int numbusies = 0;
03323 int x=0;
03324 char *announce = NULL;
03325 char digit = 0;
03326 time_t callstart;
03327 time_t now = time(NULL);
03328 struct ast_bridge_config bridge_config;
03329 char nondataquality = 1;
03330 char *agiexec = NULL;
03331 char *macroexec = NULL;
03332 char *gosubexec = NULL;
03333 int ret = 0;
03334 const char *monitorfilename;
03335 const char *monitor_exec;
03336 const char *monitor_options;
03337 char tmpid[256], tmpid2[256];
03338 char meid[1024], meid2[1024];
03339 char mixmonargs[1512];
03340 struct ast_app *mixmonapp = NULL;
03341 char *p;
03342 char vars[2048];
03343 int forwardsallowed = 1;
03344 int callcompletedinsl;
03345 struct ao2_iterator memi;
03346 struct ast_datastore *datastore, *transfer_ds;
03347 struct queue_end_bridge *queue_end_bridge = NULL;
03348
03349 ast_channel_lock(qe->chan);
03350 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03351 ast_channel_unlock(qe->chan);
03352
03353 memset(&bridge_config, 0, sizeof(bridge_config));
03354 tmpid[0] = 0;
03355 meid[0] = 0;
03356 time(&now);
03357
03358
03359
03360
03361
03362 if (qe->expire && now >= qe->expire) {
03363 res = 0;
03364 goto out;
03365 }
03366
03367 for (; options && *options; options++)
03368 switch (*options) {
03369 case 't':
03370 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03371 break;
03372 case 'T':
03373 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03374 break;
03375 case 'w':
03376 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03377 break;
03378 case 'W':
03379 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03380 break;
03381 case 'c':
03382 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03383 break;
03384 case 'd':
03385 nondataquality = 0;
03386 break;
03387 case 'h':
03388 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03389 break;
03390 case 'H':
03391 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03392 break;
03393 case 'k':
03394 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03395 break;
03396 case 'K':
03397 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03398 break;
03399 case 'n':
03400 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03401 (*tries)++;
03402 else
03403 *tries = qe->parent->membercount;
03404 *noption = 1;
03405 break;
03406 case 'i':
03407 forwardsallowed = 0;
03408 break;
03409 case 'x':
03410 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03411 break;
03412 case 'X':
03413 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03414 break;
03415
03416 }
03417
03418
03419 if (use_weight)
03420 ao2_lock(queues);
03421 ao2_lock(qe->parent);
03422 ast_debug(1, "%s is trying to call a queue member.\n",
03423 qe->chan->name);
03424 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03425 if (!ast_strlen_zero(qe->announce))
03426 announce = qe->announce;
03427 if (!ast_strlen_zero(announceoverride))
03428 announce = announceoverride;
03429
03430 memi = ao2_iterator_init(qe->parent->members, 0);
03431 while ((cur = ao2_iterator_next(&memi))) {
03432 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03433 struct ast_dialed_interface *di;
03434 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03435 if (!tmp) {
03436 ao2_ref(cur, -1);
03437 ao2_unlock(qe->parent);
03438 if (use_weight)
03439 ao2_unlock(queues);
03440 goto out;
03441 }
03442 if (!datastore) {
03443 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03444 ao2_ref(cur, -1);
03445 ao2_unlock(qe->parent);
03446 if (use_weight)
03447 ao2_unlock(queues);
03448 free(tmp);
03449 goto out;
03450 }
03451 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03452 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03453 ao2_ref(cur, -1);
03454 ao2_unlock(&qe->parent);
03455 if (use_weight)
03456 ao2_unlock(queues);
03457 free(tmp);
03458 goto out;
03459 }
03460 datastore->data = dialed_interfaces;
03461 AST_LIST_HEAD_INIT(dialed_interfaces);
03462
03463 ast_channel_lock(qe->chan);
03464 ast_channel_datastore_add(qe->chan, datastore);
03465 ast_channel_unlock(qe->chan);
03466 } else
03467 dialed_interfaces = datastore->data;
03468
03469 AST_LIST_LOCK(dialed_interfaces);
03470 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03471 if (!strcasecmp(cur->interface, di->interface)) {
03472 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
03473 di->interface);
03474 break;
03475 }
03476 }
03477 AST_LIST_UNLOCK(dialed_interfaces);
03478
03479 if (di) {
03480 free(tmp);
03481 continue;
03482 }
03483
03484
03485
03486
03487
03488 if (strncasecmp(cur->interface, "Local/", 6)) {
03489 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03490 ao2_ref(cur, -1);
03491 ao2_unlock(qe->parent);
03492 if (use_weight)
03493 ao2_unlock(queues);
03494 free(tmp);
03495 goto out;
03496 }
03497 strcpy(di->interface, cur->interface);
03498
03499 AST_LIST_LOCK(dialed_interfaces);
03500 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03501 AST_LIST_UNLOCK(dialed_interfaces);
03502 }
03503
03504 tmp->stillgoing = -1;
03505 tmp->member = cur;
03506 tmp->oldstatus = cur->status;
03507 tmp->lastcall = cur->lastcall;
03508 tmp->lastqueue = cur->lastqueue;
03509 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03510
03511
03512 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03513
03514
03515
03516 tmp->q_next = outgoing;
03517 outgoing = tmp;
03518
03519 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03520 break;
03521 } else {
03522 ao2_ref(cur, -1);
03523 ast_free(tmp);
03524 }
03525 }
03526
03527 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03528
03529 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03530 to = (qe->expire - now) * 1000;
03531 else
03532 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03533 } else {
03534
03535 if (qe->expire && qe->expire<=now) {
03536 to = 0;
03537 } else if (qe->parent->timeout) {
03538 to = qe->parent->timeout * 1000;
03539 } else {
03540 to = -1;
03541 }
03542 }
03543 orig = to;
03544 ++qe->pending;
03545 ao2_unlock(qe->parent);
03546 ring_one(qe, outgoing, &numbusies);
03547 if (use_weight)
03548 ao2_unlock(queues);
03549 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03550
03551
03552
03553
03554
03555
03556 ast_channel_lock(qe->chan);
03557 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03558 ast_datastore_free(datastore);
03559 }
03560 ast_channel_unlock(qe->chan);
03561 ao2_lock(qe->parent);
03562 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03563 store_next_rr(qe, outgoing);
03564 }
03565 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03566 store_next_lin(qe, outgoing);
03567 }
03568 ao2_unlock(qe->parent);
03569 peer = lpeer ? lpeer->chan : NULL;
03570 if (!peer) {
03571 qe->pending = 0;
03572 if (to) {
03573
03574 res = -1;
03575 } else {
03576
03577 res = digit;
03578 }
03579 if (res == -1)
03580 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03581 } else {
03582
03583
03584
03585 if (!strcmp(qe->chan->tech->type, "DAHDI"))
03586 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03587 if (!strcmp(peer->tech->type, "DAHDI"))
03588 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03589
03590 time(&now);
03591 recalc_holdtime(qe, (now - qe->start));
03592 ao2_lock(qe->parent);
03593 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03594 ao2_unlock(qe->parent);
03595 member = lpeer->member;
03596
03597 ao2_ref(member, 1);
03598 hangupcalls(outgoing, peer);
03599 outgoing = NULL;
03600 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03601 int res2;
03602
03603 res2 = ast_autoservice_start(qe->chan);
03604 if (!res2) {
03605 if (qe->parent->memberdelay) {
03606 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03607 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03608 }
03609 if (!res2 && announce) {
03610 play_file(peer, announce);
03611 }
03612 if (!res2 && qe->parent->reportholdtime) {
03613 if (!play_file(peer, qe->parent->sound_reporthold)) {
03614 int holdtime, holdtimesecs;
03615
03616 time(&now);
03617 holdtime = abs((now - qe->start) / 60);
03618 holdtimesecs = abs((now - qe->start));
03619 if (holdtime == 1) {
03620 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03621 play_file(peer, qe->parent->sound_minute);
03622 } else {
03623 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03624 play_file(peer, qe->parent->sound_minutes);
03625 }
03626 if (holdtimesecs > 1) {
03627 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03628 play_file(peer, qe->parent->sound_seconds);
03629 }
03630 }
03631 }
03632 }
03633 res2 |= ast_autoservice_stop(qe->chan);
03634 if (ast_check_hangup(peer)) {
03635
03636 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03637 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03638 if (qe->parent->eventwhencalled)
03639 manager_event(EVENT_FLAG_AGENT, "AgentDump",
03640 "Queue: %s\r\n"
03641 "Uniqueid: %s\r\n"
03642 "Channel: %s\r\n"
03643 "Member: %s\r\n"
03644 "MemberName: %s\r\n"
03645 "%s",
03646 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03647 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03648 ast_hangup(peer);
03649 ao2_ref(member, -1);
03650 goto out;
03651 } else if (res2) {
03652
03653 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03654 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03655 record_abandoned(qe);
03656 ast_hangup(peer);
03657 ao2_ref(member, -1);
03658 return -1;
03659 }
03660 }
03661
03662 if (ringing)
03663 ast_indicate(qe->chan,-1);
03664 else
03665 ast_moh_stop(qe->chan);
03666
03667 if (qe->chan->cdr)
03668 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03669
03670 res = ast_channel_make_compatible(qe->chan, peer);
03671 if (res < 0) {
03672 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03673 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03674 record_abandoned(qe);
03675 ast_hangup(peer);
03676 ao2_ref(member, -1);
03677 return -1;
03678 }
03679
03680
03681 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03682 if (play_file(qe->chan, qe->parent->sound_callerannounce))
03683 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03684 }
03685
03686 ao2_lock(qe->parent);
03687
03688
03689 if (qe->parent->setinterfacevar) {
03690 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03691 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03692 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03693 pbx_builtin_setvar_multiple(peer, interfacevar);
03694 }
03695
03696
03697
03698 if (qe->parent->setqueueentryvar) {
03699 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03700 (long) time(NULL) - qe->start, qe->opos);
03701 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03702 pbx_builtin_setvar_multiple(peer, interfacevar);
03703 }
03704
03705
03706 set_queue_variables(qe->parent, qe->chan);
03707 set_queue_variables(qe->parent, peer);
03708 ao2_unlock(qe->parent);
03709
03710 ast_channel_lock(qe->chan);
03711 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03712 monitorfilename = ast_strdupa(monitorfilename);
03713 }
03714 ast_channel_unlock(qe->chan);
03715
03716 if (qe->parent->monfmt && *qe->parent->monfmt) {
03717 if (!qe->parent->montype) {
03718 const char *monexec, *monargs;
03719 ast_debug(1, "Starting Monitor as requested.\n");
03720 ast_channel_lock(qe->chan);
03721 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03722 which = qe->chan;
03723 monexec = monexec ? ast_strdupa(monexec) : NULL;
03724 }
03725 else
03726 which = peer;
03727 ast_channel_unlock(qe->chan);
03728 if (monitorfilename)
03729 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03730 else if (qe->chan->cdr)
03731 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03732 else {
03733
03734 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03735 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03736 }
03737 if (!ast_strlen_zero(monexec)) {
03738 ast_monitor_setjoinfiles(which, 1);
03739 }
03740 } else {
03741 mixmonapp = pbx_findapp("MixMonitor");
03742
03743 if (mixmonapp) {
03744 ast_debug(1, "Starting MixMonitor as requested.\n");
03745 if (!monitorfilename) {
03746 if (qe->chan->cdr)
03747 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03748 else
03749 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03750 } else {
03751 const char *m = monitorfilename;
03752 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
03753 switch (*m) {
03754 case '^':
03755 if (*(m + 1) == '{')
03756 *p = '$';
03757 break;
03758 case ',':
03759 *p++ = '\\';
03760
03761 default:
03762 *p = *m;
03763 }
03764 if (*m == '\0')
03765 break;
03766 }
03767 if (p == tmpid2 + sizeof(tmpid2))
03768 tmpid2[sizeof(tmpid2) - 1] = '\0';
03769
03770 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03771 }
03772
03773 ast_channel_lock(qe->chan);
03774 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03775 monitor_exec = ast_strdupa(monitor_exec);
03776 }
03777 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03778 monitor_options = ast_strdupa(monitor_options);
03779 } else {
03780 monitor_options = "";
03781 }
03782 ast_channel_unlock(qe->chan);
03783
03784 if (monitor_exec) {
03785 const char *m = monitor_exec;
03786 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03787 switch (*m) {
03788 case '^':
03789 if (*(m + 1) == '{')
03790 *p = '$';
03791 break;
03792 case ',':
03793 *p++ = '\\';
03794
03795 default:
03796 *p = *m;
03797 }
03798 if (*m == '\0')
03799 break;
03800 }
03801 if (p == meid2 + sizeof(meid2))
03802 meid2[sizeof(meid2) - 1] = '\0';
03803
03804 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03805 }
03806
03807 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03808
03809 if (!ast_strlen_zero(monitor_exec))
03810 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03811 else
03812 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03813
03814 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03815
03816 if (qe->chan->cdr)
03817 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03818 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03819 if (qe->chan->cdr)
03820 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03821
03822 } else {
03823 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03824 }
03825 }
03826 }
03827
03828 leave_queue(qe);
03829 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03830 ast_debug(1, "app_queue: sendurl=%s.\n", url);
03831 ast_channel_sendurl(peer, url);
03832 }
03833
03834
03835
03836 if (!ast_strlen_zero(macro)) {
03837 macroexec = ast_strdupa(macro);
03838 } else {
03839 if (qe->parent->membermacro)
03840 macroexec = ast_strdupa(qe->parent->membermacro);
03841 }
03842
03843 if (!ast_strlen_zero(macroexec)) {
03844 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03845
03846 res = ast_autoservice_start(qe->chan);
03847 if (res) {
03848 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03849 res = -1;
03850 }
03851
03852 application = pbx_findapp("Macro");
03853
03854 if (application) {
03855 res = pbx_exec(peer, application, macroexec);
03856 ast_debug(1, "Macro exited with status %d\n", res);
03857 res = 0;
03858 } else {
03859 ast_log(LOG_ERROR, "Could not find application Macro\n");
03860 res = -1;
03861 }
03862
03863 if (ast_autoservice_stop(qe->chan) < 0) {
03864 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03865 res = -1;
03866 }
03867 }
03868
03869
03870
03871 if (!ast_strlen_zero(gosub)) {
03872 gosubexec = ast_strdupa(gosub);
03873 } else {
03874 if (qe->parent->membergosub)
03875 gosubexec = ast_strdupa(qe->parent->membergosub);
03876 }
03877
03878 if (!ast_strlen_zero(gosubexec)) {
03879 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03880
03881 res = ast_autoservice_start(qe->chan);
03882 if (res) {
03883 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03884 res = -1;
03885 }
03886
03887 application = pbx_findapp("Gosub");
03888
03889 if (application) {
03890 char *gosub_args, *gosub_argstart;
03891
03892
03893 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03894 ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03895 peer->priority = 0;
03896
03897 gosub_argstart = strchr(gosubexec, ',');
03898 if (gosub_argstart) {
03899 *gosub_argstart = 0;
03900 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03901 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03902 gosub_args = NULL;
03903 }
03904 *gosub_argstart = ',';
03905 } else {
03906 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03907 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03908 gosub_args = NULL;
03909 }
03910 }
03911 if (gosub_args) {
03912 res = pbx_exec(peer, application, gosub_args);
03913 if (!res) {
03914 struct ast_pbx_args args;
03915 memset(&args, 0, sizeof(args));
03916 args.no_hangup_chan = 1;
03917 ast_pbx_run_args(peer, &args);
03918 }
03919 ast_free(gosub_args);
03920 ast_debug(1, "Gosub exited with status %d\n", res);
03921 } else {
03922 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
03923 }
03924 } else {
03925 ast_log(LOG_ERROR, "Could not find application Gosub\n");
03926 res = -1;
03927 }
03928
03929 if (ast_autoservice_stop(qe->chan) < 0) {
03930 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03931 res = -1;
03932 }
03933 }
03934
03935 if (!ast_strlen_zero(agi)) {
03936 ast_debug(1, "app_queue: agi=%s.\n", agi);
03937 application = pbx_findapp("agi");
03938 if (application) {
03939 agiexec = ast_strdupa(agi);
03940 ret = pbx_exec(qe->chan, application, agiexec);
03941 } else
03942 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03943 }
03944 qe->handled++;
03945 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
03946 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
03947 if (update_cdr && qe->chan->cdr)
03948 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
03949 if (qe->parent->eventwhencalled)
03950 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03951 "Queue: %s\r\n"
03952 "Uniqueid: %s\r\n"
03953 "Channel: %s\r\n"
03954 "Member: %s\r\n"
03955 "MemberName: %s\r\n"
03956 "Holdtime: %ld\r\n"
03957 "BridgedChannel: %s\r\n"
03958 "Ringtime: %ld\r\n"
03959 "%s",
03960 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03961 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
03962 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03963 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03964 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03965
03966 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
03967 queue_end_bridge->q = qe->parent;
03968 queue_end_bridge->chan = qe->chan;
03969 bridge_config.end_bridge_callback = end_bridge_callback;
03970 bridge_config.end_bridge_callback_data = queue_end_bridge;
03971 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
03972
03973
03974
03975
03976 queue_ref(qe->parent);
03977 }
03978
03979 time(&callstart);
03980 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03981 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03982
03983
03984
03985
03986 ast_channel_lock(qe->chan);
03987 if (!attended_transfer_occurred(qe->chan)) {
03988 struct ast_datastore *tds;
03989 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03990 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03991 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03992 (long) (time(NULL) - callstart), qe->opos);
03993 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
03994 } else if (ast_check_hangup(qe->chan)) {
03995 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03996 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03997 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
03998 } else {
03999 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04000 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04001 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04002 }
04003 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
04004 ast_channel_datastore_remove(qe->chan, tds);
04005 }
04006 update_queue(qe->parent, member, callcompletedinsl);
04007 }
04008
04009 if (transfer_ds) {
04010 ast_datastore_free(transfer_ds);
04011 }
04012 ast_channel_unlock(qe->chan);
04013 ast_hangup(peer);
04014 res = bridge ? bridge : 1;
04015 ao2_ref(member, -1);
04016 }
04017 out:
04018 hangupcalls(outgoing, NULL);
04019
04020 return res;
04021 }
04022
04023 static int wait_a_bit(struct queue_ent *qe)
04024 {
04025
04026 int retrywait = qe->parent->retry * 1000;
04027
04028 int res = ast_waitfordigit(qe->chan, retrywait);
04029 if (res > 0 && !valid_exit(qe, res))
04030 res = 0;
04031
04032 return res;
04033 }
04034
04035 static struct member *interface_exists(struct call_queue *q, const char *interface)
04036 {
04037 struct member *mem;
04038 struct ao2_iterator mem_iter;
04039
04040 if (!q)
04041 return NULL;
04042
04043 mem_iter = ao2_iterator_init(q->members, 0);
04044 while ((mem = ao2_iterator_next(&mem_iter))) {
04045 if (!strcasecmp(interface, mem->interface))
04046 return mem;
04047 ao2_ref(mem, -1);
04048 }
04049
04050 return NULL;
04051 }
04052
04053
04054
04055
04056
04057
04058 static void dump_queue_members(struct call_queue *pm_queue)
04059 {
04060 struct member *cur_member;
04061 char value[PM_MAX_LEN];
04062 int value_len = 0;
04063 int res;
04064 struct ao2_iterator mem_iter;
04065
04066 memset(value, 0, sizeof(value));
04067
04068 if (!pm_queue)
04069 return;
04070
04071 mem_iter = ao2_iterator_init(pm_queue->members, 0);
04072 while ((cur_member = ao2_iterator_next(&mem_iter))) {
04073 if (!cur_member->dynamic) {
04074 ao2_ref(cur_member, -1);
04075 continue;
04076 }
04077
04078 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04079 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04080
04081 ao2_ref(cur_member, -1);
04082
04083 if (res != strlen(value + value_len)) {
04084 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04085 break;
04086 }
04087 value_len += res;
04088 }
04089
04090 if (value_len && !cur_member) {
04091 if (ast_db_put(pm_family, pm_queue->name, value))
04092 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04093 } else
04094
04095 ast_db_del(pm_family, pm_queue->name);
04096 }
04097
04098
04099
04100
04101
04102
04103
04104 static int remove_from_queue(const char *queuename, const char *interface)
04105 {
04106 struct call_queue *q, tmpq = {
04107 .name = queuename,
04108 };
04109 struct member *mem, tmpmem;
04110 int res = RES_NOSUCHQUEUE;
04111
04112 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04113 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04114 ao2_lock(queues);
04115 ao2_lock(q);
04116 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04117
04118 if (!mem->dynamic) {
04119 ao2_ref(mem, -1);
04120 ao2_unlock(q);
04121 queue_unref(q);
04122 ao2_unlock(queues);
04123 return RES_NOT_DYNAMIC;
04124 }
04125 q->membercount--;
04126 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04127 "Queue: %s\r\n"
04128 "Location: %s\r\n"
04129 "MemberName: %s\r\n",
04130 q->name, mem->interface, mem->membername);
04131 ao2_unlink(q->members, mem);
04132 remove_from_interfaces(mem->state_interface, 0);
04133 ao2_ref(mem, -1);
04134
04135 if (queue_persistent_members)
04136 dump_queue_members(q);
04137
04138 res = RES_OKAY;
04139 } else {
04140 res = RES_EXISTS;
04141 }
04142 ao2_unlock(q);
04143 ao2_unlock(queues);
04144 queue_unref(q);
04145 }
04146
04147 return res;
04148 }
04149
04150
04151
04152
04153
04154
04155
04156
04157 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
04158 {
04159 struct call_queue *q;
04160 struct member *new_member, *old_member;
04161 int res = RES_NOSUCHQUEUE;
04162
04163
04164
04165 if (!(q = load_realtime_queue(queuename)))
04166 return res;
04167
04168 ao2_lock(queues);
04169
04170 ao2_lock(q);
04171 if ((old_member = interface_exists(q, interface)) == NULL) {
04172 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04173 add_to_interfaces(new_member->state_interface);
04174 new_member->dynamic = 1;
04175 ao2_link(q->members, new_member);
04176 q->membercount++;
04177 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04178 "Queue: %s\r\n"
04179 "Location: %s\r\n"
04180 "MemberName: %s\r\n"
04181 "Membership: %s\r\n"
04182 "Penalty: %d\r\n"
04183 "CallsTaken: %d\r\n"
04184 "LastCall: %d\r\n"
04185 "Status: %d\r\n"
04186 "Paused: %d\r\n",
04187 q->name, new_member->interface, new_member->membername,
04188 "dynamic",
04189 new_member->penalty, new_member->calls, (int) new_member->lastcall,
04190 new_member->status, new_member->paused);
04191
04192 ao2_ref(new_member, -1);
04193 new_member = NULL;
04194
04195 if (dump)
04196 dump_queue_members(q);
04197
04198 res = RES_OKAY;
04199 } else {
04200 res = RES_OUTOFMEMORY;
04201 }
04202 } else {
04203 ao2_ref(old_member, -1);
04204 res = RES_EXISTS;
04205 }
04206 ao2_unlock(q);
04207 ao2_unlock(queues);
04208
04209 return res;
04210 }
04211
04212 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
04213 {
04214 int found = 0;
04215 struct call_queue *q;
04216 struct member *mem;
04217 struct ao2_iterator queue_iter;
04218 int failed;
04219
04220
04221
04222 if (ast_strlen_zero(queuename))
04223 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04224
04225 queue_iter = ao2_iterator_init(queues, 0);
04226 while ((q = ao2_iterator_next(&queue_iter))) {
04227 ao2_lock(q);
04228 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04229 if ((mem = interface_exists(q, interface))) {
04230 if (mem->paused == paused) {
04231 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04232 }
04233
04234 failed = 0;
04235 if (mem->realtime) {
04236 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04237 }
04238
04239 if (failed) {
04240 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04241 ao2_ref(mem, -1);
04242 ao2_unlock(q);
04243 continue;
04244 }
04245 found++;
04246 mem->paused = paused;
04247
04248 if (queue_persistent_members)
04249 dump_queue_members(q);
04250
04251 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04252
04253 if (!ast_strlen_zero(reason)) {
04254 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04255 "Queue: %s\r\n"
04256 "Location: %s\r\n"
04257 "MemberName: %s\r\n"
04258 "Paused: %d\r\n"
04259 "Reason: %s\r\n",
04260 q->name, mem->interface, mem->membername, paused, reason);
04261 } else {
04262 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04263 "Queue: %s\r\n"
04264 "Location: %s\r\n"
04265 "MemberName: %s\r\n"
04266 "Paused: %d\r\n",
04267 q->name, mem->interface, mem->membername, paused);
04268 }
04269 ao2_ref(mem, -1);
04270 }
04271 }
04272
04273 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04274 ao2_unlock(q);
04275 queue_unref(q);
04276 break;
04277 }
04278
04279 ao2_unlock(q);
04280 queue_unref(q);
04281 }
04282
04283 return found ? RESULT_SUCCESS : RESULT_FAILURE;
04284 }
04285
04286
04287 static int set_member_penalty(char *queuename, char *interface, int penalty)
04288 {
04289 int foundinterface = 0, foundqueue = 0;
04290 struct call_queue *q;
04291 struct member *mem;
04292 struct ao2_iterator queue_iter;
04293
04294 if (penalty < 0) {
04295 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04296 return RESULT_FAILURE;
04297 }
04298
04299 queue_iter = ao2_iterator_init(queues, 0);
04300 while ((q = ao2_iterator_next(&queue_iter))) {
04301 ao2_lock(q);
04302 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04303 foundqueue++;
04304 if ((mem = interface_exists(q, interface))) {
04305 foundinterface++;
04306 mem->penalty = penalty;
04307
04308 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04309 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04310 "Queue: %s\r\n"
04311 "Location: %s\r\n"
04312 "Penalty: %d\r\n",
04313 q->name, mem->interface, penalty);
04314 ao2_ref(mem, -1);
04315 }
04316 }
04317 ao2_unlock(q);
04318 queue_unref(q);
04319 }
04320
04321 if (foundinterface) {
04322 return RESULT_SUCCESS;
04323 } else if (!foundqueue) {
04324 ast_log (LOG_ERROR, "Invalid queuename\n");
04325 } else {
04326 ast_log (LOG_ERROR, "Invalid interface\n");
04327 }
04328
04329 return RESULT_FAILURE;
04330 }
04331
04332
04333
04334
04335 static int get_member_penalty(char *queuename, char *interface)
04336 {
04337 int foundqueue = 0, penalty;
04338 struct call_queue *q, tmpq = {
04339 .name = queuename,
04340 };
04341 struct member *mem;
04342
04343 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04344 foundqueue = 1;
04345 ao2_lock(q);
04346 if ((mem = interface_exists(q, interface))) {
04347 penalty = mem->penalty;
04348 ao2_ref(mem, -1);
04349 ao2_unlock(q);
04350 queue_unref(q);
04351 return penalty;
04352 }
04353 ao2_unlock(q);
04354 queue_unref(q);
04355 }
04356
04357
04358 if (foundqueue)
04359 ast_log (LOG_ERROR, "Invalid queuename\n");
04360 else
04361 ast_log (LOG_ERROR, "Invalid interface\n");
04362
04363 return RESULT_FAILURE;
04364 }
04365
04366
04367 static void reload_queue_members(void)
04368 {
04369 char *cur_ptr;
04370 const char *queue_name;
04371 char *member;
04372 char *interface;
04373 char *membername = NULL;
04374 char *state_interface;
04375 char *penalty_tok;
04376 int penalty = 0;
04377 char *paused_tok;
04378 int paused = 0;
04379 struct ast_db_entry *db_tree;
04380 struct ast_db_entry *entry;
04381 struct call_queue *cur_queue;
04382 char queue_data[PM_MAX_LEN];
04383
04384 ao2_lock(queues);
04385
04386
04387 db_tree = ast_db_gettree(pm_family, NULL);
04388 for (entry = db_tree; entry; entry = entry->next) {
04389
04390 queue_name = entry->key + strlen(pm_family) + 2;
04391
04392 {
04393 struct call_queue tmpq = {
04394 .name = queue_name,
04395 };
04396 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04397 }
04398
04399 if (!cur_queue)
04400 cur_queue = load_realtime_queue(queue_name);
04401
04402 if (!cur_queue) {
04403
04404
04405 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04406 ast_db_del(pm_family, queue_name);
04407 continue;
04408 }
04409
04410 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04411 queue_unref(cur_queue);
04412 continue;
04413 }
04414
04415 cur_ptr = queue_data;
04416 while ((member = strsep(&cur_ptr, ",|"))) {
04417 if (ast_strlen_zero(member))
04418 continue;
04419
04420 interface = strsep(&member, ";");
04421 penalty_tok = strsep(&member, ";");
04422 paused_tok = strsep(&member, ";");
04423 membername = strsep(&member, ";");
04424 state_interface = strsep(&member, ";");
04425
04426 if (!penalty_tok) {
04427 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04428 break;
04429 }
04430 penalty = strtol(penalty_tok, NULL, 10);
04431 if (errno == ERANGE) {
04432 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04433 break;
04434 }
04435
04436 if (!paused_tok) {
04437 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04438 break;
04439 }
04440 paused = strtol(paused_tok, NULL, 10);
04441 if ((errno == ERANGE) || paused < 0 || paused > 1) {
04442 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04443 break;
04444 }
04445
04446 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
04447
04448 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04449 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04450 break;
04451 }
04452 }
04453 queue_unref(cur_queue);
04454 }
04455
04456 ao2_unlock(queues);
04457 if (db_tree) {
04458 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04459 ast_db_freetree(db_tree);
04460 }
04461 }
04462
04463
04464 static int pqm_exec(struct ast_channel *chan, void *data)
04465 {
04466 char *parse;
04467 AST_DECLARE_APP_ARGS(args,
04468 AST_APP_ARG(queuename);
04469 AST_APP_ARG(interface);
04470 AST_APP_ARG(options);
04471 AST_APP_ARG(reason);
04472 );
04473
04474 if (ast_strlen_zero(data)) {
04475 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04476 return -1;
04477 }
04478
04479 parse = ast_strdupa(data);
04480
04481 AST_STANDARD_APP_ARGS(args, parse);
04482
04483 if (ast_strlen_zero(args.interface)) {
04484 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04485 return -1;
04486 }
04487
04488 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04489 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04490 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04491 return 0;
04492 }
04493
04494 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04495
04496 return 0;
04497 }
04498
04499
04500 static int upqm_exec(struct ast_channel *chan, void *data)
04501 {
04502 char *parse;
04503 AST_DECLARE_APP_ARGS(args,
04504 AST_APP_ARG(queuename);
04505 AST_APP_ARG(interface);
04506 AST_APP_ARG(options);
04507 AST_APP_ARG(reason);
04508 );
04509
04510 if (ast_strlen_zero(data)) {
04511 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04512 return -1;
04513 }
04514
04515 parse = ast_strdupa(data);
04516
04517 AST_STANDARD_APP_ARGS(args, parse);
04518
04519 if (ast_strlen_zero(args.interface)) {
04520 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04521 return -1;
04522 }
04523
04524 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04525 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04526 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04527 return 0;
04528 }
04529
04530 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04531
04532 return 0;
04533 }
04534
04535
04536 static int rqm_exec(struct ast_channel *chan, void *data)
04537 {
04538 int res=-1;
04539 char *parse, *temppos = NULL;
04540 AST_DECLARE_APP_ARGS(args,
04541 AST_APP_ARG(queuename);
04542 AST_APP_ARG(interface);
04543 AST_APP_ARG(options);
04544 );
04545
04546
04547 if (ast_strlen_zero(data)) {
04548 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04549 return -1;
04550 }
04551
04552 parse = ast_strdupa(data);
04553
04554 AST_STANDARD_APP_ARGS(args, parse);
04555
04556 if (ast_strlen_zero(args.interface)) {
04557 args.interface = ast_strdupa(chan->name);
04558 temppos = strrchr(args.interface, '-');
04559 if (temppos)
04560 *temppos = '\0';
04561 }
04562
04563 switch (remove_from_queue(args.queuename, args.interface)) {
04564 case RES_OKAY:
04565 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04566 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04567 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04568 res = 0;
04569 break;
04570 case RES_EXISTS:
04571 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04572 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04573 res = 0;
04574 break;
04575 case RES_NOSUCHQUEUE:
04576 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04577 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04578 res = 0;
04579 break;
04580 case RES_NOT_DYNAMIC:
04581 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04582 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04583 res = 0;
04584 break;
04585 }
04586
04587 return res;
04588 }
04589
04590
04591 static int aqm_exec(struct ast_channel *chan, void *data)
04592 {
04593 int res=-1;
04594 char *parse, *temppos = NULL;
04595 AST_DECLARE_APP_ARGS(args,
04596 AST_APP_ARG(queuename);
04597 AST_APP_ARG(interface);
04598 AST_APP_ARG(penalty);
04599 AST_APP_ARG(options);
04600 AST_APP_ARG(membername);
04601 AST_APP_ARG(state_interface);
04602 );
04603 int penalty = 0;
04604
04605 if (ast_strlen_zero(data)) {
04606 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04607 return -1;
04608 }
04609
04610 parse = ast_strdupa(data);
04611
04612 AST_STANDARD_APP_ARGS(args, parse);
04613
04614 if (ast_strlen_zero(args.interface)) {
04615 args.interface = ast_strdupa(chan->name);
04616 temppos = strrchr(args.interface, '-');
04617 if (temppos)
04618 *temppos = '\0';
04619 }
04620
04621 if (!ast_strlen_zero(args.penalty)) {
04622 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
04623 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04624 penalty = 0;
04625 }
04626 }
04627
04628 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04629 case RES_OKAY:
04630 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04631 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04632 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04633 res = 0;
04634 break;
04635 case RES_EXISTS:
04636 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04637 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04638 res = 0;
04639 break;
04640 case RES_NOSUCHQUEUE:
04641 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04642 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04643 res = 0;
04644 break;
04645 case RES_OUTOFMEMORY:
04646 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04647 break;
04648 }
04649
04650 return res;
04651 }
04652
04653
04654 static int ql_exec(struct ast_channel *chan, void *data)
04655 {
04656 char *parse;
04657
04658 AST_DECLARE_APP_ARGS(args,
04659 AST_APP_ARG(queuename);
04660 AST_APP_ARG(uniqueid);
04661 AST_APP_ARG(membername);
04662 AST_APP_ARG(event);
04663 AST_APP_ARG(params);
04664 );
04665
04666 if (ast_strlen_zero(data)) {
04667 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04668 return -1;
04669 }
04670
04671 parse = ast_strdupa(data);
04672
04673 AST_STANDARD_APP_ARGS(args, parse);
04674
04675 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04676 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04677 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04678 return -1;
04679 }
04680
04681 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
04682 "%s", args.params ? args.params : "");
04683
04684 return 0;
04685 }
04686
04687
04688 static void copy_rules(struct queue_ent *qe, const char *rulename)
04689 {
04690 struct penalty_rule *pr_iter;
04691 struct rule_list *rl_iter;
04692 const char *tmp = rulename;
04693 if (ast_strlen_zero(tmp)) {
04694 return;
04695 }
04696 AST_LIST_LOCK(&rule_lists);
04697 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04698 if (!strcasecmp(rl_iter->name, tmp))
04699 break;
04700 }
04701 if (rl_iter) {
04702 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04703 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04704 if (!new_pr) {
04705 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04706 AST_LIST_UNLOCK(&rule_lists);
04707 break;
04708 }
04709 new_pr->time = pr_iter->time;
04710 new_pr->max_value = pr_iter->max_value;
04711 new_pr->min_value = pr_iter->min_value;
04712 new_pr->max_relative = pr_iter->max_relative;
04713 new_pr->min_relative = pr_iter->min_relative;
04714 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04715 }
04716 }
04717 AST_LIST_UNLOCK(&rule_lists);
04718 }
04719
04720
04721
04722
04723
04724
04725
04726
04727
04728
04729
04730
04731
04732 static int queue_exec(struct ast_channel *chan, void *data)
04733 {
04734 int res=-1;
04735 int ringing=0;
04736 const char *user_priority;
04737 const char *max_penalty_str;
04738 const char *min_penalty_str;
04739 int prio;
04740 int qcontinue = 0;
04741 int max_penalty, min_penalty;
04742 enum queue_result reason = QUEUE_UNKNOWN;
04743
04744 int tries = 0;
04745 int noption = 0;
04746 char *parse;
04747 int makeannouncement = 0;
04748 AST_DECLARE_APP_ARGS(args,
04749 AST_APP_ARG(queuename);
04750 AST_APP_ARG(options);
04751 AST_APP_ARG(url);
04752 AST_APP_ARG(announceoverride);
04753 AST_APP_ARG(queuetimeoutstr);
04754 AST_APP_ARG(agi);
04755 AST_APP_ARG(macro);
04756 AST_APP_ARG(gosub);
04757 AST_APP_ARG(rule);
04758 );
04759
04760 struct queue_ent qe;
04761
04762 if (ast_strlen_zero(data)) {
04763 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04764 return -1;
04765 }
04766
04767 parse = ast_strdupa(data);
04768 AST_STANDARD_APP_ARGS(args, parse);
04769
04770
04771 memset(&qe, 0, sizeof(qe));
04772 qe.start = time(NULL);
04773
04774
04775 if (!ast_strlen_zero(args.queuetimeoutstr))
04776 qe.expire = qe.start + atoi(args.queuetimeoutstr);
04777 else
04778 qe.expire = 0;
04779
04780
04781 ast_channel_lock(chan);
04782 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04783 if (user_priority) {
04784 if (sscanf(user_priority, "%d", &prio) == 1) {
04785 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04786 } else {
04787 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04788 user_priority, chan->name);
04789 prio = 0;
04790 }
04791 } else {
04792 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04793 prio = 0;
04794 }
04795
04796
04797
04798 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04799 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
04800 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04801 } else {
04802 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04803 max_penalty_str, chan->name);
04804 max_penalty = 0;
04805 }
04806 } else {
04807 max_penalty = 0;
04808 }
04809
04810 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04811 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
04812 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04813 } else {
04814 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04815 min_penalty_str, chan->name);
04816 min_penalty = 0;
04817 }
04818 } else {
04819 min_penalty = 0;
04820 }
04821 ast_channel_unlock(chan);
04822
04823 if (args.options && (strchr(args.options, 'r')))
04824 ringing = 1;
04825
04826 if (args.options && (strchr(args.options, 'c')))
04827 qcontinue = 1;
04828
04829 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04830 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04831
04832 qe.chan = chan;
04833 qe.prio = prio;
04834 qe.max_penalty = max_penalty;
04835 qe.min_penalty = min_penalty;
04836 qe.last_pos_said = 0;
04837 qe.last_pos = 0;
04838 qe.last_periodic_announce_time = time(NULL);
04839 qe.last_periodic_announce_sound = 0;
04840 qe.valid_digits = 0;
04841 if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04842 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04843 set_queue_result(chan, reason);
04844 return 0;
04845 }
04846 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04847 S_OR(chan->cid.cid_num, ""));
04848 check_turns:
04849 if (ringing) {
04850 ast_indicate(chan, AST_CONTROL_RINGING);
04851 } else {
04852 ast_moh_start(chan, qe.moh, NULL);
04853 }
04854
04855
04856 res = wait_our_turn(&qe, ringing, &reason);
04857 if (res) {
04858 goto stop;
04859 }
04860
04861 makeannouncement = 0;
04862
04863 for (;;) {
04864
04865
04866
04867
04868
04869 enum queue_member_status status = QUEUE_NORMAL;
04870 int exit = 0;
04871
04872
04873 if (qe.expire && (time(NULL) >= qe.expire)) {
04874 record_abandoned(&qe);
04875 reason = QUEUE_TIMEOUT;
04876 res = 0;
04877 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
04878 qe.pos, qe.opos, (long) time(NULL) - qe.start);
04879 break;
04880 }
04881
04882 if (makeannouncement) {
04883
04884 if (qe.parent->announcefrequency)
04885 if ((res = say_position(&qe,ringing)))
04886 goto stop;
04887 }
04888 makeannouncement = 1;
04889
04890
04891 if (qe.parent->periodicannouncefrequency)
04892 if ((res = say_periodic_announcement(&qe,ringing)))
04893 goto stop;
04894
04895
04896 if (qe.expire && (time(NULL) >= qe.expire)) {
04897 record_abandoned(&qe);
04898 reason = QUEUE_TIMEOUT;
04899 res = 0;
04900 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04901 break;
04902 }
04903
04904
04905 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04906 update_qe_rule(&qe);
04907 }
04908
04909
04910 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04911 if (res) {
04912 goto stop;
04913 }
04914
04915
04916 if (noption && tries >= qe.parent->membercount) {
04917 ast_verb(3, "Exiting on time-out cycle\n");
04918 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04919 record_abandoned(&qe);
04920 reason = QUEUE_TIMEOUT;
04921 res = 0;
04922 break;
04923 }
04924
04925 for (; !exit || qe.pr; update_qe_rule(&qe)) {
04926 status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04927
04928 if (!qe.pr || status == QUEUE_NORMAL) {
04929 break;
04930 }
04931
04932 if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
04933 ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04934 ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
04935 continue;
04936 } else {
04937 exit = 1;
04938 }
04939 }
04940
04941
04942 if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
04943 record_abandoned(&qe);
04944 reason = QUEUE_LEAVEEMPTY;
04945 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04946 res = 0;
04947 break;
04948 }
04949
04950
04951 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04952 record_abandoned(&qe);
04953 reason = QUEUE_LEAVEUNAVAIL;
04954 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04955 res = 0;
04956 break;
04957 }
04958 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
04959 record_abandoned(&qe);
04960 reason = QUEUE_LEAVEUNAVAIL;
04961 res = 0;
04962 break;
04963 }
04964
04965
04966 if (qe.expire && (time(NULL) >= qe.expire)) {
04967 record_abandoned(&qe);
04968 reason = QUEUE_TIMEOUT;
04969 res = 0;
04970 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
04971 break;
04972 }
04973
04974
04975 update_realtime_members(qe.parent);
04976
04977 res = wait_a_bit(&qe);
04978 if (res)
04979 goto stop;
04980
04981
04982
04983
04984
04985 if (!is_our_turn(&qe)) {
04986 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
04987 goto check_turns;
04988 }
04989 }
04990
04991 stop:
04992 if (res) {
04993 if (res < 0) {
04994 if (!qe.handled) {
04995 record_abandoned(&qe);
04996 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04997 "%d|%d|%ld", qe.pos, qe.opos,
04998 (long) time(NULL) - qe.start);
04999 res = -1;
05000 } else if (qcontinue) {
05001 reason = QUEUE_CONTINUE;
05002 res = 0;
05003 }
05004 } else if (qe.valid_digits) {
05005 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05006 "%s|%d", qe.digits, qe.pos);
05007 }
05008 }
05009
05010
05011 if (res >= 0) {
05012 res = 0;
05013 if (ringing) {
05014 ast_indicate(chan, -1);
05015 } else {
05016 ast_moh_stop(chan);
05017 }
05018 ast_stopstream(chan);
05019 }
05020
05021 set_queue_variables(qe.parent, qe.chan);
05022
05023 leave_queue(&qe);
05024 if (reason != QUEUE_UNKNOWN)
05025 set_queue_result(chan, reason);
05026
05027 return res;
05028 }
05029
05030
05031
05032
05033
05034
05035 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05036 {
05037 int res = -1;
05038 struct call_queue *q, tmpq = {
05039 .name = data,
05040 };
05041
05042 char interfacevar[256] = "";
05043 float sl = 0;
05044
05045 if (ast_strlen_zero(data)) {
05046 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05047 return -1;
05048 }
05049
05050 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05051 ao2_lock(q);
05052 if (q->setqueuevar) {
05053 sl = 0;
05054 res = 0;
05055
05056 if (q->callscompleted > 0) {
05057 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05058 }
05059
05060 snprintf(interfacevar, sizeof(interfacevar),
05061 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05062 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
05063
05064 pbx_builtin_setvar_multiple(chan, interfacevar);
05065 }
05066
05067 ao2_unlock(q);
05068 queue_unref(q);
05069 } else {
05070 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05071 }
05072
05073 snprintf(buf, len, "%d", res);
05074
05075 return 0;
05076 }
05077
05078
05079
05080
05081
05082
05083 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05084 {
05085 int count = 0;
05086 struct member *m;
05087 struct ao2_iterator mem_iter;
05088 struct call_queue *q;
05089 char *option;
05090
05091 if (ast_strlen_zero(data)) {
05092 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05093 return -1;
05094 }
05095
05096 if ((option = strchr(data, ',')))
05097 *option++ = '\0';
05098 else
05099 option = "logged";
05100 if ((q = load_realtime_queue(data))) {
05101 ao2_lock(q);
05102 if (!strcasecmp(option, "logged")) {
05103 mem_iter = ao2_iterator_init(q->members, 0);
05104 while ((m = ao2_iterator_next(&mem_iter))) {
05105
05106 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05107 count++;
05108 }
05109 ao2_ref(m, -1);
05110 }
05111 } else if (!strcasecmp(option, "free")) {
05112 mem_iter = ao2_iterator_init(q->members, 0);
05113 while ((m = ao2_iterator_next(&mem_iter))) {
05114
05115 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05116 count++;
05117 }
05118 ao2_ref(m, -1);
05119 }
05120 } else
05121 count = q->membercount;
05122 ao2_unlock(q);
05123 queue_unref(q);
05124 } else
05125 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05126
05127 snprintf(buf, len, "%d", count);
05128
05129 return 0;
05130 }
05131
05132
05133
05134
05135
05136
05137 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05138 {
05139 int count = 0;
05140 struct member *m;
05141 struct call_queue *q;
05142 struct ao2_iterator mem_iter;
05143 static int depflag = 1;
05144
05145 if (depflag) {
05146 depflag = 0;
05147 ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
05148 }
05149
05150 if (ast_strlen_zero(data)) {
05151 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05152 return -1;
05153 }
05154
05155 if ((q = load_realtime_queue(data))) {
05156 ao2_lock(q);
05157 mem_iter = ao2_iterator_init(q->members, 0);
05158 while ((m = ao2_iterator_next(&mem_iter))) {
05159
05160 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05161 count++;
05162 }
05163 ao2_ref(m, -1);
05164 }
05165 ao2_unlock(q);
05166 queue_unref(q);
05167 } else
05168 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05169
05170 snprintf(buf, len, "%d", count);
05171
05172 return 0;
05173 }
05174
05175
05176 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05177 {
05178 int count = 0;
05179 struct call_queue *q, tmpq = {
05180 .name = data,
05181 };
05182 struct ast_variable *var = NULL;
05183
05184 buf[0] = '\0';
05185
05186 if (ast_strlen_zero(data)) {
05187 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05188 return -1;
05189 }
05190
05191 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05192 ao2_lock(q);
05193 count = q->count;
05194 ao2_unlock(q);
05195 queue_unref(q);
05196 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05197
05198
05199
05200
05201 count = 0;
05202 ast_variables_destroy(var);
05203 } else
05204 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05205
05206 snprintf(buf, len, "%d", count);
05207
05208 return 0;
05209 }
05210
05211
05212 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05213 {
05214 struct call_queue *q, tmpq = {
05215 .name = data,
05216 };
05217 struct member *m;
05218
05219
05220 buf[0] = '\0';
05221
05222 if (ast_strlen_zero(data)) {
05223 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05224 return -1;
05225 }
05226
05227 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05228 int buflen = 0, count = 0;
05229 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05230
05231 ao2_lock(q);
05232 while ((m = ao2_iterator_next(&mem_iter))) {
05233
05234 if (count++) {
05235 strncat(buf + buflen, ",", len - buflen - 1);
05236 buflen++;
05237 }
05238 strncat(buf + buflen, m->membername, len - buflen - 1);
05239 buflen += strlen(m->membername);
05240
05241 if (buflen >= len - 2) {
05242 ao2_ref(m, -1);
05243 ast_log(LOG_WARNING, "Truncating list\n");
05244 break;
05245 }
05246 ao2_ref(m, -1);
05247 }
05248 ao2_unlock(q);
05249 queue_unref(q);
05250 } else
05251 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05252
05253
05254 buf[len - 1] = '\0';
05255
05256 return 0;
05257 }
05258
05259
05260 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05261 {
05262 int penalty;
05263 AST_DECLARE_APP_ARGS(args,
05264 AST_APP_ARG(queuename);
05265 AST_APP_ARG(interface);
05266 );
05267
05268 buf[0] = '\0';
05269
05270 if (ast_strlen_zero(data)) {
05271 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05272 return -1;
05273 }
05274
05275 AST_STANDARD_APP_ARGS(args, data);
05276
05277 if (args.argc < 2) {
05278 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05279 return -1;
05280 }
05281
05282 penalty = get_member_penalty (args.queuename, args.interface);
05283
05284 if (penalty >= 0)
05285 snprintf (buf, len, "%d", penalty);
05286
05287 return 0;
05288 }
05289
05290
05291 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
05292 {
05293 int penalty;
05294 AST_DECLARE_APP_ARGS(args,
05295 AST_APP_ARG(queuename);
05296 AST_APP_ARG(interface);
05297 );
05298
05299 if (ast_strlen_zero(data)) {
05300 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05301 return -1;
05302 }
05303
05304 AST_STANDARD_APP_ARGS(args, data);
05305
05306 if (args.argc < 2) {
05307 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05308 return -1;
05309 }
05310
05311 penalty = atoi(value);
05312
05313 if (ast_strlen_zero(args.interface)) {
05314 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05315 return -1;
05316 }
05317
05318
05319 if (set_member_penalty(args.queuename, args.interface, penalty)) {
05320 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05321 return -1;
05322 }
05323
05324 return 0;
05325 }
05326
05327 static struct ast_custom_function queuevar_function = {
05328 .name = "QUEUE_VARIABLES",
05329 .synopsis = "Return Queue information in variables",
05330 .syntax = "QUEUE_VARIABLES(<queuename>)",
05331 .desc =
05332 "Makes the following queue variables available.\n"
05333 "QUEUEMAX maxmimum number of calls allowed\n"
05334 "QUEUESTRATEGY the strategy of the queue\n"
05335 "QUEUECALLS number of calls currently in the queue\n"
05336 "QUEUEHOLDTIME current average hold time\n"
05337 "QUEUECOMPLETED number of completed calls for the queue\n"
05338 "QUEUEABANDONED number of abandoned calls\n"
05339 "QUEUESRVLEVEL queue service level\n"
05340 "QUEUESRVLEVELPERF current service level performance\n"
05341 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
05342 .read = queue_function_var,
05343 };
05344
05345 static struct ast_custom_function queuemembercount_function = {
05346 .name = "QUEUE_MEMBER",
05347 .synopsis = "Count number of members answering a queue",
05348 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
05349 .desc =
05350 "Returns the number of members currently associated with the specified queue.\n"
05351 "One of three options may be passed to determine the count returned:\n"
05352 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
05353 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
05354 "\"count\" - Returns the total number of members for the specified queue\n",
05355 .read = queue_function_qac,
05356 };
05357
05358 static struct ast_custom_function queuemembercount_dep = {
05359 .name = "QUEUE_MEMBER_COUNT",
05360 .synopsis = "Count number of members answering a queue",
05361 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
05362 .desc =
05363 "Returns the number of members currently associated with the specified queue.\n\n"
05364 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
05365 .read = queue_function_qac_dep,
05366 };
05367
05368 static struct ast_custom_function queuewaitingcount_function = {
05369 .name = "QUEUE_WAITING_COUNT",
05370 .synopsis = "Count number of calls currently waiting in a queue",
05371 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
05372 .desc =
05373 "Returns the number of callers currently waiting in the specified queue.\n",
05374 .read = queue_function_queuewaitingcount,
05375 };
05376
05377 static struct ast_custom_function queuememberlist_function = {
05378 .name = "QUEUE_MEMBER_LIST",
05379 .synopsis = "Returns a list of interfaces on a queue",
05380 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
05381 .desc =
05382 "Returns a comma-separated list of members associated with the specified queue.\n",
05383 .read = queue_function_queuememberlist,
05384 };
05385
05386 static struct ast_custom_function queuememberpenalty_function = {
05387 .name = "QUEUE_MEMBER_PENALTY",
05388 .synopsis = "Gets or sets queue members penalty.",
05389 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
05390 .desc =
05391 "Gets or sets queue members penalty\n",
05392 .read = queue_function_memberpenalty_read,
05393 .write = queue_function_memberpenalty_write,
05394 };
05395
05396 static int reload_queue_rules(int reload)
05397 {
05398 struct ast_config *cfg;
05399 struct rule_list *rl_iter, *new_rl;
05400 struct penalty_rule *pr_iter;
05401 char *rulecat = NULL;
05402 struct ast_variable *rulevar = NULL;
05403 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05404
05405 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05406 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05407 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05408 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05409 return AST_MODULE_LOAD_SUCCESS;
05410 } else {
05411 AST_LIST_LOCK(&rule_lists);
05412 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05413 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05414 ast_free(pr_iter);
05415 ast_free(rl_iter);
05416 }
05417 while ((rulecat = ast_category_browse(cfg, rulecat))) {
05418 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05419 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05420 AST_LIST_UNLOCK(&rule_lists);
05421 return AST_MODULE_LOAD_FAILURE;
05422 } else {
05423 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05424 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05425 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05426 if(!strcasecmp(rulevar->name, "penaltychange")) {
05427 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05428 } else {
05429 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05430 }
05431 }
05432 }
05433 AST_LIST_UNLOCK(&rule_lists);
05434 }
05435
05436 ast_config_destroy(cfg);
05437
05438 return AST_MODULE_LOAD_SUCCESS;
05439 }
05440
05441
05442 static int reload_queues(int reload)
05443 {
05444 struct call_queue *q;
05445 struct ast_config *cfg;
05446 char *cat, *tmp;
05447 struct ast_variable *var;
05448 struct member *cur, *newm;
05449 struct ao2_iterator mem_iter;
05450 int new;
05451 const char *general_val = NULL;
05452 char parse[80];
05453 char *interface, *state_interface;
05454 char *membername = NULL;
05455 int penalty;
05456 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05457 struct ao2_iterator queue_iter;
05458 AST_DECLARE_APP_ARGS(args,
05459 AST_APP_ARG(interface);
05460 AST_APP_ARG(penalty);
05461 AST_APP_ARG(membername);
05462 AST_APP_ARG(state_interface);
05463 );
05464
05465
05466 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05467 return AST_MODULE_LOAD_FAILURE;
05468
05469 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05470 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05471 return 0;
05472 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05473 return 0;
05474 ao2_lock(queues);
05475 use_weight=0;
05476
05477 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05478 while ((q = ao2_iterator_next(&queue_iter))) {
05479 if (!q->realtime) {
05480 q->dead = 1;
05481 q->found = 0;
05482 }
05483 queue_unref(q);
05484 }
05485
05486
05487 cat = NULL;
05488 while ((cat = ast_category_browse(cfg, cat)) ) {
05489 if (!strcasecmp(cat, "general")) {
05490
05491 queue_keep_stats = 0;
05492 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05493 queue_keep_stats = ast_true(general_val);
05494 queue_persistent_members = 0;
05495 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05496 queue_persistent_members = ast_true(general_val);
05497 autofill_default = 0;
05498 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05499 autofill_default = ast_true(general_val);
05500 montype_default = 0;
05501 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05502 if (!strcasecmp(general_val, "mixmonitor"))
05503 montype_default = 1;
05504 }
05505 update_cdr = 0;
05506 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05507 update_cdr = ast_true(general_val);
05508 shared_lastcall = 0;
05509 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05510 shared_lastcall = ast_true(general_val);
05511 } else {
05512
05513 struct call_queue tmpq = {
05514 .name = cat,
05515 };
05516 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05517
05518 if (!(q = alloc_queue(cat))) {
05519
05520 }
05521 new = 1;
05522 } else
05523 new = 0;
05524 if (q) {
05525 const char *tmpvar = NULL;
05526 if (!new)
05527 ao2_lock(q);
05528
05529 if (q->found) {
05530 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05531 if (!new) {
05532 ao2_unlock(q);
05533 queue_unref(q);
05534 }
05535 continue;
05536 }
05537
05538
05539
05540 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05541 q->strategy = strat2int(tmpvar);
05542 if (q->strategy < 0) {
05543 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05544 tmpvar, q->name);
05545 q->strategy = QUEUE_STRATEGY_RINGALL;
05546 }
05547 } else
05548 q->strategy = QUEUE_STRATEGY_RINGALL;
05549
05550 init_queue(q);
05551 if (!queue_keep_stats)
05552 clear_queue(q);
05553 mem_iter = ao2_iterator_init(q->members, 0);
05554 while ((cur = ao2_iterator_next(&mem_iter))) {
05555 if (!cur->dynamic) {
05556 cur->delme = 1;
05557 }
05558 ao2_ref(cur, -1);
05559 }
05560 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05561 if (!strcasecmp(var->name, "member")) {
05562 struct member tmpmem;
05563 membername = NULL;
05564
05565 if (ast_strlen_zero(var->value)) {
05566 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05567 continue;
05568 }
05569
05570
05571 ast_copy_string(parse, var->value, sizeof(parse));
05572
05573 AST_STANDARD_APP_ARGS(args, parse);
05574
05575 interface = args.interface;
05576 if (!ast_strlen_zero(args.penalty)) {
05577 tmp = args.penalty;
05578 while (*tmp && *tmp < 33) tmp++;
05579 penalty = atoi(tmp);
05580 if (penalty < 0) {
05581 penalty = 0;
05582 }
05583 } else
05584 penalty = 0;
05585
05586 if (!ast_strlen_zero(args.membername)) {
05587 membername = args.membername;
05588 while (*membername && *membername < 33) membername++;
05589 }
05590
05591 if (!ast_strlen_zero(args.state_interface)) {
05592 state_interface = args.state_interface;
05593 while (*state_interface && *state_interface < 33) state_interface++;
05594 } else
05595 state_interface = interface;
05596
05597
05598 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05599 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05600
05601 if (cur && strcasecmp(cur->state_interface, state_interface)) {
05602 remove_from_interfaces(cur->state_interface, 0);
05603 }
05604 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05605 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05606 add_to_interfaces(state_interface);
05607 ao2_link(q->members, newm);
05608 ao2_ref(newm, -1);
05609 newm = NULL;
05610
05611 if (cur)
05612 ao2_ref(cur, -1);
05613 else {
05614 q->membercount++;
05615 }
05616 } else {
05617 queue_set_param(q, var->name, var->value, var->lineno, 1);
05618 }
05619 }
05620
05621
05622 mem_iter = ao2_iterator_init(q->members, 0);
05623 while ((cur = ao2_iterator_next(&mem_iter))) {
05624 if (! cur->delme) {
05625 ao2_ref(cur, -1);
05626 continue;
05627 }
05628 q->membercount--;
05629 ao2_unlink(q->members, cur);
05630 remove_from_interfaces(cur->interface, 0);
05631 ao2_ref(cur, -1);
05632 }
05633
05634 if (new) {
05635 ao2_link(queues, q);
05636 } else
05637 ao2_unlock(q);
05638 queue_unref(q);
05639 }
05640 }
05641 }
05642 ast_config_destroy(cfg);
05643 queue_iter = ao2_iterator_init(queues, 0);
05644 while ((q = ao2_iterator_next(&queue_iter))) {
05645 if (q->dead) {
05646 ao2_unlink(queues, q);
05647 } else {
05648 ao2_lock(q);
05649 mem_iter = ao2_iterator_init(q->members, 0);
05650 while ((cur = ao2_iterator_next(&mem_iter))) {
05651 if (cur->dynamic)
05652 q->membercount++;
05653 cur->status = ast_device_state(cur->state_interface);
05654 ao2_ref(cur, -1);
05655 }
05656 ao2_unlock(q);
05657 }
05658 queue_unref(q);
05659 }
05660 ao2_unlock(queues);
05661 return 1;
05662 }
05663
05664
05665 static void do_print(struct mansession *s, int fd, const char *str)
05666 {
05667 if (s)
05668 astman_append(s, "%s\r\n", str);
05669 else
05670 ast_cli(fd, "%s\n", str);
05671 }
05672
05673
05674
05675
05676
05677
05678
05679 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
05680 {
05681 struct call_queue *q;
05682 struct ast_str *out = ast_str_alloca(240);
05683 int found = 0;
05684 time_t now = time(NULL);
05685 struct ao2_iterator queue_iter;
05686 struct ao2_iterator mem_iter;
05687
05688 if (argc != 2 && argc != 3)
05689 return CLI_SHOWUSAGE;
05690
05691 if (argc == 3) {
05692 if ((q = load_realtime_queue(argv[2]))) {
05693 queue_unref(q);
05694 }
05695 } else if (ast_check_realtime("queues")) {
05696
05697
05698
05699 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05700 char *queuename;
05701 if (cfg) {
05702 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05703 if ((q = load_realtime_queue(queuename))) {
05704 queue_unref(q);
05705 }
05706 }
05707 ast_config_destroy(cfg);
05708 }
05709 }
05710
05711 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05712 ao2_lock(queues);
05713 while ((q = ao2_iterator_next(&queue_iter))) {
05714 float sl;
05715 struct call_queue *realtime_queue = NULL;
05716
05717 ao2_lock(q);
05718
05719
05720
05721
05722 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05723 ao2_unlock(q);
05724 queue_unref(q);
05725 continue;
05726 } else if (q->realtime) {
05727 queue_unref(realtime_queue);
05728 }
05729 if (argc == 3 && strcasecmp(q->name, argv[2])) {
05730 ao2_unlock(q);
05731 queue_unref(q);
05732 continue;
05733 }
05734 found = 1;
05735
05736 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05737 if (q->maxlen)
05738 ast_str_append(&out, 0, "%d", q->maxlen);
05739 else
05740 ast_str_append(&out, 0, "unlimited");
05741 sl = 0;
05742 if (q->callscompleted > 0)
05743 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05744 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05745 int2strat(q->strategy), q->holdtime, q->weight,
05746 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05747 do_print(s, fd, out->str);
05748 if (!ao2_container_count(q->members))
05749 do_print(s, fd, " No Members");
05750 else {
05751 struct member *mem;
05752
05753 do_print(s, fd, " Members: ");
05754 mem_iter = ao2_iterator_init(q->members, 0);
05755 while ((mem = ao2_iterator_next(&mem_iter))) {
05756 ast_str_set(&out, 0, " %s", mem->membername);
05757 if (strcasecmp(mem->membername, mem->interface)) {
05758 ast_str_append(&out, 0, " (%s)", mem->interface);
05759 }
05760 if (mem->penalty)
05761 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05762 ast_str_append(&out, 0, "%s%s%s (%s)",
05763 mem->dynamic ? " (dynamic)" : "",
05764 mem->realtime ? " (realtime)" : "",
05765 mem->paused ? " (paused)" : "",
05766 devstate2str(mem->status));
05767 if (mem->calls)
05768 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05769 mem->calls, (long) (time(NULL) - mem->lastcall));
05770 else
05771 ast_str_append(&out, 0, " has taken no calls yet");
05772 do_print(s, fd, out->str);
05773 ao2_ref(mem, -1);
05774 }
05775 }
05776 if (!q->head)
05777 do_print(s, fd, " No Callers");
05778 else {
05779 struct queue_ent *qe;
05780 int pos = 1;
05781
05782 do_print(s, fd, " Callers: ");
05783 for (qe = q->head; qe; qe = qe->next) {
05784 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
05785 pos++, qe->chan->name, (long) (now - qe->start) / 60,
05786 (long) (now - qe->start) % 60, qe->prio);
05787 do_print(s, fd, out->str);
05788 }
05789 }
05790 do_print(s, fd, "");
05791 ao2_unlock(q);
05792 queue_unref(q);
05793 }
05794 ao2_unlock(queues);
05795 if (!found) {
05796 if (argc == 3)
05797 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05798 else
05799 ast_str_set(&out, 0, "No queues.");
05800 do_print(s, fd, out->str);
05801 }
05802 return CLI_SUCCESS;
05803 }
05804
05805 static char *complete_queue(const char *line, const char *word, int pos, int state)
05806 {
05807 struct call_queue *q;
05808 char *ret = NULL;
05809 int which = 0;
05810 int wordlen = strlen(word);
05811 struct ao2_iterator queue_iter;
05812
05813 queue_iter = ao2_iterator_init(queues, 0);
05814 while ((q = ao2_iterator_next(&queue_iter))) {
05815 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05816 ret = ast_strdup(q->name);
05817 queue_unref(q);
05818 break;
05819 }
05820 queue_unref(q);
05821 }
05822
05823 return ret;
05824 }
05825
05826 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
05827 {
05828 if (pos == 2)
05829 return complete_queue(line, word, pos, state);
05830 return NULL;
05831 }
05832
05833 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
05834 {
05835 switch ( cmd ) {
05836 case CLI_INIT:
05837 e->command = "queue show";
05838 e->usage =
05839 "Usage: queue show\n"
05840 " Provides summary information on a specified queue.\n";
05841 return NULL;
05842 case CLI_GENERATE:
05843 return complete_queue_show(a->line, a->word, a->pos, a->n);
05844 }
05845
05846 return __queues_show(NULL, a->fd, a->argc, a->argv);
05847 }
05848
05849
05850
05851
05852 static int manager_queues_show(struct mansession *s, const struct message *m)
05853 {
05854 char *a[] = { "queue", "show" };
05855
05856 __queues_show(s, -1, 2, a);
05857 astman_append(s, "\r\n\r\n");
05858
05859 return RESULT_SUCCESS;
05860 }
05861
05862 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
05863 {
05864 const char *rule = astman_get_header(m, "Rule");
05865 struct rule_list *rl_iter;
05866 struct penalty_rule *pr_iter;
05867
05868 AST_LIST_LOCK(&rule_lists);
05869 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05870 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05871 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05872 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05873 astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
05874 }
05875 if (!ast_strlen_zero(rule))
05876 break;
05877 }
05878 }
05879 AST_LIST_UNLOCK(&rule_lists);
05880
05881 astman_append(s, "\r\n\r\n");
05882
05883 return RESULT_SUCCESS;
05884 }
05885
05886
05887 static int manager_queues_summary(struct mansession *s, const struct message *m)
05888 {
05889 time_t now;
05890 int qmemcount = 0;
05891 int qmemavail = 0;
05892 int qchancount = 0;
05893 int qlongestholdtime = 0;
05894 const char *id = astman_get_header(m, "ActionID");
05895 const char *queuefilter = astman_get_header(m, "Queue");
05896 char idText[256] = "";
05897 struct call_queue *q;
05898 struct queue_ent *qe;
05899 struct member *mem;
05900 struct ao2_iterator queue_iter;
05901 struct ao2_iterator mem_iter;
05902
05903 astman_send_ack(s, m, "Queue summary will follow");
05904 time(&now);
05905 if (!ast_strlen_zero(id))
05906 snprintf(idText, 256, "ActionID: %s\r\n", id);
05907 queue_iter = ao2_iterator_init(queues, 0);
05908 while ((q = ao2_iterator_next(&queue_iter))) {
05909 ao2_lock(q);
05910
05911
05912 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05913
05914 qmemcount = 0;
05915 qmemavail = 0;
05916 qchancount = 0;
05917 qlongestholdtime = 0;
05918
05919
05920 mem_iter = ao2_iterator_init(q->members, 0);
05921 while ((mem = ao2_iterator_next(&mem_iter))) {
05922 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05923 ++qmemcount;
05924 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05925 ++qmemavail;
05926 }
05927 }
05928 ao2_ref(mem, -1);
05929 }
05930 for (qe = q->head; qe; qe = qe->next) {
05931 if ((now - qe->start) > qlongestholdtime) {
05932 qlongestholdtime = now - qe->start;
05933 }
05934 ++qchancount;
05935 }
05936 astman_append(s, "Event: QueueSummary\r\n"
05937 "Queue: %s\r\n"
05938 "LoggedIn: %d\r\n"
05939 "Available: %d\r\n"
05940 "Callers: %d\r\n"
05941 "HoldTime: %d\r\n"
05942 "LongestHoldTime: %d\r\n"
05943 "%s"
05944 "\r\n",
05945 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05946 }
05947 ao2_unlock(q);
05948 queue_unref(q);
05949 }
05950 astman_append(s,
05951 "Event: QueueSummaryComplete\r\n"
05952 "%s"
05953 "\r\n", idText);
05954
05955 return RESULT_SUCCESS;
05956 }
05957
05958
05959 static int manager_queues_status(struct mansession *s, const struct message *m)
05960 {
05961 time_t now;
05962 int pos;
05963 const char *id = astman_get_header(m,"ActionID");
05964 const char *queuefilter = astman_get_header(m,"Queue");
05965 const char *memberfilter = astman_get_header(m,"Member");
05966 char idText[256] = "";
05967 struct call_queue *q;
05968 struct queue_ent *qe;
05969 float sl = 0;
05970 struct member *mem;
05971 struct ao2_iterator queue_iter;
05972 struct ao2_iterator mem_iter;
05973
05974 astman_send_ack(s, m, "Queue status will follow");
05975 time(&now);
05976 if (!ast_strlen_zero(id))
05977 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05978
05979 queue_iter = ao2_iterator_init(queues, 0);
05980 while ((q = ao2_iterator_next(&queue_iter))) {
05981 ao2_lock(q);
05982
05983
05984 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05985 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05986 astman_append(s, "Event: QueueParams\r\n"
05987 "Queue: %s\r\n"
05988 "Max: %d\r\n"
05989 "Strategy: %s\r\n"
05990 "Calls: %d\r\n"
05991 "Holdtime: %d\r\n"
05992 "Completed: %d\r\n"
05993 "Abandoned: %d\r\n"
05994 "ServiceLevel: %d\r\n"
05995 "ServicelevelPerf: %2.1f\r\n"
05996 "Weight: %d\r\n"
05997 "%s"
05998 "\r\n",
05999 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06000 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06001
06002 mem_iter = ao2_iterator_init(q->members, 0);
06003 while ((mem = ao2_iterator_next(&mem_iter))) {
06004 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06005 astman_append(s, "Event: QueueMember\r\n"
06006 "Queue: %s\r\n"
06007 "Name: %s\r\n"
06008 "Location: %s\r\n"
06009 "Membership: %s\r\n"
06010 "Penalty: %d\r\n"
06011 "CallsTaken: %d\r\n"
06012 "LastCall: %d\r\n"
06013 "Status: %d\r\n"
06014 "Paused: %d\r\n"
06015 "%s"
06016 "\r\n",
06017 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06018 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06019 }
06020 ao2_ref(mem, -1);
06021 }
06022
06023 pos = 1;
06024 for (qe = q->head; qe; qe = qe->next) {
06025 astman_append(s, "Event: QueueEntry\r\n"
06026 "Queue: %s\r\n"
06027 "Position: %d\r\n"
06028 "Channel: %s\r\n"
06029 "CallerIDNum: %s\r\n"
06030 "CallerIDName: %s\r\n"
06031 "Wait: %ld\r\n"
06032 "%s"
06033 "\r\n",
06034 q->name, pos++, qe->chan->name,
06035 S_OR(qe->chan->cid.cid_num, "unknown"),
06036 S_OR(qe->chan->cid.cid_name, "unknown"),
06037 (long) (now - qe->start), idText);
06038 }
06039 }
06040 ao2_unlock(q);
06041 queue_unref(q);
06042 }
06043
06044 astman_append(s,
06045 "Event: QueueStatusComplete\r\n"
06046 "%s"
06047 "\r\n",idText);
06048
06049 return RESULT_SUCCESS;
06050 }
06051
06052 static int manager_add_queue_member(struct mansession *s, const struct message *m)
06053 {
06054 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06055 int paused, penalty = 0;
06056
06057 queuename = astman_get_header(m, "Queue");
06058 interface = astman_get_header(m, "Interface");
06059 penalty_s = astman_get_header(m, "Penalty");
06060 paused_s = astman_get_header(m, "Paused");
06061 membername = astman_get_header(m, "MemberName");
06062 state_interface = astman_get_header(m, "StateInterface");
06063
06064 if (ast_strlen_zero(queuename)) {
06065 astman_send_error(s, m, "'Queue' not specified.");
06066 return 0;
06067 }
06068
06069 if (ast_strlen_zero(interface)) {
06070 astman_send_error(s, m, "'Interface' not specified.");
06071 return 0;
06072 }
06073
06074 if (ast_strlen_zero(penalty_s))
06075 penalty = 0;
06076 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
06077 penalty = 0;
06078
06079 if (ast_strlen_zero(paused_s))
06080 paused = 0;
06081 else
06082 paused = abs(ast_true(paused_s));
06083
06084 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06085 case RES_OKAY:
06086 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06087 astman_send_ack(s, m, "Added interface to queue");
06088 break;
06089 case RES_EXISTS:
06090 astman_send_error(s, m, "Unable to add interface: Already there");
06091 break;
06092 case RES_NOSUCHQUEUE:
06093 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06094 break;
06095 case RES_OUTOFMEMORY:
06096 astman_send_error(s, m, "Out of memory");
06097 break;
06098 }
06099
06100 return 0;
06101 }
06102
06103 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
06104 {
06105 const char *queuename, *interface;
06106
06107 queuename = astman_get_header(m, "Queue");
06108 interface = astman_get_header(m, "Interface");
06109
06110 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06111 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06112 return 0;
06113 }
06114
06115 switch (remove_from_queue(queuename, interface)) {
06116 case RES_OKAY:
06117 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06118 astman_send_ack(s, m, "Removed interface from queue");
06119 break;
06120 case RES_EXISTS:
06121 astman_send_error(s, m, "Unable to remove interface: Not there");
06122 break;
06123 case RES_NOSUCHQUEUE:
06124 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06125 break;
06126 case RES_OUTOFMEMORY:
06127 astman_send_error(s, m, "Out of memory");
06128 break;
06129 case RES_NOT_DYNAMIC:
06130 astman_send_error(s, m, "Member not dynamic");
06131 break;
06132 }
06133
06134 return 0;
06135 }
06136
06137 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
06138 {
06139 const char *queuename, *interface, *paused_s, *reason;
06140 int paused;
06141
06142 interface = astman_get_header(m, "Interface");
06143 paused_s = astman_get_header(m, "Paused");
06144 queuename = astman_get_header(m, "Queue");
06145 reason = astman_get_header(m, "Reason");
06146
06147 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06148 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06149 return 0;
06150 }
06151
06152 paused = abs(ast_true(paused_s));
06153
06154 if (set_member_paused(queuename, interface, reason, paused))
06155 astman_send_error(s, m, "Interface not found");
06156 else
06157 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06158 return 0;
06159 }
06160
06161 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
06162 {
06163 const char *queuename, *event, *message, *interface, *uniqueid;
06164
06165 queuename = astman_get_header(m, "Queue");
06166 uniqueid = astman_get_header(m, "UniqueId");
06167 interface = astman_get_header(m, "Interface");
06168 event = astman_get_header(m, "Event");
06169 message = astman_get_header(m, "Message");
06170
06171 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06172 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06173 return 0;
06174 }
06175
06176 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06177 astman_send_ack(s, m, "Event added successfully");
06178
06179 return 0;
06180 }
06181
06182 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
06183 {
06184
06185 switch (pos) {
06186 case 3:
06187 return NULL;
06188 case 4:
06189 return state == 0 ? ast_strdup("to") : NULL;
06190 case 5:
06191 return complete_queue(line, word, pos, state);
06192 case 6:
06193 return state == 0 ? ast_strdup("penalty") : NULL;
06194 case 7:
06195 if (state < 100) {
06196 char *num;
06197 if ((num = ast_malloc(3))) {
06198 sprintf(num, "%d", state);
06199 }
06200 return num;
06201 } else {
06202 return NULL;
06203 }
06204 case 8:
06205 return state == 0 ? ast_strdup("as") : NULL;
06206 case 9:
06207 return NULL;
06208 default:
06209 return NULL;
06210 }
06211 }
06212
06213 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
06214 {
06215 const char *queuename, *interface, *penalty_s;
06216 int penalty;
06217
06218 interface = astman_get_header(m, "Interface");
06219 penalty_s = astman_get_header(m, "Penalty");
06220
06221 queuename = astman_get_header(m, "Queue");
06222
06223 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06224 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06225 return 0;
06226 }
06227
06228 penalty = atoi(penalty_s);
06229
06230 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06231 astman_send_error(s, m, "Invalid interface, queuename or penalty");
06232 else
06233 astman_send_ack(s, m, "Interface penalty set successfully");
06234
06235 return 0;
06236 }
06237
06238 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06239 {
06240 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06241 int penalty;
06242
06243 switch ( cmd ) {
06244 case CLI_INIT:
06245 e->command = "queue add member";
06246 e->usage =
06247 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06248 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n";
06249 return NULL;
06250 case CLI_GENERATE:
06251 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06252 }
06253
06254 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06255 return CLI_SHOWUSAGE;
06256 } else if (strcmp(a->argv[4], "to")) {
06257 return CLI_SHOWUSAGE;
06258 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06259 return CLI_SHOWUSAGE;
06260 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06261 return CLI_SHOWUSAGE;
06262 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06263 return CLI_SHOWUSAGE;
06264 }
06265
06266 queuename = a->argv[5];
06267 interface = a->argv[3];
06268 if (a->argc >= 8) {
06269 if (sscanf(a->argv[7], "%d", &penalty) == 1) {
06270 if (penalty < 0) {
06271 ast_cli(a->fd, "Penalty must be >= 0\n");
06272 penalty = 0;
06273 }
06274 } else {
06275 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06276 penalty = 0;
06277 }
06278 } else {
06279 penalty = 0;
06280 }
06281
06282 if (a->argc >= 10) {
06283 membername = a->argv[9];
06284 }
06285
06286 if (a->argc >= 12) {
06287 state_interface = a->argv[11];
06288 }
06289
06290 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06291 case RES_OKAY:
06292 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06293 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06294 return CLI_SUCCESS;
06295 case RES_EXISTS:
06296 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06297 return CLI_FAILURE;
06298 case RES_NOSUCHQUEUE:
06299 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06300 return CLI_FAILURE;
06301 case RES_OUTOFMEMORY:
06302 ast_cli(a->fd, "Out of memory\n");
06303 return CLI_FAILURE;
06304 case RES_NOT_DYNAMIC:
06305 ast_cli(a->fd, "Member not dynamic\n");
06306 return CLI_FAILURE;
06307 default:
06308 return CLI_FAILURE;
06309 }
06310 }
06311
06312 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
06313 {
06314 int which = 0;
06315 struct call_queue *q;
06316 struct member *m;
06317 struct ao2_iterator queue_iter;
06318 struct ao2_iterator mem_iter;
06319 int wordlen = strlen(word);
06320
06321
06322 if (pos > 5 || pos < 3)
06323 return NULL;
06324 if (pos == 4)
06325 return (state == 0 ? ast_strdup("from") : NULL);
06326
06327 if (pos == 5)
06328 return complete_queue(line, word, pos, state);
06329
06330
06331 queue_iter = ao2_iterator_init(queues, 0);
06332 while ((q = ao2_iterator_next(&queue_iter))) {
06333 ao2_lock(q);
06334 mem_iter = ao2_iterator_init(q->members, 0);
06335 while ((m = ao2_iterator_next(&mem_iter))) {
06336 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06337 char *tmp;
06338 ao2_unlock(q);
06339 tmp = ast_strdup(m->interface);
06340 ao2_ref(m, -1);
06341 queue_unref(q);
06342 return tmp;
06343 }
06344 ao2_ref(m, -1);
06345 }
06346 ao2_unlock(q);
06347 queue_unref(q);
06348 }
06349
06350 return NULL;
06351 }
06352
06353 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06354 {
06355 char *queuename, *interface;
06356
06357 switch (cmd) {
06358 case CLI_INIT:
06359 e->command = "queue remove member";
06360 e->usage =
06361 "Usage: queue remove member <channel> from <queue>\n"
06362 " Remove a specific channel from a queue.\n";
06363 return NULL;
06364 case CLI_GENERATE:
06365 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06366 }
06367
06368 if (a->argc != 6) {
06369 return CLI_SHOWUSAGE;
06370 } else if (strcmp(a->argv[4], "from")) {
06371 return CLI_SHOWUSAGE;
06372 }
06373
06374 queuename = a->argv[5];
06375 interface = a->argv[3];
06376
06377 switch (remove_from_queue(queuename, interface)) {
06378 case RES_OKAY:
06379 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06380 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06381 return CLI_SUCCESS;
06382 case RES_EXISTS:
06383 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06384 return CLI_FAILURE;
06385 case RES_NOSUCHQUEUE:
06386 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06387 return CLI_FAILURE;
06388 case RES_OUTOFMEMORY:
06389 ast_cli(a->fd, "Out of memory\n");
06390 return CLI_FAILURE;
06391 default:
06392 return CLI_FAILURE;
06393 }
06394 }
06395
06396 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
06397 {
06398
06399 switch (pos) {
06400 case 3:
06401 return NULL;
06402 case 4:
06403 return state == 0 ? ast_strdup("queue") : NULL;
06404 case 5:
06405 return complete_queue(line, word, pos, state);
06406 case 6:
06407 return state == 0 ? ast_strdup("reason") : NULL;
06408 case 7:
06409 return NULL;
06410 default:
06411 return NULL;
06412 }
06413 }
06414
06415 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06416 {
06417 char *queuename, *interface, *reason;
06418 int paused;
06419
06420 switch (cmd) {
06421 case CLI_INIT:
06422 e->command = "queue {pause|unpause} member";
06423 e->usage =
06424 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06425 " Pause or unpause a queue member. Not specifying a particular queue\n"
06426 " will pause or unpause a member across all queues to which the member\n"
06427 " belongs.\n";
06428 return NULL;
06429 case CLI_GENERATE:
06430 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06431 }
06432
06433 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06434 return CLI_SHOWUSAGE;
06435 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06436 return CLI_SHOWUSAGE;
06437 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06438 return CLI_SHOWUSAGE;
06439 }
06440
06441
06442 interface = a->argv[3];
06443 queuename = a->argc >= 6 ? a->argv[5] : NULL;
06444 reason = a->argc == 8 ? a->argv[7] : NULL;
06445 paused = !strcasecmp(a->argv[1], "pause");
06446
06447 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06448 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06449 if (!ast_strlen_zero(queuename))
06450 ast_cli(a->fd, " in queue '%s'", queuename);
06451 if (!ast_strlen_zero(reason))
06452 ast_cli(a->fd, " for reason '%s'", reason);
06453 ast_cli(a->fd, "\n");
06454 return CLI_SUCCESS;
06455 } else {
06456 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06457 if (!ast_strlen_zero(queuename))
06458 ast_cli(a->fd, " in queue '%s'", queuename);
06459 if (!ast_strlen_zero(reason))
06460 ast_cli(a->fd, " for reason '%s'", reason);
06461 ast_cli(a->fd, "\n");
06462 return CLI_FAILURE;
06463 }
06464 }
06465
06466 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
06467 {
06468
06469 switch (pos) {
06470 case 4:
06471 if (state == 0) {
06472 return ast_strdup("on");
06473 } else {
06474 return NULL;
06475 }
06476 case 6:
06477 if (state == 0) {
06478 return ast_strdup("in");
06479 } else {
06480 return NULL;
06481 }
06482 case 7:
06483 return complete_queue(line, word, pos, state);
06484 default:
06485 return NULL;
06486 }
06487 }
06488
06489 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06490 {
06491 char *queuename = NULL, *interface;
06492 int penalty = 0;
06493
06494 switch (cmd) {
06495 case CLI_INIT:
06496 e->command = "queue set penalty";
06497 e->usage =
06498 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06499 " Set a member's penalty in the queue specified. If no queue is specified\n"
06500 " then that interface's penalty is set in all queues to which that interface is a member\n";
06501 return NULL;
06502 case CLI_GENERATE:
06503 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06504 }
06505
06506 if (a->argc != 6 && a->argc != 8) {
06507 return CLI_SHOWUSAGE;
06508 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06509 return CLI_SHOWUSAGE;
06510 }
06511
06512 if (a->argc == 8)
06513 queuename = a->argv[7];
06514 interface = a->argv[5];
06515 penalty = atoi(a->argv[3]);
06516
06517 switch (set_member_penalty(queuename, interface, penalty)) {
06518 case RESULT_SUCCESS:
06519 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06520 return CLI_SUCCESS;
06521 case RESULT_FAILURE:
06522 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06523 return CLI_FAILURE;
06524 default:
06525 return CLI_FAILURE;
06526 }
06527 }
06528
06529 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
06530 {
06531 int which = 0;
06532 struct rule_list *rl_iter;
06533 int wordlen = strlen(word);
06534 char *ret = NULL;
06535 if (pos != 3) {
06536 return NULL;
06537 }
06538
06539 AST_LIST_LOCK(&rule_lists);
06540 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06541 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06542 ret = ast_strdup(rl_iter->name);
06543 break;
06544 }
06545 }
06546 AST_LIST_UNLOCK(&rule_lists);
06547
06548 return ret;
06549 }
06550
06551 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06552 {
06553 char *rule;
06554 struct rule_list *rl_iter;
06555 struct penalty_rule *pr_iter;
06556 switch (cmd) {
06557 case CLI_INIT:
06558 e->command = "queue rules show";
06559 e->usage =
06560 "Usage: queue rules show [rulename]\n"
06561 " Show the list of rules associated with rulename. If no\n"
06562 " rulename is specified, list all rules defined in queuerules.conf\n";
06563 return NULL;
06564 case CLI_GENERATE:
06565 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06566 }
06567
06568 if (a->argc != 3 && a->argc != 4)
06569 return CLI_SHOWUSAGE;
06570
06571 rule = a->argc == 4 ? a->argv[3] : "";
06572 AST_LIST_LOCK(&rule_lists);
06573 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06574 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06575 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06576 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06577 ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
06578 }
06579 }
06580 }
06581 AST_LIST_UNLOCK(&rule_lists);
06582 return CLI_SUCCESS;
06583 }
06584
06585 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06586 {
06587 switch (cmd) {
06588 case CLI_INIT:
06589 e->command = "queue rules reload";
06590 e->usage =
06591 "Usage: queue rules reload\n"
06592 " Reloads rules defined in queuerules.conf\n";
06593 return NULL;
06594 case CLI_GENERATE:
06595 return NULL;
06596 }
06597 reload_queue_rules(1);
06598 return CLI_SUCCESS;
06599 }
06600
06601 static const char qpm_cmd_usage[] =
06602 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
06603
06604 static const char qum_cmd_usage[] =
06605 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
06606
06607 static const char qsmp_cmd_usage[] =
06608 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
06609
06610 static struct ast_cli_entry cli_queue[] = {
06611 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
06612 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
06613 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
06614 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
06615 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
06616 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
06617 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
06618 };
06619
06620 static int unload_module(void)
06621 {
06622 int res;
06623 struct ast_context *con;
06624 struct ao2_iterator q_iter;
06625 struct call_queue *q = NULL;
06626
06627 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06628 res = ast_manager_unregister("QueueStatus");
06629 res |= ast_manager_unregister("Queues");
06630 res |= ast_manager_unregister("QueueRule");
06631 res |= ast_manager_unregister("QueueSummary");
06632 res |= ast_manager_unregister("QueueAdd");
06633 res |= ast_manager_unregister("QueueRemove");
06634 res |= ast_manager_unregister("QueuePause");
06635 res |= ast_manager_unregister("QueueLog");
06636 res |= ast_manager_unregister("QueuePenalty");
06637 res |= ast_unregister_application(app_aqm);
06638 res |= ast_unregister_application(app_rqm);
06639 res |= ast_unregister_application(app_pqm);
06640 res |= ast_unregister_application(app_upqm);
06641 res |= ast_unregister_application(app_ql);
06642 res |= ast_unregister_application(app);
06643 res |= ast_custom_function_unregister(&queuevar_function);
06644 res |= ast_custom_function_unregister(&queuemembercount_function);
06645 res |= ast_custom_function_unregister(&queuemembercount_dep);
06646 res |= ast_custom_function_unregister(&queuememberlist_function);
06647 res |= ast_custom_function_unregister(&queuewaitingcount_function);
06648 res |= ast_custom_function_unregister(&queuememberpenalty_function);
06649
06650 if (device_state_sub)
06651 ast_event_unsubscribe(device_state_sub);
06652
06653 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06654 ast_context_remove_extension2(con, "s", 1, NULL, 0);
06655 ast_context_destroy(con, "app_queue");
06656 }
06657
06658 clear_and_free_interfaces();
06659
06660 q_iter = ao2_iterator_init(queues, 0);
06661 while ((q = ao2_iterator_next(&q_iter))) {
06662 ao2_unlink(queues, q);
06663 queue_unref(q);
06664 }
06665 ao2_ref(queues, -1);
06666 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06667 ast_unload_realtime("queue_members");
06668 return res;
06669 }
06670
06671 static int load_module(void)
06672 {
06673 int res;
06674 struct ast_context *con;
06675
06676 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06677
06678 if (!reload_queues(0))
06679 return AST_MODULE_LOAD_DECLINE;
06680
06681 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06682 if (!con)
06683 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06684 else
06685 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06686
06687 if (queue_persistent_members)
06688 reload_queue_members();
06689
06690 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06691 res = ast_register_application(app, queue_exec, synopsis, descrip);
06692 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06693 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06694 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06695 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06696 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06697 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06698 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06699 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06700 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06701 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06702 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06703 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06704 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
06705 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06706 res |= ast_custom_function_register(&queuevar_function);
06707 res |= ast_custom_function_register(&queuemembercount_function);
06708 res |= ast_custom_function_register(&queuemembercount_dep);
06709 res |= ast_custom_function_register(&queuememberlist_function);
06710 res |= ast_custom_function_register(&queuewaitingcount_function);
06711 res |= ast_custom_function_register(&queuememberpenalty_function);
06712
06713 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06714 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06715 }
06716
06717 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06718 res = -1;
06719 }
06720
06721 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06722
06723 return res ? AST_MODULE_LOAD_DECLINE : 0;
06724 }
06725
06726 static int reload(void)
06727 {
06728 ast_unload_realtime("queue_members");
06729 reload_queues(1);
06730 return 0;
06731 }
06732
06733 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
06734 .load = load_module,
06735 .unload = unload_module,
06736 .reload = reload,
06737 );
06738