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: 211569 $")
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 char tmp_interface[80];
00720
00721 queue_iter = ao2_iterator_init(queues, 0);
00722 while ((q = ao2_iterator_next(&queue_iter))) {
00723 ao2_lock(q);
00724 mem_iter = ao2_iterator_init(q->members, 0);
00725 while ((cur = ao2_iterator_next(&mem_iter))) {
00726 char *slash_pos;
00727 ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_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 char interface[80];
00772
00773 AST_LIST_LOCK(&interfaces);
00774 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00775 char *slash_pos;
00776 ast_copy_string(interface, curint->interface, sizeof(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
01964 if ((res > 0 && !valid_exit(qe, res)))
01965 res = 0;
01966
01967
01968 qe->last_pos = now;
01969 qe->last_pos_said = qe->pos;
01970
01971
01972 if (!res) {
01973 if (ringing) {
01974 ast_indicate(qe->chan, AST_CONTROL_RINGING);
01975 } else {
01976 ast_moh_start(qe->chan, qe->moh, NULL);
01977 }
01978 }
01979 return res;
01980 }
01981
01982 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01983 {
01984 int oldvalue;
01985
01986
01987
01988
01989
01990 ao2_lock(qe->parent);
01991 oldvalue = qe->parent->holdtime;
01992 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01993 ao2_unlock(qe->parent);
01994 }
01995
01996
01997
01998
01999
02000
02001 static void leave_queue(struct queue_ent *qe)
02002 {
02003 struct call_queue *q;
02004 struct queue_ent *current, *prev = NULL;
02005 struct penalty_rule *pr_iter;
02006 int pos = 0;
02007
02008 if (!(q = qe->parent))
02009 return;
02010 queue_ref(q);
02011 ao2_lock(q);
02012
02013 prev = NULL;
02014 for (current = q->head; current; current = current->next) {
02015 if (current == qe) {
02016 q->count--;
02017
02018
02019 manager_event(EVENT_FLAG_CALL, "Leave",
02020 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02021 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
02022 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02023
02024 if (prev)
02025 prev->next = current->next;
02026 else
02027 q->head = current->next;
02028
02029 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02030 ast_free(pr_iter);
02031 } else {
02032
02033 current->pos = ++pos;
02034 prev = current;
02035 }
02036 }
02037 ao2_unlock(q);
02038
02039
02040 if (q->realtime) {
02041 struct ast_variable *var;
02042 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02043 q->dead = 1;
02044 } else {
02045 ast_variables_destroy(var);
02046 }
02047 }
02048
02049 if (q->dead) {
02050
02051 ao2_unlink(queues, q);
02052 }
02053
02054 queue_unref(q);
02055 }
02056
02057
02058 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
02059 {
02060 struct callattempt *oo;
02061
02062 while (outgoing) {
02063
02064 if (outgoing->chan && (outgoing->chan != exception))
02065 ast_hangup(outgoing->chan);
02066 oo = outgoing;
02067 outgoing = outgoing->q_next;
02068 if (oo->member)
02069 ao2_ref(oo->member, -1);
02070 ast_free(oo);
02071 }
02072 }
02073
02074
02075
02076
02077
02078
02079
02080
02081
02082 static int num_available_members(struct call_queue *q)
02083 {
02084 struct member *mem;
02085 int avl = 0;
02086 struct ao2_iterator mem_iter;
02087
02088 mem_iter = ao2_iterator_init(q->members, 0);
02089 while ((mem = ao2_iterator_next(&mem_iter))) {
02090 switch (mem->status) {
02091 case AST_DEVICE_INUSE:
02092 if (!q->ringinuse)
02093 break;
02094
02095 case AST_DEVICE_NOT_INUSE:
02096 case AST_DEVICE_UNKNOWN:
02097 if (!mem->paused) {
02098 avl++;
02099 }
02100 break;
02101 }
02102 ao2_ref(mem, -1);
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02115 break;
02116 }
02117 }
02118
02119 return avl;
02120 }
02121
02122
02123
02124 static int compare_weight(struct call_queue *rq, struct member *member)
02125 {
02126 struct call_queue *q;
02127 struct member *mem;
02128 int found = 0;
02129 struct ao2_iterator queue_iter;
02130
02131
02132
02133 queue_iter = ao2_iterator_init(queues, 0);
02134 while ((q = ao2_iterator_next(&queue_iter))) {
02135 if (q == rq) {
02136 queue_unref(q);
02137 continue;
02138 }
02139 ao2_lock(q);
02140 if (q->count && q->members) {
02141 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02142 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02143 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02144 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);
02145 found = 1;
02146 }
02147 ao2_ref(mem, -1);
02148 }
02149 }
02150 ao2_unlock(q);
02151 queue_unref(q);
02152 if (found) {
02153 break;
02154 }
02155 }
02156 return found;
02157 }
02158
02159
02160 static void do_hang(struct callattempt *o)
02161 {
02162 o->stillgoing = 0;
02163 ast_hangup(o->chan);
02164 o->chan = NULL;
02165 }
02166
02167
02168 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
02169 {
02170 struct ast_str *buf = ast_str_alloca(len + 1);
02171 char *tmp;
02172
02173 if (pbx_builtin_serialize_variables(chan, &buf)) {
02174 int i, j;
02175
02176
02177 strcpy(vars, "Variable: ");
02178 tmp = buf->str;
02179
02180 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02181 vars[j] = tmp[i];
02182
02183 if (tmp[i + 1] == '\0')
02184 break;
02185 if (tmp[i] == '\n') {
02186 vars[j++] = '\r';
02187 vars[j++] = '\n';
02188
02189 ast_copy_string(&(vars[j]), "Variable: ", len - j);
02190 j += 9;
02191 }
02192 }
02193 if (j > len - 3)
02194 j = len - 3;
02195 vars[j++] = '\r';
02196 vars[j++] = '\n';
02197 vars[j] = '\0';
02198 } else {
02199
02200 *vars = '\0';
02201 }
02202 return vars;
02203 }
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214
02215
02216
02217
02218
02219 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
02220 {
02221 int res;
02222 int status;
02223 char tech[256];
02224 char *location;
02225 const char *macrocontext, *macroexten;
02226
02227
02228 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02229 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02230 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
02231 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02232 if (qe->chan->cdr)
02233 ast_cdr_busy(qe->chan->cdr);
02234 tmp->stillgoing = 0;
02235 (*busies)++;
02236 return 0;
02237 }
02238
02239 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02240 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02241 if (qe->chan->cdr)
02242 ast_cdr_busy(qe->chan->cdr);
02243 tmp->stillgoing = 0;
02244 return 0;
02245 }
02246
02247 if (tmp->member->paused) {
02248 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02249 if (qe->chan->cdr)
02250 ast_cdr_busy(qe->chan->cdr);
02251 tmp->stillgoing = 0;
02252 return 0;
02253 }
02254 if (use_weight && compare_weight(qe->parent,tmp->member)) {
02255 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02256 if (qe->chan->cdr)
02257 ast_cdr_busy(qe->chan->cdr);
02258 tmp->stillgoing = 0;
02259 (*busies)++;
02260 return 0;
02261 }
02262
02263 ast_copy_string(tech, tmp->interface, sizeof(tech));
02264 if ((location = strchr(tech, '/')))
02265 *location++ = '\0';
02266 else
02267 location = "";
02268
02269
02270 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02271 if (!tmp->chan) {
02272 if (qe->chan->cdr)
02273 ast_cdr_busy(qe->chan->cdr);
02274 tmp->stillgoing = 0;
02275
02276 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02277
02278 ao2_lock(qe->parent);
02279 qe->parent->rrpos++;
02280 qe->linpos++;
02281 ao2_unlock(qe->parent);
02282
02283
02284 (*busies)++;
02285 return 0;
02286 }
02287
02288 tmp->chan->appl = "AppQueue";
02289 tmp->chan->data = "(Outgoing Line)";
02290 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02291 if (tmp->chan->cid.cid_num)
02292 ast_free(tmp->chan->cid.cid_num);
02293 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02294 if (tmp->chan->cid.cid_name)
02295 ast_free(tmp->chan->cid.cid_name);
02296 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02297 if (tmp->chan->cid.cid_ani)
02298 ast_free(tmp->chan->cid.cid_ani);
02299 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02300
02301
02302 ast_channel_inherit_variables(qe->chan, tmp->chan);
02303
02304
02305 tmp->chan->adsicpe = qe->chan->adsicpe;
02306
02307
02308 ast_channel_lock(qe->chan);
02309 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02310 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02311 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02312 if (!ast_strlen_zero(macroexten))
02313 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02314 else
02315 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02316 if (ast_cdr_isset_unanswered()) {
02317
02318
02319 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02320 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02321 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02322 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02323 strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02324 strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02325 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02326 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02327 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02328 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02329 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02330 }
02331 ast_channel_unlock(qe->chan);
02332
02333
02334 if ((res = ast_call(tmp->chan, location, 0))) {
02335
02336 ast_debug(1, "ast call on peer returned %d\n", res);
02337 ast_verb(3, "Couldn't call %s\n", tmp->interface);
02338 do_hang(tmp);
02339 (*busies)++;
02340 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02341 return 0;
02342 } else if (qe->parent->eventwhencalled) {
02343 char vars[2048];
02344
02345 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02346 "Queue: %s\r\n"
02347 "AgentCalled: %s\r\n"
02348 "AgentName: %s\r\n"
02349 "ChannelCalling: %s\r\n"
02350 "DestinationChannel: %s\r\n"
02351 "CallerIDNum: %s\r\n"
02352 "CallerIDName: %s\r\n"
02353 "Context: %s\r\n"
02354 "Extension: %s\r\n"
02355 "Priority: %d\r\n"
02356 "Uniqueid: %s\r\n"
02357 "%s",
02358 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02359 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02360 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02361 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02362 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02363 ast_verb(3, "Called %s\n", tmp->interface);
02364 }
02365
02366 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02367 return 1;
02368 }
02369
02370
02371 static struct callattempt *find_best(struct callattempt *outgoing)
02372 {
02373 struct callattempt *best = NULL, *cur;
02374
02375 for (cur = outgoing; cur; cur = cur->q_next) {
02376 if (cur->stillgoing &&
02377 !cur->chan &&
02378 (!best || cur->metric < best->metric)) {
02379 best = cur;
02380 }
02381 }
02382
02383 return best;
02384 }
02385
02386
02387
02388
02389
02390
02391
02392
02393
02394
02395
02396 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
02397 {
02398 int ret = 0;
02399
02400 while (ret == 0) {
02401 struct callattempt *best = find_best(outgoing);
02402 if (!best) {
02403 ast_debug(1, "Nobody left to try ringing in queue\n");
02404 break;
02405 }
02406 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02407 struct callattempt *cur;
02408
02409 for (cur = outgoing; cur; cur = cur->q_next) {
02410 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02411 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02412 ret |= ring_entry(qe, cur, busies);
02413 }
02414 }
02415 } else {
02416
02417 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02418 ret = ring_entry(qe, best, busies);
02419 }
02420
02421
02422 if (qe->expire && (time(NULL) >= qe->expire)) {
02423 ast_debug(1, "Queue timed out while ringing members.\n");
02424 ret = 0;
02425 break;
02426 }
02427 }
02428
02429 return ret;
02430 }
02431
02432
02433 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
02434 {
02435 struct callattempt *best = find_best(outgoing);
02436
02437 if (best) {
02438
02439 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02440 qe->parent->rrpos = best->metric % 1000;
02441 } else {
02442
02443 if (qe->parent->wrapped) {
02444
02445 qe->parent->rrpos = 0;
02446 } else {
02447
02448 qe->parent->rrpos++;
02449 }
02450 }
02451 qe->parent->wrapped = 0;
02452
02453 return 0;
02454 }
02455
02456
02457 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
02458 {
02459 struct callattempt *best = find_best(outgoing);
02460
02461 if (best) {
02462
02463 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02464 qe->linpos = best->metric % 1000;
02465 } else {
02466
02467 if (qe->linwrapped) {
02468
02469 qe->linpos = 0;
02470 } else {
02471
02472 qe->linpos++;
02473 }
02474 }
02475 qe->linwrapped = 0;
02476
02477 return 0;
02478 }
02479
02480
02481 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
02482 {
02483 int res = 0;
02484 time_t now;
02485
02486
02487 time(&now);
02488
02489
02490 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02491 return 0;
02492
02493
02494 if (ringing)
02495 ast_indicate(qe->chan,-1);
02496 else
02497 ast_moh_stop(qe->chan);
02498
02499 ast_verb(3, "Playing periodic announcement\n");
02500
02501 if (qe->parent->randomperiodicannounce) {
02502 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02503 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
02504 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02505 qe->last_periodic_announce_sound = 0;
02506 }
02507
02508
02509 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02510
02511 if (res > 0 && !valid_exit(qe, res))
02512 res = 0;
02513
02514
02515 if (!res) {
02516 if (ringing)
02517 ast_indicate(qe->chan, AST_CONTROL_RINGING);
02518 else
02519 ast_moh_start(qe->chan, qe->moh, NULL);
02520 }
02521
02522
02523 qe->last_periodic_announce_time = now;
02524
02525
02526 if (!qe->parent->randomperiodicannounce) {
02527 qe->last_periodic_announce_sound++;
02528 }
02529
02530 return res;
02531 }
02532
02533
02534 static void record_abandoned(struct queue_ent *qe)
02535 {
02536 ao2_lock(qe->parent);
02537 set_queue_variables(qe->parent, qe->chan);
02538 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02539 "Queue: %s\r\n"
02540 "Uniqueid: %s\r\n"
02541 "Position: %d\r\n"
02542 "OriginalPosition: %d\r\n"
02543 "HoldTime: %d\r\n",
02544 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02545
02546 qe->parent->callsabandoned++;
02547 ao2_unlock(qe->parent);
02548 }
02549
02550
02551 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
02552 {
02553 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02554 if (qe->parent->eventwhencalled)
02555 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02556 "Queue: %s\r\n"
02557 "Uniqueid: %s\r\n"
02558 "Channel: %s\r\n"
02559 "Member: %s\r\n"
02560 "MemberName: %s\r\n"
02561 "Ringtime: %d\r\n",
02562 qe->parent->name,
02563 qe->chan->uniqueid,
02564 qe->chan->name,
02565 interface,
02566 membername,
02567 rnatime);
02568 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02569 if (qe->parent->autopause && pause) {
02570 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02571 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02572 } else {
02573 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02574 }
02575 }
02576 return;
02577 }
02578
02579 #define AST_MAX_WATCHERS 256
02580
02581
02582
02583
02584
02585
02586
02587
02588
02589
02590 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02591 {
02592 const char *queue = qe->parent->name;
02593 struct callattempt *o, *start = NULL, *prev = NULL;
02594 int status;
02595 int numbusies = prebusies;
02596 int numnochan = 0;
02597 int stillgoing = 0;
02598 int orig = *to;
02599 struct ast_frame *f;
02600 struct callattempt *peer = NULL;
02601 struct ast_channel *winner;
02602 struct ast_channel *in = qe->chan;
02603 char on[80] = "";
02604 char membername[80] = "";
02605 long starttime = 0;
02606 long endtime = 0;
02607 #ifdef HAVE_EPOLL
02608 struct callattempt *epollo;
02609 #endif
02610
02611 starttime = (long) time(NULL);
02612 #ifdef HAVE_EPOLL
02613 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02614 if (epollo->chan)
02615 ast_poll_channel_add(in, epollo->chan);
02616 }
02617 #endif
02618
02619 while (*to && !peer) {
02620 int numlines, retry, pos = 1;
02621 struct ast_channel *watchers[AST_MAX_WATCHERS];
02622 watchers[0] = in;
02623 start = NULL;
02624
02625 for (retry = 0; retry < 2; retry++) {
02626 numlines = 0;
02627 for (o = outgoing; o; o = o->q_next) {
02628 if (o->stillgoing) {
02629 stillgoing = 1;
02630 if (o->chan) {
02631 watchers[pos++] = o->chan;
02632 if (!start)
02633 start = o;
02634 else
02635 prev->call_next = o;
02636 prev = o;
02637 }
02638 }
02639 numlines++;
02640 }
02641 if (pos > 1 || !stillgoing ||
02642 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
02643 break;
02644
02645
02646 ring_one(qe, outgoing, &numbusies);
02647
02648 }
02649 if (pos == 1 ) {
02650 if (numlines == (numbusies + numnochan)) {
02651 ast_debug(1, "Everyone is busy at this time\n");
02652 } else {
02653 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02654 }
02655 *to = 0;
02656 return NULL;
02657 }
02658 winner = ast_waitfor_n(watchers, pos, to);
02659 for (o = start; o; o = o->call_next) {
02660 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
02661 if (!peer) {
02662 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02663 peer = o;
02664 }
02665 } else if (o->chan && (o->chan == winner)) {
02666
02667 ast_copy_string(on, o->member->interface, sizeof(on));
02668 ast_copy_string(membername, o->member->membername, sizeof(membername));
02669
02670 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02671 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02672 numnochan++;
02673 do_hang(o);
02674 winner = NULL;
02675 continue;
02676 } else if (!ast_strlen_zero(o->chan->call_forward)) {
02677 char tmpchan[256];
02678 char *stuff;
02679 char *tech;
02680
02681 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02682 if ((stuff = strchr(tmpchan, '/'))) {
02683 *stuff++ = '\0';
02684 tech = tmpchan;
02685 } else {
02686 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02687 stuff = tmpchan;
02688 tech = "Local";
02689 }
02690
02691 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02692
02693 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02694 if (!o->chan) {
02695 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02696 o->stillgoing = 0;
02697 numnochan++;
02698 } else {
02699 ast_channel_inherit_variables(in, o->chan);
02700 ast_channel_datastore_inherit(in, o->chan);
02701 if (o->chan->cid.cid_num)
02702 ast_free(o->chan->cid.cid_num);
02703 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02704
02705 if (o->chan->cid.cid_name)
02706 ast_free(o->chan->cid.cid_name);
02707 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02708
02709 ast_string_field_set(o->chan, accountcode, in->accountcode);
02710 o->chan->cdrflags = in->cdrflags;
02711
02712 if (in->cid.cid_ani) {
02713 if (o->chan->cid.cid_ani)
02714 ast_free(o->chan->cid.cid_ani);
02715 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02716 }
02717 if (o->chan->cid.cid_rdnis)
02718 ast_free(o->chan->cid.cid_rdnis);
02719 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02720 if (ast_call(o->chan, tmpchan, 0)) {
02721 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02722 do_hang(o);
02723 numnochan++;
02724 }
02725 }
02726
02727 ast_hangup(winner);
02728 continue;
02729 }
02730 f = ast_read(winner);
02731 if (f) {
02732 if (f->frametype == AST_FRAME_CONTROL) {
02733 switch (f->subclass) {
02734 case AST_CONTROL_ANSWER:
02735
02736 if (!peer) {
02737 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02738 peer = o;
02739 }
02740 break;
02741 case AST_CONTROL_BUSY:
02742 ast_verb(3, "%s is busy\n", o->chan->name);
02743 if (in->cdr)
02744 ast_cdr_busy(in->cdr);
02745 do_hang(o);
02746 endtime = (long) time(NULL);
02747 endtime -= starttime;
02748 rna(endtime * 1000, qe, on, membername, 0);
02749 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02750 if (qe->parent->timeoutrestart)
02751 *to = orig;
02752 ring_one(qe, outgoing, &numbusies);
02753 }
02754 numbusies++;
02755 break;
02756 case AST_CONTROL_CONGESTION:
02757 ast_verb(3, "%s is circuit-busy\n", o->chan->name);
02758 if (in->cdr)
02759 ast_cdr_busy(in->cdr);
02760 endtime = (long) time(NULL);
02761 endtime -= starttime;
02762 rna(endtime * 1000, qe, on, membername, 0);
02763 do_hang(o);
02764 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02765 if (qe->parent->timeoutrestart)
02766 *to = orig;
02767 ring_one(qe, outgoing, &numbusies);
02768 }
02769 numbusies++;
02770 break;
02771 case AST_CONTROL_RINGING:
02772 ast_verb(3, "%s is ringing\n", o->chan->name);
02773 break;
02774 case AST_CONTROL_OFFHOOK:
02775
02776 break;
02777 default:
02778 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
02779 }
02780 }
02781 ast_frfree(f);
02782 } else {
02783 endtime = (long) time(NULL) - starttime;
02784 rna(endtime * 1000, qe, on, membername, 1);
02785 do_hang(o);
02786 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02787 if (qe->parent->timeoutrestart)
02788 *to = orig;
02789 ring_one(qe, outgoing, &numbusies);
02790 }
02791 }
02792 }
02793 }
02794 if (winner == in) {
02795 f = ast_read(in);
02796 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02797
02798 *to = -1;
02799 if (f) {
02800 if (f->data.uint32) {
02801 in->hangupcause = f->data.uint32;
02802 }
02803 ast_frfree(f);
02804 }
02805 return NULL;
02806 }
02807 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02808 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
02809 *to = 0;
02810 ast_frfree(f);
02811 return NULL;
02812 }
02813 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02814 ast_verb(3, "User pressed digit: %c\n", f->subclass);
02815 *to = 0;
02816 *digit = f->subclass;
02817 ast_frfree(f);
02818 return NULL;
02819 }
02820 ast_frfree(f);
02821 }
02822 if (!*to) {
02823 for (o = start; o; o = o->call_next)
02824 rna(orig, qe, o->interface, o->member->membername, 1);
02825 }
02826 }
02827
02828 #ifdef HAVE_EPOLL
02829 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02830 if (epollo->chan)
02831 ast_poll_channel_del(in, epollo->chan);
02832 }
02833 #endif
02834
02835 return peer;
02836 }
02837
02838
02839
02840
02841
02842
02843
02844
02845
02846
02847
02848
02849 static int is_our_turn(struct queue_ent *qe)
02850 {
02851 struct queue_ent *ch;
02852 int res;
02853 int avl;
02854 int idx = 0;
02855
02856 ao2_lock(qe->parent);
02857
02858 avl = num_available_members(qe->parent);
02859
02860 ch = qe->parent->head;
02861
02862 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02863
02864 while ((idx < avl) && (ch) && (ch != qe)) {
02865 if (!ch->pending)
02866 idx++;
02867 ch = ch->next;
02868 }
02869
02870 ao2_unlock(qe->parent);
02871
02872
02873 if (ch && idx < avl) {
02874 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02875 res = 1;
02876 } else {
02877 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02878 res = 0;
02879 }
02880
02881 return res;
02882 }
02883
02884
02885
02886
02887
02888
02889
02890 static void update_qe_rule(struct queue_ent *qe)
02891 {
02892 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02893 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02894 char max_penalty_str[20], min_penalty_str[20];
02895
02896 if (max_penalty < 0)
02897 max_penalty = 0;
02898 if (min_penalty < 0)
02899 min_penalty = 0;
02900 if (min_penalty > max_penalty)
02901 min_penalty = max_penalty;
02902 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02903 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02904 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02905 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02906 qe->max_penalty = max_penalty;
02907 qe->min_penalty = min_penalty;
02908 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);
02909 qe->pr = AST_LIST_NEXT(qe->pr, list);
02910 }
02911
02912
02913
02914
02915
02916
02917
02918
02919
02920
02921
02922 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02923 {
02924 int res = 0;
02925
02926
02927 for (;;) {
02928 enum queue_member_status status = QUEUE_NORMAL;
02929 int exit = 0;
02930
02931 if (is_our_turn(qe))
02932 break;
02933
02934
02935 if (qe->expire && (time(NULL) >= qe->expire)) {
02936 *reason = QUEUE_TIMEOUT;
02937 break;
02938 }
02939
02940
02941
02942
02943
02944 for (; !exit || qe->pr; update_qe_rule(qe)) {
02945 status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02946
02947 if (!qe->pr || status == QUEUE_NORMAL) {
02948 break;
02949 }
02950
02951
02952 if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
02953 ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02954 ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
02955 continue;
02956 } else {
02957 exit = 1;
02958 }
02959 }
02960
02961 if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
02962 *reason = QUEUE_LEAVEEMPTY;
02963 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02964 leave_queue(qe);
02965 break;
02966 }
02967
02968
02969 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02970 *reason = QUEUE_LEAVEUNAVAIL;
02971 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02972 leave_queue(qe);
02973 break;
02974 }
02975 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
02976 *reason = QUEUE_LEAVEUNAVAIL;
02977 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02978 leave_queue(qe);
02979 break;
02980 }
02981
02982
02983 if (qe->parent->announcefrequency &&
02984 (res = say_position(qe,ringing)))
02985 break;
02986
02987
02988 if (qe->expire && (time(NULL) >= qe->expire)) {
02989 *reason = QUEUE_TIMEOUT;
02990 break;
02991 }
02992
02993
02994 if (qe->parent->periodicannouncefrequency &&
02995 (res = say_periodic_announcement(qe,ringing)))
02996 break;
02997
02998
02999 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03000 update_qe_rule(qe);
03001 }
03002
03003
03004 if (qe->expire && (time(NULL) >= qe->expire)) {
03005 *reason = QUEUE_TIMEOUT;
03006 break;
03007 }
03008
03009
03010 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03011 if (res > 0 && !valid_exit(qe, res))
03012 res = 0;
03013 else
03014 break;
03015 }
03016
03017
03018 if (qe->expire && (time(NULL) >= qe->expire)) {
03019 *reason = QUEUE_TIMEOUT;
03020 break;
03021 }
03022 }
03023
03024 return res;
03025 }
03026
03027
03028
03029
03030
03031 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
03032 {
03033 struct member *mem;
03034 struct call_queue *qtmp;
03035 struct ao2_iterator queue_iter;
03036
03037 if (shared_lastcall) {
03038 queue_iter = ao2_iterator_init(queues, 0);
03039 while ((qtmp = ao2_iterator_next(&queue_iter))) {
03040 ao2_lock(qtmp);
03041 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03042 time(&mem->lastcall);
03043 mem->calls++;
03044 mem->lastqueue = q;
03045 ao2_ref(mem, -1);
03046 }
03047 ao2_unlock(qtmp);
03048 ao2_ref(qtmp, -1);
03049 }
03050 } else {
03051 ao2_lock(q);
03052 time(&member->lastcall);
03053 member->calls++;
03054 member->lastqueue = q;
03055 ao2_unlock(q);
03056 }
03057 ao2_lock(q);
03058 q->callscompleted++;
03059 if (callcompletedinsl)
03060 q->callscompletedinsl++;
03061 ao2_unlock(q);
03062 return 0;
03063 }
03064
03065
03066
03067
03068
03069
03070
03071
03072
03073 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
03074 {
03075 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03076 return -1;
03077
03078 switch (q->strategy) {
03079 case QUEUE_STRATEGY_RINGALL:
03080
03081 tmp->metric = mem->penalty * 1000000;
03082 break;
03083 case QUEUE_STRATEGY_LINEAR:
03084 if (pos < qe->linpos) {
03085 tmp->metric = 1000 + pos;
03086 } else {
03087 if (pos > qe->linpos)
03088
03089 qe->linwrapped = 1;
03090 tmp->metric = pos;
03091 }
03092 tmp->metric += mem->penalty * 1000000;
03093 break;
03094 case QUEUE_STRATEGY_RRMEMORY:
03095 if (pos < q->rrpos) {
03096 tmp->metric = 1000 + pos;
03097 } else {
03098 if (pos > q->rrpos)
03099
03100 q->wrapped = 1;
03101 tmp->metric = pos;
03102 }
03103 tmp->metric += mem->penalty * 1000000;
03104 break;
03105 case QUEUE_STRATEGY_RANDOM:
03106 tmp->metric = ast_random() % 1000;
03107 tmp->metric += mem->penalty * 1000000;
03108 break;
03109 case QUEUE_STRATEGY_WRANDOM:
03110 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03111 break;
03112 case QUEUE_STRATEGY_FEWESTCALLS:
03113 tmp->metric = mem->calls;
03114 tmp->metric += mem->penalty * 1000000;
03115 break;
03116 case QUEUE_STRATEGY_LEASTRECENT:
03117 if (!mem->lastcall)
03118 tmp->metric = 0;
03119 else
03120 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03121 tmp->metric += mem->penalty * 1000000;
03122 break;
03123 default:
03124 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03125 break;
03126 }
03127 return 0;
03128 }
03129
03130 enum agent_complete_reason {
03131 CALLER,
03132 AGENT,
03133 TRANSFER
03134 };
03135
03136
03137 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
03138 const struct ast_channel *peer, const struct member *member, time_t callstart,
03139 char *vars, size_t vars_len, enum agent_complete_reason rsn)
03140 {
03141 const char *reason = NULL;
03142
03143 if (!qe->parent->eventwhencalled)
03144 return;
03145
03146 switch (rsn) {
03147 case CALLER:
03148 reason = "caller";
03149 break;
03150 case AGENT:
03151 reason = "agent";
03152 break;
03153 case TRANSFER:
03154 reason = "transfer";
03155 break;
03156 }
03157
03158 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03159 "Queue: %s\r\n"
03160 "Uniqueid: %s\r\n"
03161 "Channel: %s\r\n"
03162 "Member: %s\r\n"
03163 "MemberName: %s\r\n"
03164 "HoldTime: %ld\r\n"
03165 "TalkTime: %ld\r\n"
03166 "Reason: %s\r\n"
03167 "%s",
03168 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03169 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03170 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03171 }
03172
03173 struct queue_transfer_ds {
03174 struct queue_ent *qe;
03175 struct member *member;
03176 time_t starttime;
03177 int callcompletedinsl;
03178 };
03179
03180 static void queue_transfer_destroy(void *data)
03181 {
03182 struct queue_transfer_ds *qtds = data;
03183 ast_free(qtds);
03184 }
03185
03186
03187
03188 static const struct ast_datastore_info queue_transfer_info = {
03189 .type = "queue_transfer",
03190 .chan_fixup = queue_transfer_fixup,
03191 .destroy = queue_transfer_destroy,
03192 };
03193
03194
03195
03196
03197
03198
03199
03200
03201
03202
03203 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
03204 {
03205 struct queue_transfer_ds *qtds = data;
03206 struct queue_ent *qe = qtds->qe;
03207 struct member *member = qtds->member;
03208 time_t callstart = qtds->starttime;
03209 int callcompletedinsl = qtds->callcompletedinsl;
03210 struct ast_datastore *datastore;
03211
03212 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03213 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03214 (long) (time(NULL) - callstart), qe->opos);
03215
03216 update_queue(qe->parent, member, callcompletedinsl);
03217
03218
03219 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03220 ast_channel_datastore_remove(old_chan, datastore);
03221 } else {
03222 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03223 }
03224 }
03225
03226
03227
03228
03229
03230
03231
03232
03233
03234 static int attended_transfer_occurred(struct ast_channel *chan)
03235 {
03236 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03237 }
03238
03239
03240
03241 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
03242 {
03243 struct ast_datastore *ds;
03244 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03245
03246 if (!qtds) {
03247 ast_log(LOG_WARNING, "Memory allocation error!\n");
03248 return NULL;
03249 }
03250
03251 ast_channel_lock(qe->chan);
03252 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03253 ast_channel_unlock(qe->chan);
03254 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03255 return NULL;
03256 }
03257
03258 qtds->qe = qe;
03259
03260 qtds->member = member;
03261 qtds->starttime = starttime;
03262 qtds->callcompletedinsl = callcompletedinsl;
03263 ds->data = qtds;
03264 ast_channel_datastore_add(qe->chan, ds);
03265 ast_channel_unlock(qe->chan);
03266 return ds;
03267 }
03268
03269 struct queue_end_bridge {
03270 struct call_queue *q;
03271 struct ast_channel *chan;
03272 };
03273
03274 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
03275 {
03276 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03277 ao2_ref(qeb, +1);
03278 qeb->chan = originator;
03279 }
03280
03281 static void end_bridge_callback(void *data)
03282 {
03283 struct queue_end_bridge *qeb = data;
03284 struct call_queue *q = qeb->q;
03285 struct ast_channel *chan = qeb->chan;
03286
03287 if (ao2_ref(qeb, -1) == 1) {
03288 ao2_lock(q);
03289 set_queue_variables(q, chan);
03290 ao2_unlock(q);
03291
03292 queue_unref(q);
03293 }
03294 }
03295
03296
03297
03298
03299
03300
03301
03302
03303
03304
03305
03306
03307
03308
03309
03310
03311
03312
03313
03314
03315
03316
03317
03318
03319
03320
03321
03322
03323 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)
03324 {
03325 struct member *cur;
03326 struct callattempt *outgoing = NULL;
03327 int to, orig;
03328 char oldexten[AST_MAX_EXTENSION]="";
03329 char oldcontext[AST_MAX_CONTEXT]="";
03330 char queuename[256]="";
03331 char interfacevar[256]="";
03332 struct ast_channel *peer;
03333 struct ast_channel *which;
03334 struct callattempt *lpeer;
03335 struct member *member;
03336 struct ast_app *application;
03337 int res = 0, bridge = 0;
03338 int numbusies = 0;
03339 int x=0;
03340 char *announce = NULL;
03341 char digit = 0;
03342 time_t callstart;
03343 time_t now = time(NULL);
03344 struct ast_bridge_config bridge_config;
03345 char nondataquality = 1;
03346 char *agiexec = NULL;
03347 char *macroexec = NULL;
03348 char *gosubexec = NULL;
03349 int ret = 0;
03350 const char *monitorfilename;
03351 const char *monitor_exec;
03352 const char *monitor_options;
03353 char tmpid[256], tmpid2[256];
03354 char meid[1024], meid2[1024];
03355 char mixmonargs[1512];
03356 struct ast_app *mixmonapp = NULL;
03357 char *p;
03358 char vars[2048];
03359 int forwardsallowed = 1;
03360 int callcompletedinsl;
03361 struct ao2_iterator memi;
03362 struct ast_datastore *datastore, *transfer_ds;
03363 struct queue_end_bridge *queue_end_bridge = NULL;
03364
03365 ast_channel_lock(qe->chan);
03366 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03367 ast_channel_unlock(qe->chan);
03368
03369 memset(&bridge_config, 0, sizeof(bridge_config));
03370 tmpid[0] = 0;
03371 meid[0] = 0;
03372 time(&now);
03373
03374
03375
03376
03377
03378 if (qe->expire && now >= qe->expire) {
03379 res = 0;
03380 goto out;
03381 }
03382
03383 for (; options && *options; options++)
03384 switch (*options) {
03385 case 't':
03386 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03387 break;
03388 case 'T':
03389 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03390 break;
03391 case 'w':
03392 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03393 break;
03394 case 'W':
03395 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03396 break;
03397 case 'c':
03398 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03399 break;
03400 case 'd':
03401 nondataquality = 0;
03402 break;
03403 case 'h':
03404 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03405 break;
03406 case 'H':
03407 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03408 break;
03409 case 'k':
03410 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03411 break;
03412 case 'K':
03413 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03414 break;
03415 case 'n':
03416 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03417 (*tries)++;
03418 else
03419 *tries = qe->parent->membercount;
03420 *noption = 1;
03421 break;
03422 case 'i':
03423 forwardsallowed = 0;
03424 break;
03425 case 'x':
03426 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03427 break;
03428 case 'X':
03429 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03430 break;
03431
03432 }
03433
03434
03435 if (use_weight)
03436 ao2_lock(queues);
03437 ao2_lock(qe->parent);
03438 ast_debug(1, "%s is trying to call a queue member.\n",
03439 qe->chan->name);
03440 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03441 if (!ast_strlen_zero(qe->announce))
03442 announce = qe->announce;
03443 if (!ast_strlen_zero(announceoverride))
03444 announce = announceoverride;
03445
03446 memi = ao2_iterator_init(qe->parent->members, 0);
03447 while ((cur = ao2_iterator_next(&memi))) {
03448 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03449 struct ast_dialed_interface *di;
03450 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03451 if (!tmp) {
03452 ao2_ref(cur, -1);
03453 ao2_unlock(qe->parent);
03454 if (use_weight)
03455 ao2_unlock(queues);
03456 goto out;
03457 }
03458 if (!datastore) {
03459 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03460 ao2_ref(cur, -1);
03461 ao2_unlock(qe->parent);
03462 if (use_weight)
03463 ao2_unlock(queues);
03464 free(tmp);
03465 goto out;
03466 }
03467 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03468 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03469 ao2_ref(cur, -1);
03470 ao2_unlock(&qe->parent);
03471 if (use_weight)
03472 ao2_unlock(queues);
03473 free(tmp);
03474 goto out;
03475 }
03476 datastore->data = dialed_interfaces;
03477 AST_LIST_HEAD_INIT(dialed_interfaces);
03478
03479 ast_channel_lock(qe->chan);
03480 ast_channel_datastore_add(qe->chan, datastore);
03481 ast_channel_unlock(qe->chan);
03482 } else
03483 dialed_interfaces = datastore->data;
03484
03485 AST_LIST_LOCK(dialed_interfaces);
03486 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03487 if (!strcasecmp(cur->interface, di->interface)) {
03488 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
03489 di->interface);
03490 break;
03491 }
03492 }
03493 AST_LIST_UNLOCK(dialed_interfaces);
03494
03495 if (di) {
03496 free(tmp);
03497 continue;
03498 }
03499
03500
03501
03502
03503
03504 if (strncasecmp(cur->interface, "Local/", 6)) {
03505 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03506 ao2_ref(cur, -1);
03507 ao2_unlock(qe->parent);
03508 if (use_weight)
03509 ao2_unlock(queues);
03510 free(tmp);
03511 goto out;
03512 }
03513 strcpy(di->interface, cur->interface);
03514
03515 AST_LIST_LOCK(dialed_interfaces);
03516 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03517 AST_LIST_UNLOCK(dialed_interfaces);
03518 }
03519
03520 tmp->stillgoing = -1;
03521 tmp->member = cur;
03522 tmp->oldstatus = cur->status;
03523 tmp->lastcall = cur->lastcall;
03524 tmp->lastqueue = cur->lastqueue;
03525 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03526
03527
03528 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03529
03530
03531
03532 tmp->q_next = outgoing;
03533 outgoing = tmp;
03534
03535 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03536 break;
03537 } else {
03538 ao2_ref(cur, -1);
03539 ast_free(tmp);
03540 }
03541 }
03542
03543 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03544
03545 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03546 to = (qe->expire - now) * 1000;
03547 else
03548 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03549 } else {
03550
03551 if (qe->expire && qe->expire<=now) {
03552 to = 0;
03553 } else if (qe->parent->timeout) {
03554 to = qe->parent->timeout * 1000;
03555 } else {
03556 to = -1;
03557 }
03558 }
03559 orig = to;
03560 ++qe->pending;
03561 ao2_unlock(qe->parent);
03562 ring_one(qe, outgoing, &numbusies);
03563 if (use_weight)
03564 ao2_unlock(queues);
03565 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03566
03567
03568
03569
03570
03571
03572 ast_channel_lock(qe->chan);
03573 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03574 ast_datastore_free(datastore);
03575 }
03576 ast_channel_unlock(qe->chan);
03577 ao2_lock(qe->parent);
03578 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03579 store_next_rr(qe, outgoing);
03580 }
03581 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03582 store_next_lin(qe, outgoing);
03583 }
03584 ao2_unlock(qe->parent);
03585 peer = lpeer ? lpeer->chan : NULL;
03586 if (!peer) {
03587 qe->pending = 0;
03588 if (to) {
03589
03590 res = -1;
03591 } else {
03592
03593 res = digit;
03594 }
03595 if (res == -1)
03596 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03597 if (ast_cdr_isset_unanswered()) {
03598
03599
03600 struct callattempt *o;
03601 for (o = outgoing; o; o = o->q_next) {
03602 if (!o->chan) {
03603 continue;
03604 }
03605 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03606 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03607 break;
03608 }
03609 }
03610 }
03611 } else {
03612
03613
03614
03615 if (!strcmp(qe->chan->tech->type, "DAHDI"))
03616 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03617 if (!strcmp(peer->tech->type, "DAHDI"))
03618 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03619
03620 time(&now);
03621 recalc_holdtime(qe, (now - qe->start));
03622 ao2_lock(qe->parent);
03623 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03624 ao2_unlock(qe->parent);
03625 member = lpeer->member;
03626
03627 ao2_ref(member, 1);
03628 hangupcalls(outgoing, peer);
03629 outgoing = NULL;
03630 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03631 int res2;
03632
03633 res2 = ast_autoservice_start(qe->chan);
03634 if (!res2) {
03635 if (qe->parent->memberdelay) {
03636 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03637 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03638 }
03639 if (!res2 && announce) {
03640 play_file(peer, announce);
03641 }
03642 if (!res2 && qe->parent->reportholdtime) {
03643 if (!play_file(peer, qe->parent->sound_reporthold)) {
03644 int holdtime, holdtimesecs;
03645
03646 time(&now);
03647 holdtime = abs((now - qe->start) / 60);
03648 holdtimesecs = abs((now - qe->start));
03649 if (holdtime == 1) {
03650 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03651 play_file(peer, qe->parent->sound_minute);
03652 } else {
03653 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03654 play_file(peer, qe->parent->sound_minutes);
03655 }
03656 if (holdtimesecs > 1) {
03657 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03658 play_file(peer, qe->parent->sound_seconds);
03659 }
03660 }
03661 }
03662 }
03663 res2 |= ast_autoservice_stop(qe->chan);
03664 if (ast_check_hangup(peer)) {
03665
03666 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03667 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03668 if (qe->parent->eventwhencalled)
03669 manager_event(EVENT_FLAG_AGENT, "AgentDump",
03670 "Queue: %s\r\n"
03671 "Uniqueid: %s\r\n"
03672 "Channel: %s\r\n"
03673 "Member: %s\r\n"
03674 "MemberName: %s\r\n"
03675 "%s",
03676 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03677 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03678 ast_hangup(peer);
03679 ao2_ref(member, -1);
03680 goto out;
03681 } else if (res2) {
03682
03683 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03684 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03685 record_abandoned(qe);
03686 ast_cdr_noanswer(qe->chan->cdr);
03687 ast_hangup(peer);
03688 ao2_ref(member, -1);
03689 return -1;
03690 }
03691 }
03692
03693 if (ringing)
03694 ast_indicate(qe->chan,-1);
03695 else
03696 ast_moh_stop(qe->chan);
03697
03698 if (qe->chan->cdr)
03699 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03700
03701 res = ast_channel_make_compatible(qe->chan, peer);
03702 if (res < 0) {
03703 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03704 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03705 record_abandoned(qe);
03706 ast_cdr_failed(qe->chan->cdr);
03707 ast_hangup(peer);
03708 ao2_ref(member, -1);
03709 return -1;
03710 }
03711
03712
03713 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03714 if (play_file(qe->chan, qe->parent->sound_callerannounce))
03715 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03716 }
03717
03718 ao2_lock(qe->parent);
03719
03720
03721 if (qe->parent->setinterfacevar) {
03722 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03723 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03724 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03725 pbx_builtin_setvar_multiple(peer, interfacevar);
03726 }
03727
03728
03729
03730 if (qe->parent->setqueueentryvar) {
03731 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03732 (long) time(NULL) - qe->start, qe->opos);
03733 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03734 pbx_builtin_setvar_multiple(peer, interfacevar);
03735 }
03736
03737
03738 set_queue_variables(qe->parent, qe->chan);
03739 set_queue_variables(qe->parent, peer);
03740 ao2_unlock(qe->parent);
03741
03742 ast_channel_lock(qe->chan);
03743 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03744 monitorfilename = ast_strdupa(monitorfilename);
03745 }
03746 ast_channel_unlock(qe->chan);
03747
03748 if (qe->parent->monfmt && *qe->parent->monfmt) {
03749 if (!qe->parent->montype) {
03750 const char *monexec, *monargs;
03751 ast_debug(1, "Starting Monitor as requested.\n");
03752 ast_channel_lock(qe->chan);
03753 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03754 which = qe->chan;
03755 monexec = monexec ? ast_strdupa(monexec) : NULL;
03756 }
03757 else
03758 which = peer;
03759 ast_channel_unlock(qe->chan);
03760 if (ast_monitor_start) {
03761 if (monitorfilename) {
03762 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03763 } else if (qe->chan->cdr && ast_monitor_start) {
03764 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03765 } else if (ast_monitor_start) {
03766
03767 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03768 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03769 }
03770 }
03771 if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) {
03772 ast_monitor_setjoinfiles(which, 1);
03773 }
03774 } else {
03775 mixmonapp = pbx_findapp("MixMonitor");
03776
03777 if (mixmonapp) {
03778 ast_debug(1, "Starting MixMonitor as requested.\n");
03779 if (!monitorfilename) {
03780 if (qe->chan->cdr)
03781 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03782 else
03783 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03784 } else {
03785 const char *m = monitorfilename;
03786 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 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 == tmpid2 + sizeof(tmpid2))
03802 tmpid2[sizeof(tmpid2) - 1] = '\0';
03803
03804 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03805 }
03806
03807 ast_channel_lock(qe->chan);
03808 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03809 monitor_exec = ast_strdupa(monitor_exec);
03810 }
03811 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03812 monitor_options = ast_strdupa(monitor_options);
03813 } else {
03814 monitor_options = "";
03815 }
03816 ast_channel_unlock(qe->chan);
03817
03818 if (monitor_exec) {
03819 const char *m = monitor_exec;
03820 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03821 switch (*m) {
03822 case '^':
03823 if (*(m + 1) == '{')
03824 *p = '$';
03825 break;
03826 case ',':
03827 *p++ = '\\';
03828
03829 default:
03830 *p = *m;
03831 }
03832 if (*m == '\0')
03833 break;
03834 }
03835 if (p == meid2 + sizeof(meid2))
03836 meid2[sizeof(meid2) - 1] = '\0';
03837
03838 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03839 }
03840
03841 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03842
03843 if (!ast_strlen_zero(monitor_exec))
03844 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03845 else
03846 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03847
03848 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03849
03850 if (qe->chan->cdr)
03851 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03852 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03853 if (qe->chan->cdr)
03854 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03855
03856 } else {
03857 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03858 }
03859 }
03860 }
03861
03862 leave_queue(qe);
03863 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03864 ast_debug(1, "app_queue: sendurl=%s.\n", url);
03865 ast_channel_sendurl(peer, url);
03866 }
03867
03868
03869
03870 if (!ast_strlen_zero(macro)) {
03871 macroexec = ast_strdupa(macro);
03872 } else {
03873 if (qe->parent->membermacro)
03874 macroexec = ast_strdupa(qe->parent->membermacro);
03875 }
03876
03877 if (!ast_strlen_zero(macroexec)) {
03878 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03879
03880 res = ast_autoservice_start(qe->chan);
03881 if (res) {
03882 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03883 res = -1;
03884 }
03885
03886 application = pbx_findapp("Macro");
03887
03888 if (application) {
03889 res = pbx_exec(peer, application, macroexec);
03890 ast_debug(1, "Macro exited with status %d\n", res);
03891 res = 0;
03892 } else {
03893 ast_log(LOG_ERROR, "Could not find application Macro\n");
03894 res = -1;
03895 }
03896
03897 if (ast_autoservice_stop(qe->chan) < 0) {
03898 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03899 res = -1;
03900 }
03901 }
03902
03903
03904
03905 if (!ast_strlen_zero(gosub)) {
03906 gosubexec = ast_strdupa(gosub);
03907 } else {
03908 if (qe->parent->membergosub)
03909 gosubexec = ast_strdupa(qe->parent->membergosub);
03910 }
03911
03912 if (!ast_strlen_zero(gosubexec)) {
03913 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03914
03915 res = ast_autoservice_start(qe->chan);
03916 if (res) {
03917 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03918 res = -1;
03919 }
03920
03921 application = pbx_findapp("Gosub");
03922
03923 if (application) {
03924 char *gosub_args, *gosub_argstart;
03925
03926
03927 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03928 ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03929 peer->priority = 0;
03930
03931 gosub_argstart = strchr(gosubexec, ',');
03932 if (gosub_argstart) {
03933 *gosub_argstart = 0;
03934 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03935 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03936 gosub_args = NULL;
03937 }
03938 *gosub_argstart = ',';
03939 } else {
03940 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03941 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03942 gosub_args = NULL;
03943 }
03944 }
03945 if (gosub_args) {
03946 res = pbx_exec(peer, application, gosub_args);
03947 if (!res) {
03948 struct ast_pbx_args args;
03949 memset(&args, 0, sizeof(args));
03950 args.no_hangup_chan = 1;
03951 ast_pbx_run_args(peer, &args);
03952 }
03953 ast_free(gosub_args);
03954 ast_debug(1, "Gosub exited with status %d\n", res);
03955 } else {
03956 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
03957 }
03958 } else {
03959 ast_log(LOG_ERROR, "Could not find application Gosub\n");
03960 res = -1;
03961 }
03962
03963 if (ast_autoservice_stop(qe->chan) < 0) {
03964 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03965 res = -1;
03966 }
03967 }
03968
03969 if (!ast_strlen_zero(agi)) {
03970 ast_debug(1, "app_queue: agi=%s.\n", agi);
03971 application = pbx_findapp("agi");
03972 if (application) {
03973 agiexec = ast_strdupa(agi);
03974 ret = pbx_exec(qe->chan, application, agiexec);
03975 } else
03976 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03977 }
03978 qe->handled++;
03979 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
03980 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
03981 if (update_cdr && qe->chan->cdr)
03982 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
03983 if (qe->parent->eventwhencalled)
03984 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03985 "Queue: %s\r\n"
03986 "Uniqueid: %s\r\n"
03987 "Channel: %s\r\n"
03988 "Member: %s\r\n"
03989 "MemberName: %s\r\n"
03990 "Holdtime: %ld\r\n"
03991 "BridgedChannel: %s\r\n"
03992 "Ringtime: %ld\r\n"
03993 "%s",
03994 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03995 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
03996 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03997 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03998 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03999
04000 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
04001 queue_end_bridge->q = qe->parent;
04002 queue_end_bridge->chan = qe->chan;
04003 bridge_config.end_bridge_callback = end_bridge_callback;
04004 bridge_config.end_bridge_callback_data = queue_end_bridge;
04005 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
04006
04007
04008
04009
04010 queue_ref(qe->parent);
04011 }
04012
04013 time(&callstart);
04014 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
04015 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
04016
04017
04018
04019
04020 ast_channel_lock(qe->chan);
04021 if (!attended_transfer_occurred(qe->chan)) {
04022 struct ast_datastore *tds;
04023 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
04024 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04025 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
04026 (long) (time(NULL) - callstart), qe->opos);
04027 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04028 } else if (ast_check_hangup(qe->chan)) {
04029 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
04030 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04031 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
04032 } else {
04033 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04034 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04035 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04036 }
04037 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
04038 ast_channel_datastore_remove(qe->chan, tds);
04039 }
04040 update_queue(qe->parent, member, callcompletedinsl);
04041 }
04042
04043 if (transfer_ds) {
04044 ast_datastore_free(transfer_ds);
04045 }
04046 ast_channel_unlock(qe->chan);
04047 ast_hangup(peer);
04048 res = bridge ? bridge : 1;
04049 ao2_ref(member, -1);
04050 }
04051 out:
04052 hangupcalls(outgoing, NULL);
04053
04054 return res;
04055 }
04056
04057 static int wait_a_bit(struct queue_ent *qe)
04058 {
04059
04060 int retrywait = qe->parent->retry * 1000;
04061
04062 int res = ast_waitfordigit(qe->chan, retrywait);
04063 if (res > 0 && !valid_exit(qe, res))
04064 res = 0;
04065
04066 return res;
04067 }
04068
04069 static struct member *interface_exists(struct call_queue *q, const char *interface)
04070 {
04071 struct member *mem;
04072 struct ao2_iterator mem_iter;
04073
04074 if (!q)
04075 return NULL;
04076
04077 mem_iter = ao2_iterator_init(q->members, 0);
04078 while ((mem = ao2_iterator_next(&mem_iter))) {
04079 if (!strcasecmp(interface, mem->interface))
04080 return mem;
04081 ao2_ref(mem, -1);
04082 }
04083
04084 return NULL;
04085 }
04086
04087
04088
04089
04090
04091
04092 static void dump_queue_members(struct call_queue *pm_queue)
04093 {
04094 struct member *cur_member;
04095 char value[PM_MAX_LEN];
04096 int value_len = 0;
04097 int res;
04098 struct ao2_iterator mem_iter;
04099
04100 memset(value, 0, sizeof(value));
04101
04102 if (!pm_queue)
04103 return;
04104
04105 mem_iter = ao2_iterator_init(pm_queue->members, 0);
04106 while ((cur_member = ao2_iterator_next(&mem_iter))) {
04107 if (!cur_member->dynamic) {
04108 ao2_ref(cur_member, -1);
04109 continue;
04110 }
04111
04112 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04113 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04114
04115 ao2_ref(cur_member, -1);
04116
04117 if (res != strlen(value + value_len)) {
04118 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04119 break;
04120 }
04121 value_len += res;
04122 }
04123
04124 if (value_len && !cur_member) {
04125 if (ast_db_put(pm_family, pm_queue->name, value))
04126 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04127 } else
04128
04129 ast_db_del(pm_family, pm_queue->name);
04130 }
04131
04132
04133
04134
04135
04136
04137
04138 static int remove_from_queue(const char *queuename, const char *interface)
04139 {
04140 struct call_queue *q, tmpq = {
04141 .name = queuename,
04142 };
04143 struct member *mem, tmpmem;
04144 int res = RES_NOSUCHQUEUE;
04145
04146 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04147 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04148 ao2_lock(queues);
04149 ao2_lock(q);
04150 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04151
04152 if (!mem->dynamic) {
04153 ao2_ref(mem, -1);
04154 ao2_unlock(q);
04155 queue_unref(q);
04156 ao2_unlock(queues);
04157 return RES_NOT_DYNAMIC;
04158 }
04159 q->membercount--;
04160 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04161 "Queue: %s\r\n"
04162 "Location: %s\r\n"
04163 "MemberName: %s\r\n",
04164 q->name, mem->interface, mem->membername);
04165 ao2_unlink(q->members, mem);
04166 remove_from_interfaces(mem->state_interface, 0);
04167 ao2_ref(mem, -1);
04168
04169 if (queue_persistent_members)
04170 dump_queue_members(q);
04171
04172 res = RES_OKAY;
04173 } else {
04174 res = RES_EXISTS;
04175 }
04176 ao2_unlock(q);
04177 ao2_unlock(queues);
04178 queue_unref(q);
04179 }
04180
04181 return res;
04182 }
04183
04184
04185
04186
04187
04188
04189
04190
04191 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
04192 {
04193 struct call_queue *q;
04194 struct member *new_member, *old_member;
04195 int res = RES_NOSUCHQUEUE;
04196
04197
04198
04199 if (!(q = load_realtime_queue(queuename)))
04200 return res;
04201
04202 ao2_lock(queues);
04203
04204 ao2_lock(q);
04205 if ((old_member = interface_exists(q, interface)) == NULL) {
04206 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04207 add_to_interfaces(new_member->state_interface);
04208 new_member->dynamic = 1;
04209 ao2_link(q->members, new_member);
04210 q->membercount++;
04211 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04212 "Queue: %s\r\n"
04213 "Location: %s\r\n"
04214 "MemberName: %s\r\n"
04215 "Membership: %s\r\n"
04216 "Penalty: %d\r\n"
04217 "CallsTaken: %d\r\n"
04218 "LastCall: %d\r\n"
04219 "Status: %d\r\n"
04220 "Paused: %d\r\n",
04221 q->name, new_member->interface, new_member->membername,
04222 "dynamic",
04223 new_member->penalty, new_member->calls, (int) new_member->lastcall,
04224 new_member->status, new_member->paused);
04225
04226 ao2_ref(new_member, -1);
04227 new_member = NULL;
04228
04229 if (dump)
04230 dump_queue_members(q);
04231
04232 res = RES_OKAY;
04233 } else {
04234 res = RES_OUTOFMEMORY;
04235 }
04236 } else {
04237 ao2_ref(old_member, -1);
04238 res = RES_EXISTS;
04239 }
04240 ao2_unlock(q);
04241 ao2_unlock(queues);
04242
04243 return res;
04244 }
04245
04246 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
04247 {
04248 int found = 0;
04249 struct call_queue *q;
04250 struct member *mem;
04251 struct ao2_iterator queue_iter;
04252 int failed;
04253
04254
04255
04256 if (ast_strlen_zero(queuename))
04257 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04258
04259 queue_iter = ao2_iterator_init(queues, 0);
04260 while ((q = ao2_iterator_next(&queue_iter))) {
04261 ao2_lock(q);
04262 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04263 if ((mem = interface_exists(q, interface))) {
04264 if (mem->paused == paused) {
04265 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04266 }
04267
04268 failed = 0;
04269 if (mem->realtime) {
04270 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04271 }
04272
04273 if (failed) {
04274 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04275 ao2_ref(mem, -1);
04276 ao2_unlock(q);
04277 continue;
04278 }
04279 found++;
04280 mem->paused = paused;
04281
04282 if (queue_persistent_members)
04283 dump_queue_members(q);
04284
04285 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04286
04287 if (!ast_strlen_zero(reason)) {
04288 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04289 "Queue: %s\r\n"
04290 "Location: %s\r\n"
04291 "MemberName: %s\r\n"
04292 "Paused: %d\r\n"
04293 "Reason: %s\r\n",
04294 q->name, mem->interface, mem->membername, paused, reason);
04295 } else {
04296 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04297 "Queue: %s\r\n"
04298 "Location: %s\r\n"
04299 "MemberName: %s\r\n"
04300 "Paused: %d\r\n",
04301 q->name, mem->interface, mem->membername, paused);
04302 }
04303 ao2_ref(mem, -1);
04304 }
04305 }
04306
04307 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04308 ao2_unlock(q);
04309 queue_unref(q);
04310 break;
04311 }
04312
04313 ao2_unlock(q);
04314 queue_unref(q);
04315 }
04316
04317 return found ? RESULT_SUCCESS : RESULT_FAILURE;
04318 }
04319
04320
04321 static int set_member_penalty(char *queuename, char *interface, int penalty)
04322 {
04323 int foundinterface = 0, foundqueue = 0;
04324 struct call_queue *q;
04325 struct member *mem;
04326 struct ao2_iterator queue_iter;
04327
04328 if (penalty < 0) {
04329 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04330 return RESULT_FAILURE;
04331 }
04332
04333 queue_iter = ao2_iterator_init(queues, 0);
04334 while ((q = ao2_iterator_next(&queue_iter))) {
04335 ao2_lock(q);
04336 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04337 foundqueue++;
04338 if ((mem = interface_exists(q, interface))) {
04339 foundinterface++;
04340 mem->penalty = penalty;
04341
04342 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04343 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04344 "Queue: %s\r\n"
04345 "Location: %s\r\n"
04346 "Penalty: %d\r\n",
04347 q->name, mem->interface, penalty);
04348 ao2_ref(mem, -1);
04349 }
04350 }
04351 ao2_unlock(q);
04352 queue_unref(q);
04353 }
04354
04355 if (foundinterface) {
04356 return RESULT_SUCCESS;
04357 } else if (!foundqueue) {
04358 ast_log (LOG_ERROR, "Invalid queuename\n");
04359 } else {
04360 ast_log (LOG_ERROR, "Invalid interface\n");
04361 }
04362
04363 return RESULT_FAILURE;
04364 }
04365
04366
04367
04368
04369 static int get_member_penalty(char *queuename, char *interface)
04370 {
04371 int foundqueue = 0, penalty;
04372 struct call_queue *q, tmpq = {
04373 .name = queuename,
04374 };
04375 struct member *mem;
04376
04377 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04378 foundqueue = 1;
04379 ao2_lock(q);
04380 if ((mem = interface_exists(q, interface))) {
04381 penalty = mem->penalty;
04382 ao2_ref(mem, -1);
04383 ao2_unlock(q);
04384 queue_unref(q);
04385 return penalty;
04386 }
04387 ao2_unlock(q);
04388 queue_unref(q);
04389 }
04390
04391
04392 if (foundqueue)
04393 ast_log (LOG_ERROR, "Invalid queuename\n");
04394 else
04395 ast_log (LOG_ERROR, "Invalid interface\n");
04396
04397 return RESULT_FAILURE;
04398 }
04399
04400
04401 static void reload_queue_members(void)
04402 {
04403 char *cur_ptr;
04404 const char *queue_name;
04405 char *member;
04406 char *interface;
04407 char *membername = NULL;
04408 char *state_interface;
04409 char *penalty_tok;
04410 int penalty = 0;
04411 char *paused_tok;
04412 int paused = 0;
04413 struct ast_db_entry *db_tree;
04414 struct ast_db_entry *entry;
04415 struct call_queue *cur_queue;
04416 char queue_data[PM_MAX_LEN];
04417
04418 ao2_lock(queues);
04419
04420
04421 db_tree = ast_db_gettree(pm_family, NULL);
04422 for (entry = db_tree; entry; entry = entry->next) {
04423
04424 queue_name = entry->key + strlen(pm_family) + 2;
04425
04426 {
04427 struct call_queue tmpq = {
04428 .name = queue_name,
04429 };
04430 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04431 }
04432
04433 if (!cur_queue)
04434 cur_queue = load_realtime_queue(queue_name);
04435
04436 if (!cur_queue) {
04437
04438
04439 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04440 ast_db_del(pm_family, queue_name);
04441 continue;
04442 }
04443
04444 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04445 queue_unref(cur_queue);
04446 continue;
04447 }
04448
04449 cur_ptr = queue_data;
04450 while ((member = strsep(&cur_ptr, ",|"))) {
04451 if (ast_strlen_zero(member))
04452 continue;
04453
04454 interface = strsep(&member, ";");
04455 penalty_tok = strsep(&member, ";");
04456 paused_tok = strsep(&member, ";");
04457 membername = strsep(&member, ";");
04458 state_interface = strsep(&member, ";");
04459
04460 if (!penalty_tok) {
04461 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04462 break;
04463 }
04464 penalty = strtol(penalty_tok, NULL, 10);
04465 if (errno == ERANGE) {
04466 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04467 break;
04468 }
04469
04470 if (!paused_tok) {
04471 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04472 break;
04473 }
04474 paused = strtol(paused_tok, NULL, 10);
04475 if ((errno == ERANGE) || paused < 0 || paused > 1) {
04476 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04477 break;
04478 }
04479
04480 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
04481
04482 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04483 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04484 break;
04485 }
04486 }
04487 queue_unref(cur_queue);
04488 }
04489
04490 ao2_unlock(queues);
04491 if (db_tree) {
04492 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04493 ast_db_freetree(db_tree);
04494 }
04495 }
04496
04497
04498 static int pqm_exec(struct ast_channel *chan, void *data)
04499 {
04500 char *parse;
04501 AST_DECLARE_APP_ARGS(args,
04502 AST_APP_ARG(queuename);
04503 AST_APP_ARG(interface);
04504 AST_APP_ARG(options);
04505 AST_APP_ARG(reason);
04506 );
04507
04508 if (ast_strlen_zero(data)) {
04509 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04510 return -1;
04511 }
04512
04513 parse = ast_strdupa(data);
04514
04515 AST_STANDARD_APP_ARGS(args, parse);
04516
04517 if (ast_strlen_zero(args.interface)) {
04518 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04519 return -1;
04520 }
04521
04522 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04523 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04524 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04525 return 0;
04526 }
04527
04528 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04529
04530 return 0;
04531 }
04532
04533
04534 static int upqm_exec(struct ast_channel *chan, void *data)
04535 {
04536 char *parse;
04537 AST_DECLARE_APP_ARGS(args,
04538 AST_APP_ARG(queuename);
04539 AST_APP_ARG(interface);
04540 AST_APP_ARG(options);
04541 AST_APP_ARG(reason);
04542 );
04543
04544 if (ast_strlen_zero(data)) {
04545 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04546 return -1;
04547 }
04548
04549 parse = ast_strdupa(data);
04550
04551 AST_STANDARD_APP_ARGS(args, parse);
04552
04553 if (ast_strlen_zero(args.interface)) {
04554 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04555 return -1;
04556 }
04557
04558 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04559 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04560 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04561 return 0;
04562 }
04563
04564 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04565
04566 return 0;
04567 }
04568
04569
04570 static int rqm_exec(struct ast_channel *chan, void *data)
04571 {
04572 int res=-1;
04573 char *parse, *temppos = NULL;
04574 AST_DECLARE_APP_ARGS(args,
04575 AST_APP_ARG(queuename);
04576 AST_APP_ARG(interface);
04577 AST_APP_ARG(options);
04578 );
04579
04580
04581 if (ast_strlen_zero(data)) {
04582 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04583 return -1;
04584 }
04585
04586 parse = ast_strdupa(data);
04587
04588 AST_STANDARD_APP_ARGS(args, parse);
04589
04590 if (ast_strlen_zero(args.interface)) {
04591 args.interface = ast_strdupa(chan->name);
04592 temppos = strrchr(args.interface, '-');
04593 if (temppos)
04594 *temppos = '\0';
04595 }
04596
04597 switch (remove_from_queue(args.queuename, args.interface)) {
04598 case RES_OKAY:
04599 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04600 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04601 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04602 res = 0;
04603 break;
04604 case RES_EXISTS:
04605 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04606 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04607 res = 0;
04608 break;
04609 case RES_NOSUCHQUEUE:
04610 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04611 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04612 res = 0;
04613 break;
04614 case RES_NOT_DYNAMIC:
04615 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04616 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04617 res = 0;
04618 break;
04619 }
04620
04621 return res;
04622 }
04623
04624
04625 static int aqm_exec(struct ast_channel *chan, void *data)
04626 {
04627 int res=-1;
04628 char *parse, *temppos = NULL;
04629 AST_DECLARE_APP_ARGS(args,
04630 AST_APP_ARG(queuename);
04631 AST_APP_ARG(interface);
04632 AST_APP_ARG(penalty);
04633 AST_APP_ARG(options);
04634 AST_APP_ARG(membername);
04635 AST_APP_ARG(state_interface);
04636 );
04637 int penalty = 0;
04638
04639 if (ast_strlen_zero(data)) {
04640 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04641 return -1;
04642 }
04643
04644 parse = ast_strdupa(data);
04645
04646 AST_STANDARD_APP_ARGS(args, parse);
04647
04648 if (ast_strlen_zero(args.interface)) {
04649 args.interface = ast_strdupa(chan->name);
04650 temppos = strrchr(args.interface, '-');
04651 if (temppos)
04652 *temppos = '\0';
04653 }
04654
04655 if (!ast_strlen_zero(args.penalty)) {
04656 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04657 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04658 penalty = 0;
04659 }
04660 }
04661
04662 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04663 case RES_OKAY:
04664 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04665 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04666 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04667 res = 0;
04668 break;
04669 case RES_EXISTS:
04670 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04671 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04672 res = 0;
04673 break;
04674 case RES_NOSUCHQUEUE:
04675 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04676 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04677 res = 0;
04678 break;
04679 case RES_OUTOFMEMORY:
04680 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04681 break;
04682 }
04683
04684 return res;
04685 }
04686
04687
04688 static int ql_exec(struct ast_channel *chan, void *data)
04689 {
04690 char *parse;
04691
04692 AST_DECLARE_APP_ARGS(args,
04693 AST_APP_ARG(queuename);
04694 AST_APP_ARG(uniqueid);
04695 AST_APP_ARG(membername);
04696 AST_APP_ARG(event);
04697 AST_APP_ARG(params);
04698 );
04699
04700 if (ast_strlen_zero(data)) {
04701 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04702 return -1;
04703 }
04704
04705 parse = ast_strdupa(data);
04706
04707 AST_STANDARD_APP_ARGS(args, parse);
04708
04709 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04710 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04711 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04712 return -1;
04713 }
04714
04715 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
04716 "%s", args.params ? args.params : "");
04717
04718 return 0;
04719 }
04720
04721
04722 static void copy_rules(struct queue_ent *qe, const char *rulename)
04723 {
04724 struct penalty_rule *pr_iter;
04725 struct rule_list *rl_iter;
04726 const char *tmp = rulename;
04727 if (ast_strlen_zero(tmp)) {
04728 return;
04729 }
04730 AST_LIST_LOCK(&rule_lists);
04731 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04732 if (!strcasecmp(rl_iter->name, tmp))
04733 break;
04734 }
04735 if (rl_iter) {
04736 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04737 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04738 if (!new_pr) {
04739 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04740 AST_LIST_UNLOCK(&rule_lists);
04741 break;
04742 }
04743 new_pr->time = pr_iter->time;
04744 new_pr->max_value = pr_iter->max_value;
04745 new_pr->min_value = pr_iter->min_value;
04746 new_pr->max_relative = pr_iter->max_relative;
04747 new_pr->min_relative = pr_iter->min_relative;
04748 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04749 }
04750 }
04751 AST_LIST_UNLOCK(&rule_lists);
04752 }
04753
04754
04755
04756
04757
04758
04759
04760
04761
04762
04763
04764
04765
04766 static int queue_exec(struct ast_channel *chan, void *data)
04767 {
04768 int res=-1;
04769 int ringing=0;
04770 const char *user_priority;
04771 const char *max_penalty_str;
04772 const char *min_penalty_str;
04773 int prio;
04774 int qcontinue = 0;
04775 int max_penalty, min_penalty;
04776 enum queue_result reason = QUEUE_UNKNOWN;
04777
04778 int tries = 0;
04779 int noption = 0;
04780 char *parse;
04781 int makeannouncement = 0;
04782 AST_DECLARE_APP_ARGS(args,
04783 AST_APP_ARG(queuename);
04784 AST_APP_ARG(options);
04785 AST_APP_ARG(url);
04786 AST_APP_ARG(announceoverride);
04787 AST_APP_ARG(queuetimeoutstr);
04788 AST_APP_ARG(agi);
04789 AST_APP_ARG(macro);
04790 AST_APP_ARG(gosub);
04791 AST_APP_ARG(rule);
04792 );
04793
04794 struct queue_ent qe;
04795
04796 if (ast_strlen_zero(data)) {
04797 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
04798 return -1;
04799 }
04800
04801 parse = ast_strdupa(data);
04802 AST_STANDARD_APP_ARGS(args, parse);
04803
04804
04805 memset(&qe, 0, sizeof(qe));
04806 qe.start = time(NULL);
04807
04808
04809 if (!ast_strlen_zero(args.queuetimeoutstr))
04810 qe.expire = qe.start + atoi(args.queuetimeoutstr);
04811 else
04812 qe.expire = 0;
04813
04814
04815 ast_channel_lock(chan);
04816 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04817 if (user_priority) {
04818 if (sscanf(user_priority, "%30d", &prio) == 1) {
04819 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04820 } else {
04821 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04822 user_priority, chan->name);
04823 prio = 0;
04824 }
04825 } else {
04826 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04827 prio = 0;
04828 }
04829
04830
04831
04832 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04833 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04834 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04835 } else {
04836 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04837 max_penalty_str, chan->name);
04838 max_penalty = 0;
04839 }
04840 } else {
04841 max_penalty = 0;
04842 }
04843
04844 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04845 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
04846 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04847 } else {
04848 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04849 min_penalty_str, chan->name);
04850 min_penalty = 0;
04851 }
04852 } else {
04853 min_penalty = 0;
04854 }
04855 ast_channel_unlock(chan);
04856
04857 if (args.options && (strchr(args.options, 'r')))
04858 ringing = 1;
04859
04860 if (args.options && (strchr(args.options, 'c')))
04861 qcontinue = 1;
04862
04863 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04864 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04865
04866 qe.chan = chan;
04867 qe.prio = prio;
04868 qe.max_penalty = max_penalty;
04869 qe.min_penalty = min_penalty;
04870 qe.last_pos_said = 0;
04871 qe.last_pos = 0;
04872 qe.last_periodic_announce_time = time(NULL);
04873 qe.last_periodic_announce_sound = 0;
04874 qe.valid_digits = 0;
04875 if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04876 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04877 set_queue_result(chan, reason);
04878 return 0;
04879 }
04880 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04881 S_OR(chan->cid.cid_num, ""));
04882 check_turns:
04883 if (ringing) {
04884 ast_indicate(chan, AST_CONTROL_RINGING);
04885 } else {
04886 ast_moh_start(chan, qe.moh, NULL);
04887 }
04888
04889
04890 res = wait_our_turn(&qe, ringing, &reason);
04891 if (res) {
04892 goto stop;
04893 }
04894
04895 makeannouncement = 0;
04896
04897 for (;;) {
04898
04899
04900
04901
04902
04903 enum queue_member_status status = QUEUE_NORMAL;
04904 int exit = 0;
04905
04906
04907 if (qe.expire && (time(NULL) >= qe.expire)) {
04908 record_abandoned(&qe);
04909 ast_cdr_noanswer(qe.chan->cdr);
04910 reason = QUEUE_TIMEOUT;
04911 res = 0;
04912 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
04913 qe.pos, qe.opos, (long) time(NULL) - qe.start);
04914 break;
04915 }
04916
04917 if (makeannouncement) {
04918
04919 if (qe.parent->announcefrequency)
04920 if ((res = say_position(&qe,ringing)))
04921 goto stop;
04922 }
04923 makeannouncement = 1;
04924
04925
04926 if (qe.parent->periodicannouncefrequency)
04927 if ((res = say_periodic_announcement(&qe,ringing)))
04928 goto stop;
04929
04930
04931 if (qe.expire && (time(NULL) >= qe.expire)) {
04932 record_abandoned(&qe);
04933 ast_cdr_noanswer(qe.chan->cdr);
04934 reason = QUEUE_TIMEOUT;
04935 res = 0;
04936 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04937 break;
04938 }
04939
04940
04941 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04942 update_qe_rule(&qe);
04943 }
04944
04945
04946 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04947 if (res) {
04948 goto stop;
04949 }
04950
04951
04952 if (noption && tries >= qe.parent->membercount) {
04953 ast_verb(3, "Exiting on time-out cycle\n");
04954 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04955 record_abandoned(&qe);
04956 ast_cdr_noanswer(qe.chan->cdr);
04957 reason = QUEUE_TIMEOUT;
04958 res = 0;
04959 break;
04960 }
04961
04962 for (; !exit || qe.pr; update_qe_rule(&qe)) {
04963 status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04964
04965 if (!qe.pr || status == QUEUE_NORMAL) {
04966 break;
04967 }
04968
04969 if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
04970 ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04971 ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
04972 continue;
04973 } else {
04974 exit = 1;
04975 }
04976 }
04977
04978
04979 if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
04980 record_abandoned(&qe);
04981 ast_cdr_noanswer(qe.chan->cdr);
04982 reason = QUEUE_LEAVEEMPTY;
04983 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04984 res = 0;
04985 break;
04986 }
04987
04988
04989 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04990 record_abandoned(&qe);
04991 reason = QUEUE_LEAVEUNAVAIL;
04992 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04993 res = 0;
04994 break;
04995 }
04996 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
04997 record_abandoned(&qe);
04998 reason = QUEUE_LEAVEUNAVAIL;
04999 res = 0;
05000 break;
05001 }
05002
05003
05004 if (qe.expire && (time(NULL) >= qe.expire)) {
05005 record_abandoned(&qe);
05006 reason = QUEUE_TIMEOUT;
05007 res = 0;
05008 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05009 break;
05010 }
05011
05012
05013 update_realtime_members(qe.parent);
05014
05015 res = wait_a_bit(&qe);
05016 if (res)
05017 goto stop;
05018
05019
05020
05021
05022
05023 if (!is_our_turn(&qe)) {
05024 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05025 goto check_turns;
05026 }
05027 }
05028
05029 stop:
05030 if (res) {
05031 if (res < 0) {
05032 if (!qe.handled) {
05033 record_abandoned(&qe);
05034 ast_cdr_noanswer(qe.chan->cdr);
05035 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05036 "%d|%d|%ld", qe.pos, qe.opos,
05037 (long) time(NULL) - qe.start);
05038 res = -1;
05039 } else if (qcontinue) {
05040 reason = QUEUE_CONTINUE;
05041 res = 0;
05042 }
05043 } else if (qe.valid_digits) {
05044 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05045 "%s|%d", qe.digits, qe.pos);
05046 }
05047 }
05048
05049
05050 if (res >= 0) {
05051 res = 0;
05052 if (ringing) {
05053 ast_indicate(chan, -1);
05054 } else {
05055 ast_moh_stop(chan);
05056 }
05057 ast_stopstream(chan);
05058 }
05059
05060 set_queue_variables(qe.parent, qe.chan);
05061
05062 leave_queue(&qe);
05063 if (reason != QUEUE_UNKNOWN)
05064 set_queue_result(chan, reason);
05065
05066 return res;
05067 }
05068
05069
05070
05071
05072
05073
05074 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05075 {
05076 int res = -1;
05077 struct call_queue *q, tmpq = {
05078 .name = data,
05079 };
05080
05081 char interfacevar[256] = "";
05082 float sl = 0;
05083
05084 if (ast_strlen_zero(data)) {
05085 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05086 return -1;
05087 }
05088
05089 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05090 ao2_lock(q);
05091 if (q->setqueuevar) {
05092 sl = 0;
05093 res = 0;
05094
05095 if (q->callscompleted > 0) {
05096 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05097 }
05098
05099 snprintf(interfacevar, sizeof(interfacevar),
05100 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05101 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
05102
05103 pbx_builtin_setvar_multiple(chan, interfacevar);
05104 }
05105
05106 ao2_unlock(q);
05107 queue_unref(q);
05108 } else {
05109 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05110 }
05111
05112 snprintf(buf, len, "%d", res);
05113
05114 return 0;
05115 }
05116
05117
05118
05119
05120
05121
05122 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05123 {
05124 int count = 0;
05125 struct member *m;
05126 struct ao2_iterator mem_iter;
05127 struct call_queue *q;
05128 char *option;
05129
05130 if (ast_strlen_zero(data)) {
05131 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05132 return -1;
05133 }
05134
05135 if ((option = strchr(data, ',')))
05136 *option++ = '\0';
05137 else
05138 option = "logged";
05139 if ((q = load_realtime_queue(data))) {
05140 ao2_lock(q);
05141 if (!strcasecmp(option, "logged")) {
05142 mem_iter = ao2_iterator_init(q->members, 0);
05143 while ((m = ao2_iterator_next(&mem_iter))) {
05144
05145 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05146 count++;
05147 }
05148 ao2_ref(m, -1);
05149 }
05150 } else if (!strcasecmp(option, "free")) {
05151 mem_iter = ao2_iterator_init(q->members, 0);
05152 while ((m = ao2_iterator_next(&mem_iter))) {
05153
05154 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05155 count++;
05156 }
05157 ao2_ref(m, -1);
05158 }
05159 } else
05160 count = q->membercount;
05161 ao2_unlock(q);
05162 queue_unref(q);
05163 } else
05164 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05165
05166 snprintf(buf, len, "%d", count);
05167
05168 return 0;
05169 }
05170
05171
05172
05173
05174
05175
05176 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05177 {
05178 int count = 0;
05179 struct member *m;
05180 struct call_queue *q;
05181 struct ao2_iterator mem_iter;
05182 static int depflag = 1;
05183
05184 if (depflag) {
05185 depflag = 0;
05186 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");
05187 }
05188
05189 if (ast_strlen_zero(data)) {
05190 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05191 return -1;
05192 }
05193
05194 if ((q = load_realtime_queue(data))) {
05195 ao2_lock(q);
05196 mem_iter = ao2_iterator_init(q->members, 0);
05197 while ((m = ao2_iterator_next(&mem_iter))) {
05198
05199 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05200 count++;
05201 }
05202 ao2_ref(m, -1);
05203 }
05204 ao2_unlock(q);
05205 queue_unref(q);
05206 } else
05207 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05208
05209 snprintf(buf, len, "%d", count);
05210
05211 return 0;
05212 }
05213
05214
05215 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05216 {
05217 int count = 0;
05218 struct call_queue *q, tmpq = {
05219 .name = data,
05220 };
05221 struct ast_variable *var = NULL;
05222
05223 buf[0] = '\0';
05224
05225 if (ast_strlen_zero(data)) {
05226 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05227 return -1;
05228 }
05229
05230 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05231 ao2_lock(q);
05232 count = q->count;
05233 ao2_unlock(q);
05234 queue_unref(q);
05235 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05236
05237
05238
05239
05240 count = 0;
05241 ast_variables_destroy(var);
05242 } else
05243 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05244
05245 snprintf(buf, len, "%d", count);
05246
05247 return 0;
05248 }
05249
05250
05251 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05252 {
05253 struct call_queue *q, tmpq = {
05254 .name = data,
05255 };
05256 struct member *m;
05257
05258
05259 buf[0] = '\0';
05260
05261 if (ast_strlen_zero(data)) {
05262 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05263 return -1;
05264 }
05265
05266 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05267 int buflen = 0, count = 0;
05268 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05269
05270 ao2_lock(q);
05271 while ((m = ao2_iterator_next(&mem_iter))) {
05272
05273 if (count++) {
05274 strncat(buf + buflen, ",", len - buflen - 1);
05275 buflen++;
05276 }
05277 strncat(buf + buflen, m->interface, len - buflen - 1);
05278 buflen += strlen(m->interface);
05279
05280 if (buflen >= len - 2) {
05281 ao2_ref(m, -1);
05282 ast_log(LOG_WARNING, "Truncating list\n");
05283 break;
05284 }
05285 ao2_ref(m, -1);
05286 }
05287 ao2_unlock(q);
05288 queue_unref(q);
05289 } else
05290 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05291
05292
05293 buf[len - 1] = '\0';
05294
05295 return 0;
05296 }
05297
05298
05299 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05300 {
05301 int penalty;
05302 AST_DECLARE_APP_ARGS(args,
05303 AST_APP_ARG(queuename);
05304 AST_APP_ARG(interface);
05305 );
05306
05307 buf[0] = '\0';
05308
05309 if (ast_strlen_zero(data)) {
05310 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05311 return -1;
05312 }
05313
05314 AST_STANDARD_APP_ARGS(args, data);
05315
05316 if (args.argc < 2) {
05317 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05318 return -1;
05319 }
05320
05321 penalty = get_member_penalty (args.queuename, args.interface);
05322
05323 if (penalty >= 0)
05324 snprintf (buf, len, "%d", penalty);
05325
05326 return 0;
05327 }
05328
05329
05330 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
05331 {
05332 int penalty;
05333 AST_DECLARE_APP_ARGS(args,
05334 AST_APP_ARG(queuename);
05335 AST_APP_ARG(interface);
05336 );
05337
05338 if (ast_strlen_zero(data)) {
05339 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05340 return -1;
05341 }
05342
05343 AST_STANDARD_APP_ARGS(args, data);
05344
05345 if (args.argc < 2) {
05346 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05347 return -1;
05348 }
05349
05350 penalty = atoi(value);
05351
05352 if (ast_strlen_zero(args.interface)) {
05353 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05354 return -1;
05355 }
05356
05357
05358 if (set_member_penalty(args.queuename, args.interface, penalty)) {
05359 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05360 return -1;
05361 }
05362
05363 return 0;
05364 }
05365
05366 static struct ast_custom_function queuevar_function = {
05367 .name = "QUEUE_VARIABLES",
05368 .synopsis = "Return Queue information in variables",
05369 .syntax = "QUEUE_VARIABLES(<queuename>)",
05370 .desc =
05371 "Makes the following queue variables available.\n"
05372 "QUEUEMAX maxmimum number of calls allowed\n"
05373 "QUEUESTRATEGY the strategy of the queue\n"
05374 "QUEUECALLS number of calls currently in the queue\n"
05375 "QUEUEHOLDTIME current average hold time\n"
05376 "QUEUECOMPLETED number of completed calls for the queue\n"
05377 "QUEUEABANDONED number of abandoned calls\n"
05378 "QUEUESRVLEVEL queue service level\n"
05379 "QUEUESRVLEVELPERF current service level performance\n"
05380 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
05381 .read = queue_function_var,
05382 };
05383
05384 static struct ast_custom_function queuemembercount_function = {
05385 .name = "QUEUE_MEMBER",
05386 .synopsis = "Count number of members answering a queue",
05387 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
05388 .desc =
05389 "Returns the number of members currently associated with the specified queue.\n"
05390 "One of three options may be passed to determine the count returned:\n"
05391 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
05392 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
05393 "\"count\" - Returns the total number of members for the specified queue\n",
05394 .read = queue_function_qac,
05395 };
05396
05397 static struct ast_custom_function queuemembercount_dep = {
05398 .name = "QUEUE_MEMBER_COUNT",
05399 .synopsis = "Count number of members answering a queue",
05400 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
05401 .desc =
05402 "Returns the number of members currently associated with the specified queue.\n\n"
05403 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
05404 .read = queue_function_qac_dep,
05405 };
05406
05407 static struct ast_custom_function queuewaitingcount_function = {
05408 .name = "QUEUE_WAITING_COUNT",
05409 .synopsis = "Count number of calls currently waiting in a queue",
05410 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
05411 .desc =
05412 "Returns the number of callers currently waiting in the specified queue.\n",
05413 .read = queue_function_queuewaitingcount,
05414 };
05415
05416 static struct ast_custom_function queuememberlist_function = {
05417 .name = "QUEUE_MEMBER_LIST",
05418 .synopsis = "Returns a list of interfaces on a queue",
05419 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
05420 .desc =
05421 "Returns a comma-separated list of members associated with the specified queue.\n",
05422 .read = queue_function_queuememberlist,
05423 };
05424
05425 static struct ast_custom_function queuememberpenalty_function = {
05426 .name = "QUEUE_MEMBER_PENALTY",
05427 .synopsis = "Gets or sets queue members penalty.",
05428 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
05429 .desc =
05430 "Gets or sets queue members penalty\n",
05431 .read = queue_function_memberpenalty_read,
05432 .write = queue_function_memberpenalty_write,
05433 };
05434
05435 static int reload_queue_rules(int reload)
05436 {
05437 struct ast_config *cfg;
05438 struct rule_list *rl_iter, *new_rl;
05439 struct penalty_rule *pr_iter;
05440 char *rulecat = NULL;
05441 struct ast_variable *rulevar = NULL;
05442 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05443
05444 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05445 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05446 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05447 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05448 return AST_MODULE_LOAD_SUCCESS;
05449 } else {
05450 AST_LIST_LOCK(&rule_lists);
05451 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05452 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05453 ast_free(pr_iter);
05454 ast_free(rl_iter);
05455 }
05456 while ((rulecat = ast_category_browse(cfg, rulecat))) {
05457 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05458 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05459 AST_LIST_UNLOCK(&rule_lists);
05460 return AST_MODULE_LOAD_FAILURE;
05461 } else {
05462 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05463 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05464 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05465 if(!strcasecmp(rulevar->name, "penaltychange")) {
05466 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05467 } else {
05468 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05469 }
05470 }
05471 }
05472 AST_LIST_UNLOCK(&rule_lists);
05473 }
05474
05475 ast_config_destroy(cfg);
05476
05477 return AST_MODULE_LOAD_SUCCESS;
05478 }
05479
05480
05481 static int reload_queues(int reload)
05482 {
05483 struct call_queue *q;
05484 struct ast_config *cfg;
05485 char *cat, *tmp;
05486 struct ast_variable *var;
05487 struct member *cur, *newm;
05488 struct ao2_iterator mem_iter;
05489 int new;
05490 const char *general_val = NULL;
05491 char *parse;
05492 char *interface, *state_interface;
05493 char *membername = NULL;
05494 int penalty;
05495 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05496 struct ao2_iterator queue_iter;
05497 AST_DECLARE_APP_ARGS(args,
05498 AST_APP_ARG(interface);
05499 AST_APP_ARG(penalty);
05500 AST_APP_ARG(membername);
05501 AST_APP_ARG(state_interface);
05502 );
05503
05504
05505 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05506 return AST_MODULE_LOAD_FAILURE;
05507
05508 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05509 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05510 return 0;
05511 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05512 return 0;
05513 ao2_lock(queues);
05514 use_weight=0;
05515
05516 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05517 while ((q = ao2_iterator_next(&queue_iter))) {
05518 if (!q->realtime) {
05519 q->dead = 1;
05520 q->found = 0;
05521 }
05522 queue_unref(q);
05523 }
05524
05525
05526 cat = NULL;
05527 while ((cat = ast_category_browse(cfg, cat)) ) {
05528 if (!strcasecmp(cat, "general")) {
05529
05530 queue_keep_stats = 0;
05531 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05532 queue_keep_stats = ast_true(general_val);
05533 queue_persistent_members = 0;
05534 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05535 queue_persistent_members = ast_true(general_val);
05536 autofill_default = 0;
05537 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05538 autofill_default = ast_true(general_val);
05539 montype_default = 0;
05540 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05541 if (!strcasecmp(general_val, "mixmonitor"))
05542 montype_default = 1;
05543 }
05544 update_cdr = 0;
05545 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05546 update_cdr = ast_true(general_val);
05547 shared_lastcall = 0;
05548 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05549 shared_lastcall = ast_true(general_val);
05550 } else {
05551
05552 struct call_queue tmpq = {
05553 .name = cat,
05554 };
05555 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05556
05557 if (!(q = alloc_queue(cat))) {
05558
05559 }
05560 new = 1;
05561 } else
05562 new = 0;
05563 if (q) {
05564 const char *tmpvar = NULL;
05565 if (!new)
05566 ao2_lock(q);
05567
05568 if (q->found) {
05569 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05570 if (!new) {
05571 ao2_unlock(q);
05572 queue_unref(q);
05573 }
05574 continue;
05575 }
05576
05577
05578
05579 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05580 q->strategy = strat2int(tmpvar);
05581 if (q->strategy < 0) {
05582 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05583 tmpvar, q->name);
05584 q->strategy = QUEUE_STRATEGY_RINGALL;
05585 }
05586 } else
05587 q->strategy = QUEUE_STRATEGY_RINGALL;
05588
05589 init_queue(q);
05590 if (!queue_keep_stats)
05591 clear_queue(q);
05592 mem_iter = ao2_iterator_init(q->members, 0);
05593 while ((cur = ao2_iterator_next(&mem_iter))) {
05594 if (!cur->dynamic) {
05595 cur->delme = 1;
05596 }
05597 ao2_ref(cur, -1);
05598 }
05599 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05600 if (!strcasecmp(var->name, "member")) {
05601 struct member tmpmem;
05602 membername = NULL;
05603
05604 if (ast_strlen_zero(var->value)) {
05605 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05606 continue;
05607 }
05608
05609
05610 if (!(parse = ast_strdup(var->value))) {
05611 continue;
05612 }
05613
05614 AST_STANDARD_APP_ARGS(args, parse);
05615
05616 interface = args.interface;
05617 if (!ast_strlen_zero(args.penalty)) {
05618 tmp = args.penalty;
05619 while (*tmp && *tmp < 33) tmp++;
05620 penalty = atoi(tmp);
05621 if (penalty < 0) {
05622 penalty = 0;
05623 }
05624 } else
05625 penalty = 0;
05626
05627 if (!ast_strlen_zero(args.membername)) {
05628 membername = args.membername;
05629 while (*membername && *membername < 33) membername++;
05630 }
05631
05632 if (!ast_strlen_zero(args.state_interface)) {
05633 state_interface = args.state_interface;
05634 while (*state_interface && *state_interface < 33) state_interface++;
05635 } else
05636 state_interface = interface;
05637
05638
05639 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05640 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05641
05642 if (cur && strcasecmp(cur->state_interface, state_interface)) {
05643 remove_from_interfaces(cur->state_interface, 0);
05644 }
05645 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05646 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05647 add_to_interfaces(state_interface);
05648 ao2_link(q->members, newm);
05649 ao2_ref(newm, -1);
05650 newm = NULL;
05651
05652 if (cur)
05653 ao2_ref(cur, -1);
05654 else {
05655 q->membercount++;
05656 }
05657 ast_free(parse);
05658 } else {
05659 queue_set_param(q, var->name, var->value, var->lineno, 1);
05660 }
05661 }
05662
05663
05664 mem_iter = ao2_iterator_init(q->members, 0);
05665 while ((cur = ao2_iterator_next(&mem_iter))) {
05666 if (! cur->delme) {
05667 ao2_ref(cur, -1);
05668 continue;
05669 }
05670 q->membercount--;
05671 ao2_unlink(q->members, cur);
05672 remove_from_interfaces(cur->interface, 0);
05673 ao2_ref(cur, -1);
05674 }
05675
05676 if (new) {
05677 ao2_link(queues, q);
05678 } else
05679 ao2_unlock(q);
05680 queue_unref(q);
05681 }
05682 }
05683 }
05684 ast_config_destroy(cfg);
05685 queue_iter = ao2_iterator_init(queues, 0);
05686 while ((q = ao2_iterator_next(&queue_iter))) {
05687 if (q->dead) {
05688 ao2_unlink(queues, q);
05689 } else {
05690 ao2_lock(q);
05691 mem_iter = ao2_iterator_init(q->members, 0);
05692 while ((cur = ao2_iterator_next(&mem_iter))) {
05693 if (cur->dynamic)
05694 q->membercount++;
05695 cur->status = ast_device_state(cur->state_interface);
05696 ao2_ref(cur, -1);
05697 }
05698 ao2_unlock(q);
05699 }
05700 queue_unref(q);
05701 }
05702 ao2_unlock(queues);
05703 return 1;
05704 }
05705
05706
05707 static void do_print(struct mansession *s, int fd, const char *str)
05708 {
05709 if (s)
05710 astman_append(s, "%s\r\n", str);
05711 else
05712 ast_cli(fd, "%s\n", str);
05713 }
05714
05715
05716
05717
05718
05719
05720
05721 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
05722 {
05723 struct call_queue *q;
05724 struct ast_str *out = ast_str_alloca(240);
05725 int found = 0;
05726 time_t now = time(NULL);
05727 struct ao2_iterator queue_iter;
05728 struct ao2_iterator mem_iter;
05729
05730 if (argc != 2 && argc != 3)
05731 return CLI_SHOWUSAGE;
05732
05733 if (argc == 3) {
05734 if ((q = load_realtime_queue(argv[2]))) {
05735 queue_unref(q);
05736 }
05737 } else if (ast_check_realtime("queues")) {
05738
05739
05740
05741 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05742 char *queuename;
05743 if (cfg) {
05744 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05745 if ((q = load_realtime_queue(queuename))) {
05746 queue_unref(q);
05747 }
05748 }
05749 ast_config_destroy(cfg);
05750 }
05751 }
05752
05753 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05754 ao2_lock(queues);
05755 while ((q = ao2_iterator_next(&queue_iter))) {
05756 float sl;
05757 struct call_queue *realtime_queue = NULL;
05758
05759 ao2_lock(q);
05760
05761
05762
05763
05764 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05765 ao2_unlock(q);
05766 queue_unref(q);
05767 continue;
05768 } else if (q->realtime) {
05769 queue_unref(realtime_queue);
05770 }
05771 if (argc == 3 && strcasecmp(q->name, argv[2])) {
05772 ao2_unlock(q);
05773 queue_unref(q);
05774 continue;
05775 }
05776 found = 1;
05777
05778 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05779 if (q->maxlen)
05780 ast_str_append(&out, 0, "%d", q->maxlen);
05781 else
05782 ast_str_append(&out, 0, "unlimited");
05783 sl = 0;
05784 if (q->callscompleted > 0)
05785 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05786 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05787 int2strat(q->strategy), q->holdtime, q->weight,
05788 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05789 do_print(s, fd, out->str);
05790 if (!ao2_container_count(q->members))
05791 do_print(s, fd, " No Members");
05792 else {
05793 struct member *mem;
05794
05795 do_print(s, fd, " Members: ");
05796 mem_iter = ao2_iterator_init(q->members, 0);
05797 while ((mem = ao2_iterator_next(&mem_iter))) {
05798 ast_str_set(&out, 0, " %s", mem->membername);
05799 if (strcasecmp(mem->membername, mem->interface)) {
05800 ast_str_append(&out, 0, " (%s)", mem->interface);
05801 }
05802 if (mem->penalty)
05803 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05804 ast_str_append(&out, 0, "%s%s%s (%s)",
05805 mem->dynamic ? " (dynamic)" : "",
05806 mem->realtime ? " (realtime)" : "",
05807 mem->paused ? " (paused)" : "",
05808 devstate2str(mem->status));
05809 if (mem->calls)
05810 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05811 mem->calls, (long) (time(NULL) - mem->lastcall));
05812 else
05813 ast_str_append(&out, 0, " has taken no calls yet");
05814 do_print(s, fd, out->str);
05815 ao2_ref(mem, -1);
05816 }
05817 }
05818 if (!q->head)
05819 do_print(s, fd, " No Callers");
05820 else {
05821 struct queue_ent *qe;
05822 int pos = 1;
05823
05824 do_print(s, fd, " Callers: ");
05825 for (qe = q->head; qe; qe = qe->next) {
05826 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
05827 pos++, qe->chan->name, (long) (now - qe->start) / 60,
05828 (long) (now - qe->start) % 60, qe->prio);
05829 do_print(s, fd, out->str);
05830 }
05831 }
05832 do_print(s, fd, "");
05833 ao2_unlock(q);
05834 queue_unref(q);
05835 }
05836 ao2_unlock(queues);
05837 if (!found) {
05838 if (argc == 3)
05839 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05840 else
05841 ast_str_set(&out, 0, "No queues.");
05842 do_print(s, fd, out->str);
05843 }
05844 return CLI_SUCCESS;
05845 }
05846
05847 static char *complete_queue(const char *line, const char *word, int pos, int state)
05848 {
05849 struct call_queue *q;
05850 char *ret = NULL;
05851 int which = 0;
05852 int wordlen = strlen(word);
05853 struct ao2_iterator queue_iter;
05854
05855 queue_iter = ao2_iterator_init(queues, 0);
05856 while ((q = ao2_iterator_next(&queue_iter))) {
05857 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05858 ret = ast_strdup(q->name);
05859 queue_unref(q);
05860 break;
05861 }
05862 queue_unref(q);
05863 }
05864
05865 return ret;
05866 }
05867
05868 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
05869 {
05870 if (pos == 2)
05871 return complete_queue(line, word, pos, state);
05872 return NULL;
05873 }
05874
05875 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
05876 {
05877 switch ( cmd ) {
05878 case CLI_INIT:
05879 e->command = "queue show";
05880 e->usage =
05881 "Usage: queue show\n"
05882 " Provides summary information on a specified queue.\n";
05883 return NULL;
05884 case CLI_GENERATE:
05885 return complete_queue_show(a->line, a->word, a->pos, a->n);
05886 }
05887
05888 return __queues_show(NULL, a->fd, a->argc, a->argv);
05889 }
05890
05891
05892
05893
05894 static int manager_queues_show(struct mansession *s, const struct message *m)
05895 {
05896 char *a[] = { "queue", "show" };
05897
05898 __queues_show(s, -1, 2, a);
05899 astman_append(s, "\r\n\r\n");
05900
05901 return RESULT_SUCCESS;
05902 }
05903
05904 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
05905 {
05906 const char *rule = astman_get_header(m, "Rule");
05907 struct rule_list *rl_iter;
05908 struct penalty_rule *pr_iter;
05909
05910 AST_LIST_LOCK(&rule_lists);
05911 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05912 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05913 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05914 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05915 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 );
05916 }
05917 if (!ast_strlen_zero(rule))
05918 break;
05919 }
05920 }
05921 AST_LIST_UNLOCK(&rule_lists);
05922
05923 astman_append(s, "\r\n\r\n");
05924
05925 return RESULT_SUCCESS;
05926 }
05927
05928
05929 static int manager_queues_summary(struct mansession *s, const struct message *m)
05930 {
05931 time_t now;
05932 int qmemcount = 0;
05933 int qmemavail = 0;
05934 int qchancount = 0;
05935 int qlongestholdtime = 0;
05936 const char *id = astman_get_header(m, "ActionID");
05937 const char *queuefilter = astman_get_header(m, "Queue");
05938 char idText[256] = "";
05939 struct call_queue *q;
05940 struct queue_ent *qe;
05941 struct member *mem;
05942 struct ao2_iterator queue_iter;
05943 struct ao2_iterator mem_iter;
05944
05945 astman_send_ack(s, m, "Queue summary will follow");
05946 time(&now);
05947 if (!ast_strlen_zero(id))
05948 snprintf(idText, 256, "ActionID: %s\r\n", id);
05949 queue_iter = ao2_iterator_init(queues, 0);
05950 while ((q = ao2_iterator_next(&queue_iter))) {
05951 ao2_lock(q);
05952
05953
05954 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05955
05956 qmemcount = 0;
05957 qmemavail = 0;
05958 qchancount = 0;
05959 qlongestholdtime = 0;
05960
05961
05962 mem_iter = ao2_iterator_init(q->members, 0);
05963 while ((mem = ao2_iterator_next(&mem_iter))) {
05964 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05965 ++qmemcount;
05966 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05967 ++qmemavail;
05968 }
05969 }
05970 ao2_ref(mem, -1);
05971 }
05972 for (qe = q->head; qe; qe = qe->next) {
05973 if ((now - qe->start) > qlongestholdtime) {
05974 qlongestholdtime = now - qe->start;
05975 }
05976 ++qchancount;
05977 }
05978 astman_append(s, "Event: QueueSummary\r\n"
05979 "Queue: %s\r\n"
05980 "LoggedIn: %d\r\n"
05981 "Available: %d\r\n"
05982 "Callers: %d\r\n"
05983 "HoldTime: %d\r\n"
05984 "LongestHoldTime: %d\r\n"
05985 "%s"
05986 "\r\n",
05987 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05988 }
05989 ao2_unlock(q);
05990 queue_unref(q);
05991 }
05992 astman_append(s,
05993 "Event: QueueSummaryComplete\r\n"
05994 "%s"
05995 "\r\n", idText);
05996
05997 return RESULT_SUCCESS;
05998 }
05999
06000
06001 static int manager_queues_status(struct mansession *s, const struct message *m)
06002 {
06003 time_t now;
06004 int pos;
06005 const char *id = astman_get_header(m,"ActionID");
06006 const char *queuefilter = astman_get_header(m,"Queue");
06007 const char *memberfilter = astman_get_header(m,"Member");
06008 char idText[256] = "";
06009 struct call_queue *q;
06010 struct queue_ent *qe;
06011 float sl = 0;
06012 struct member *mem;
06013 struct ao2_iterator queue_iter;
06014 struct ao2_iterator mem_iter;
06015
06016 astman_send_ack(s, m, "Queue status will follow");
06017 time(&now);
06018 if (!ast_strlen_zero(id))
06019 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
06020
06021 queue_iter = ao2_iterator_init(queues, 0);
06022 while ((q = ao2_iterator_next(&queue_iter))) {
06023 ao2_lock(q);
06024
06025
06026 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06027 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
06028 astman_append(s, "Event: QueueParams\r\n"
06029 "Queue: %s\r\n"
06030 "Max: %d\r\n"
06031 "Strategy: %s\r\n"
06032 "Calls: %d\r\n"
06033 "Holdtime: %d\r\n"
06034 "Completed: %d\r\n"
06035 "Abandoned: %d\r\n"
06036 "ServiceLevel: %d\r\n"
06037 "ServicelevelPerf: %2.1f\r\n"
06038 "Weight: %d\r\n"
06039 "%s"
06040 "\r\n",
06041 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06042 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06043
06044 mem_iter = ao2_iterator_init(q->members, 0);
06045 while ((mem = ao2_iterator_next(&mem_iter))) {
06046 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06047 astman_append(s, "Event: QueueMember\r\n"
06048 "Queue: %s\r\n"
06049 "Name: %s\r\n"
06050 "Location: %s\r\n"
06051 "Membership: %s\r\n"
06052 "Penalty: %d\r\n"
06053 "CallsTaken: %d\r\n"
06054 "LastCall: %d\r\n"
06055 "Status: %d\r\n"
06056 "Paused: %d\r\n"
06057 "%s"
06058 "\r\n",
06059 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06060 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06061 }
06062 ao2_ref(mem, -1);
06063 }
06064
06065 pos = 1;
06066 for (qe = q->head; qe; qe = qe->next) {
06067 astman_append(s, "Event: QueueEntry\r\n"
06068 "Queue: %s\r\n"
06069 "Position: %d\r\n"
06070 "Channel: %s\r\n"
06071 "CallerIDNum: %s\r\n"
06072 "CallerIDName: %s\r\n"
06073 "Wait: %ld\r\n"
06074 "%s"
06075 "\r\n",
06076 q->name, pos++, qe->chan->name,
06077 S_OR(qe->chan->cid.cid_num, "unknown"),
06078 S_OR(qe->chan->cid.cid_name, "unknown"),
06079 (long) (now - qe->start), idText);
06080 }
06081 }
06082 ao2_unlock(q);
06083 queue_unref(q);
06084 }
06085
06086 astman_append(s,
06087 "Event: QueueStatusComplete\r\n"
06088 "%s"
06089 "\r\n",idText);
06090
06091 return RESULT_SUCCESS;
06092 }
06093
06094 static int manager_add_queue_member(struct mansession *s, const struct message *m)
06095 {
06096 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06097 int paused, penalty = 0;
06098
06099 queuename = astman_get_header(m, "Queue");
06100 interface = astman_get_header(m, "Interface");
06101 penalty_s = astman_get_header(m, "Penalty");
06102 paused_s = astman_get_header(m, "Paused");
06103 membername = astman_get_header(m, "MemberName");
06104 state_interface = astman_get_header(m, "StateInterface");
06105
06106 if (ast_strlen_zero(queuename)) {
06107 astman_send_error(s, m, "'Queue' not specified.");
06108 return 0;
06109 }
06110
06111 if (ast_strlen_zero(interface)) {
06112 astman_send_error(s, m, "'Interface' not specified.");
06113 return 0;
06114 }
06115
06116 if (ast_strlen_zero(penalty_s))
06117 penalty = 0;
06118 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
06119 penalty = 0;
06120
06121 if (ast_strlen_zero(paused_s))
06122 paused = 0;
06123 else
06124 paused = abs(ast_true(paused_s));
06125
06126 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06127 case RES_OKAY:
06128 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06129 astman_send_ack(s, m, "Added interface to queue");
06130 break;
06131 case RES_EXISTS:
06132 astman_send_error(s, m, "Unable to add interface: Already there");
06133 break;
06134 case RES_NOSUCHQUEUE:
06135 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06136 break;
06137 case RES_OUTOFMEMORY:
06138 astman_send_error(s, m, "Out of memory");
06139 break;
06140 }
06141
06142 return 0;
06143 }
06144
06145 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
06146 {
06147 const char *queuename, *interface;
06148
06149 queuename = astman_get_header(m, "Queue");
06150 interface = astman_get_header(m, "Interface");
06151
06152 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06153 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06154 return 0;
06155 }
06156
06157 switch (remove_from_queue(queuename, interface)) {
06158 case RES_OKAY:
06159 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06160 astman_send_ack(s, m, "Removed interface from queue");
06161 break;
06162 case RES_EXISTS:
06163 astman_send_error(s, m, "Unable to remove interface: Not there");
06164 break;
06165 case RES_NOSUCHQUEUE:
06166 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06167 break;
06168 case RES_OUTOFMEMORY:
06169 astman_send_error(s, m, "Out of memory");
06170 break;
06171 case RES_NOT_DYNAMIC:
06172 astman_send_error(s, m, "Member not dynamic");
06173 break;
06174 }
06175
06176 return 0;
06177 }
06178
06179 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
06180 {
06181 const char *queuename, *interface, *paused_s, *reason;
06182 int paused;
06183
06184 interface = astman_get_header(m, "Interface");
06185 paused_s = astman_get_header(m, "Paused");
06186 queuename = astman_get_header(m, "Queue");
06187 reason = astman_get_header(m, "Reason");
06188
06189 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06190 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06191 return 0;
06192 }
06193
06194 paused = abs(ast_true(paused_s));
06195
06196 if (set_member_paused(queuename, interface, reason, paused))
06197 astman_send_error(s, m, "Interface not found");
06198 else
06199 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06200 return 0;
06201 }
06202
06203 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
06204 {
06205 const char *queuename, *event, *message, *interface, *uniqueid;
06206
06207 queuename = astman_get_header(m, "Queue");
06208 uniqueid = astman_get_header(m, "UniqueId");
06209 interface = astman_get_header(m, "Interface");
06210 event = astman_get_header(m, "Event");
06211 message = astman_get_header(m, "Message");
06212
06213 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06214 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06215 return 0;
06216 }
06217
06218 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06219 astman_send_ack(s, m, "Event added successfully");
06220
06221 return 0;
06222 }
06223
06224 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
06225 {
06226
06227 switch (pos) {
06228 case 3:
06229 return NULL;
06230 case 4:
06231 return state == 0 ? ast_strdup("to") : NULL;
06232 case 5:
06233 return complete_queue(line, word, pos, state);
06234 case 6:
06235 return state == 0 ? ast_strdup("penalty") : NULL;
06236 case 7:
06237 if (state < 100) {
06238 char *num;
06239 if ((num = ast_malloc(3))) {
06240 sprintf(num, "%d", state);
06241 }
06242 return num;
06243 } else {
06244 return NULL;
06245 }
06246 case 8:
06247 return state == 0 ? ast_strdup("as") : NULL;
06248 case 9:
06249 return NULL;
06250 default:
06251 return NULL;
06252 }
06253 }
06254
06255 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
06256 {
06257 const char *queuename, *interface, *penalty_s;
06258 int penalty;
06259
06260 interface = astman_get_header(m, "Interface");
06261 penalty_s = astman_get_header(m, "Penalty");
06262
06263 queuename = astman_get_header(m, "Queue");
06264
06265 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06266 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06267 return 0;
06268 }
06269
06270 penalty = atoi(penalty_s);
06271
06272 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06273 astman_send_error(s, m, "Invalid interface, queuename or penalty");
06274 else
06275 astman_send_ack(s, m, "Interface penalty set successfully");
06276
06277 return 0;
06278 }
06279
06280 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06281 {
06282 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06283 int penalty;
06284
06285 switch ( cmd ) {
06286 case CLI_INIT:
06287 e->command = "queue add member";
06288 e->usage =
06289 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06290 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n";
06291 return NULL;
06292 case CLI_GENERATE:
06293 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06294 }
06295
06296 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06297 return CLI_SHOWUSAGE;
06298 } else if (strcmp(a->argv[4], "to")) {
06299 return CLI_SHOWUSAGE;
06300 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06301 return CLI_SHOWUSAGE;
06302 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06303 return CLI_SHOWUSAGE;
06304 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06305 return CLI_SHOWUSAGE;
06306 }
06307
06308 queuename = a->argv[5];
06309 interface = a->argv[3];
06310 if (a->argc >= 8) {
06311 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
06312 if (penalty < 0) {
06313 ast_cli(a->fd, "Penalty must be >= 0\n");
06314 penalty = 0;
06315 }
06316 } else {
06317 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06318 penalty = 0;
06319 }
06320 } else {
06321 penalty = 0;
06322 }
06323
06324 if (a->argc >= 10) {
06325 membername = a->argv[9];
06326 }
06327
06328 if (a->argc >= 12) {
06329 state_interface = a->argv[11];
06330 }
06331
06332 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06333 case RES_OKAY:
06334 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06335 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06336 return CLI_SUCCESS;
06337 case RES_EXISTS:
06338 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06339 return CLI_FAILURE;
06340 case RES_NOSUCHQUEUE:
06341 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06342 return CLI_FAILURE;
06343 case RES_OUTOFMEMORY:
06344 ast_cli(a->fd, "Out of memory\n");
06345 return CLI_FAILURE;
06346 case RES_NOT_DYNAMIC:
06347 ast_cli(a->fd, "Member not dynamic\n");
06348 return CLI_FAILURE;
06349 default:
06350 return CLI_FAILURE;
06351 }
06352 }
06353
06354 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
06355 {
06356 int which = 0;
06357 struct call_queue *q;
06358 struct member *m;
06359 struct ao2_iterator queue_iter;
06360 struct ao2_iterator mem_iter;
06361 int wordlen = strlen(word);
06362
06363
06364 if (pos > 5 || pos < 3)
06365 return NULL;
06366 if (pos == 4)
06367 return (state == 0 ? ast_strdup("from") : NULL);
06368
06369 if (pos == 5)
06370 return complete_queue(line, word, pos, state);
06371
06372
06373 queue_iter = ao2_iterator_init(queues, 0);
06374 while ((q = ao2_iterator_next(&queue_iter))) {
06375 ao2_lock(q);
06376 mem_iter = ao2_iterator_init(q->members, 0);
06377 while ((m = ao2_iterator_next(&mem_iter))) {
06378 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06379 char *tmp;
06380 ao2_unlock(q);
06381 tmp = ast_strdup(m->interface);
06382 ao2_ref(m, -1);
06383 queue_unref(q);
06384 return tmp;
06385 }
06386 ao2_ref(m, -1);
06387 }
06388 ao2_unlock(q);
06389 queue_unref(q);
06390 }
06391
06392 return NULL;
06393 }
06394
06395 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06396 {
06397 char *queuename, *interface;
06398
06399 switch (cmd) {
06400 case CLI_INIT:
06401 e->command = "queue remove member";
06402 e->usage =
06403 "Usage: queue remove member <channel> from <queue>\n"
06404 " Remove a specific channel from a queue.\n";
06405 return NULL;
06406 case CLI_GENERATE:
06407 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06408 }
06409
06410 if (a->argc != 6) {
06411 return CLI_SHOWUSAGE;
06412 } else if (strcmp(a->argv[4], "from")) {
06413 return CLI_SHOWUSAGE;
06414 }
06415
06416 queuename = a->argv[5];
06417 interface = a->argv[3];
06418
06419 switch (remove_from_queue(queuename, interface)) {
06420 case RES_OKAY:
06421 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06422 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06423 return CLI_SUCCESS;
06424 case RES_EXISTS:
06425 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06426 return CLI_FAILURE;
06427 case RES_NOSUCHQUEUE:
06428 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06429 return CLI_FAILURE;
06430 case RES_OUTOFMEMORY:
06431 ast_cli(a->fd, "Out of memory\n");
06432 return CLI_FAILURE;
06433 default:
06434 return CLI_FAILURE;
06435 }
06436 }
06437
06438 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
06439 {
06440
06441 switch (pos) {
06442 case 3:
06443 return NULL;
06444 case 4:
06445 return state == 0 ? ast_strdup("queue") : NULL;
06446 case 5:
06447 return complete_queue(line, word, pos, state);
06448 case 6:
06449 return state == 0 ? ast_strdup("reason") : NULL;
06450 case 7:
06451 return NULL;
06452 default:
06453 return NULL;
06454 }
06455 }
06456
06457 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06458 {
06459 char *queuename, *interface, *reason;
06460 int paused;
06461
06462 switch (cmd) {
06463 case CLI_INIT:
06464 e->command = "queue {pause|unpause} member";
06465 e->usage =
06466 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06467 " Pause or unpause a queue member. Not specifying a particular queue\n"
06468 " will pause or unpause a member across all queues to which the member\n"
06469 " belongs.\n";
06470 return NULL;
06471 case CLI_GENERATE:
06472 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06473 }
06474
06475 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06476 return CLI_SHOWUSAGE;
06477 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06478 return CLI_SHOWUSAGE;
06479 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06480 return CLI_SHOWUSAGE;
06481 }
06482
06483
06484 interface = a->argv[3];
06485 queuename = a->argc >= 6 ? a->argv[5] : NULL;
06486 reason = a->argc == 8 ? a->argv[7] : NULL;
06487 paused = !strcasecmp(a->argv[1], "pause");
06488
06489 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06490 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06491 if (!ast_strlen_zero(queuename))
06492 ast_cli(a->fd, " in queue '%s'", queuename);
06493 if (!ast_strlen_zero(reason))
06494 ast_cli(a->fd, " for reason '%s'", reason);
06495 ast_cli(a->fd, "\n");
06496 return CLI_SUCCESS;
06497 } else {
06498 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06499 if (!ast_strlen_zero(queuename))
06500 ast_cli(a->fd, " in queue '%s'", queuename);
06501 if (!ast_strlen_zero(reason))
06502 ast_cli(a->fd, " for reason '%s'", reason);
06503 ast_cli(a->fd, "\n");
06504 return CLI_FAILURE;
06505 }
06506 }
06507
06508 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
06509 {
06510
06511 switch (pos) {
06512 case 4:
06513 if (state == 0) {
06514 return ast_strdup("on");
06515 } else {
06516 return NULL;
06517 }
06518 case 6:
06519 if (state == 0) {
06520 return ast_strdup("in");
06521 } else {
06522 return NULL;
06523 }
06524 case 7:
06525 return complete_queue(line, word, pos, state);
06526 default:
06527 return NULL;
06528 }
06529 }
06530
06531 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06532 {
06533 char *queuename = NULL, *interface;
06534 int penalty = 0;
06535
06536 switch (cmd) {
06537 case CLI_INIT:
06538 e->command = "queue set penalty";
06539 e->usage =
06540 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06541 " Set a member's penalty in the queue specified. If no queue is specified\n"
06542 " then that interface's penalty is set in all queues to which that interface is a member\n";
06543 return NULL;
06544 case CLI_GENERATE:
06545 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06546 }
06547
06548 if (a->argc != 6 && a->argc != 8) {
06549 return CLI_SHOWUSAGE;
06550 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06551 return CLI_SHOWUSAGE;
06552 }
06553
06554 if (a->argc == 8)
06555 queuename = a->argv[7];
06556 interface = a->argv[5];
06557 penalty = atoi(a->argv[3]);
06558
06559 switch (set_member_penalty(queuename, interface, penalty)) {
06560 case RESULT_SUCCESS:
06561 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06562 return CLI_SUCCESS;
06563 case RESULT_FAILURE:
06564 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06565 return CLI_FAILURE;
06566 default:
06567 return CLI_FAILURE;
06568 }
06569 }
06570
06571 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
06572 {
06573 int which = 0;
06574 struct rule_list *rl_iter;
06575 int wordlen = strlen(word);
06576 char *ret = NULL;
06577 if (pos != 3) {
06578 return NULL;
06579 }
06580
06581 AST_LIST_LOCK(&rule_lists);
06582 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06583 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06584 ret = ast_strdup(rl_iter->name);
06585 break;
06586 }
06587 }
06588 AST_LIST_UNLOCK(&rule_lists);
06589
06590 return ret;
06591 }
06592
06593 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06594 {
06595 char *rule;
06596 struct rule_list *rl_iter;
06597 struct penalty_rule *pr_iter;
06598 switch (cmd) {
06599 case CLI_INIT:
06600 e->command = "queue rules show";
06601 e->usage =
06602 "Usage: queue rules show [rulename]\n"
06603 " Show the list of rules associated with rulename. If no\n"
06604 " rulename is specified, list all rules defined in queuerules.conf\n";
06605 return NULL;
06606 case CLI_GENERATE:
06607 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06608 }
06609
06610 if (a->argc != 3 && a->argc != 4)
06611 return CLI_SHOWUSAGE;
06612
06613 rule = a->argc == 4 ? a->argv[3] : "";
06614 AST_LIST_LOCK(&rule_lists);
06615 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06616 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06617 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06618 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06619 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);
06620 }
06621 }
06622 }
06623 AST_LIST_UNLOCK(&rule_lists);
06624 return CLI_SUCCESS;
06625 }
06626
06627 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06628 {
06629 switch (cmd) {
06630 case CLI_INIT:
06631 e->command = "queue rules reload";
06632 e->usage =
06633 "Usage: queue rules reload\n"
06634 " Reloads rules defined in queuerules.conf\n";
06635 return NULL;
06636 case CLI_GENERATE:
06637 return NULL;
06638 }
06639 reload_queue_rules(1);
06640 return CLI_SUCCESS;
06641 }
06642
06643 static const char qpm_cmd_usage[] =
06644 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
06645
06646 static const char qum_cmd_usage[] =
06647 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
06648
06649 static const char qsmp_cmd_usage[] =
06650 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
06651
06652 static struct ast_cli_entry cli_queue[] = {
06653 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
06654 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
06655 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
06656 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
06657 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
06658 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
06659 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
06660 };
06661
06662 static int unload_module(void)
06663 {
06664 int res;
06665 struct ast_context *con;
06666 struct ao2_iterator q_iter;
06667 struct call_queue *q = NULL;
06668
06669 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06670 res = ast_manager_unregister("QueueStatus");
06671 res |= ast_manager_unregister("Queues");
06672 res |= ast_manager_unregister("QueueRule");
06673 res |= ast_manager_unregister("QueueSummary");
06674 res |= ast_manager_unregister("QueueAdd");
06675 res |= ast_manager_unregister("QueueRemove");
06676 res |= ast_manager_unregister("QueuePause");
06677 res |= ast_manager_unregister("QueueLog");
06678 res |= ast_manager_unregister("QueuePenalty");
06679 res |= ast_unregister_application(app_aqm);
06680 res |= ast_unregister_application(app_rqm);
06681 res |= ast_unregister_application(app_pqm);
06682 res |= ast_unregister_application(app_upqm);
06683 res |= ast_unregister_application(app_ql);
06684 res |= ast_unregister_application(app);
06685 res |= ast_custom_function_unregister(&queuevar_function);
06686 res |= ast_custom_function_unregister(&queuemembercount_function);
06687 res |= ast_custom_function_unregister(&queuemembercount_dep);
06688 res |= ast_custom_function_unregister(&queuememberlist_function);
06689 res |= ast_custom_function_unregister(&queuewaitingcount_function);
06690 res |= ast_custom_function_unregister(&queuememberpenalty_function);
06691
06692 if (device_state_sub)
06693 ast_event_unsubscribe(device_state_sub);
06694
06695 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06696 ast_context_remove_extension2(con, "s", 1, NULL, 0);
06697 ast_context_destroy(con, "app_queue");
06698 }
06699
06700 clear_and_free_interfaces();
06701
06702 q_iter = ao2_iterator_init(queues, 0);
06703 while ((q = ao2_iterator_next(&q_iter))) {
06704 ao2_unlink(queues, q);
06705 queue_unref(q);
06706 }
06707 ao2_ref(queues, -1);
06708 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06709 ast_unload_realtime("queue_members");
06710 return res;
06711 }
06712
06713 static int load_module(void)
06714 {
06715 int res;
06716 struct ast_context *con;
06717
06718 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06719
06720 if (!reload_queues(0))
06721 return AST_MODULE_LOAD_DECLINE;
06722
06723 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06724 if (!con)
06725 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06726 else
06727 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06728
06729 if (queue_persistent_members)
06730 reload_queue_members();
06731
06732 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06733 res = ast_register_application(app, queue_exec, synopsis, descrip);
06734 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06735 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06736 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06737 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06738 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06739 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06740 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06741 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06742 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06743 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06744 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06745 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06746 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
06747 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06748 res |= ast_custom_function_register(&queuevar_function);
06749 res |= ast_custom_function_register(&queuemembercount_function);
06750 res |= ast_custom_function_register(&queuemembercount_dep);
06751 res |= ast_custom_function_register(&queuememberlist_function);
06752 res |= ast_custom_function_register(&queuewaitingcount_function);
06753 res |= ast_custom_function_register(&queuememberpenalty_function);
06754
06755 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06756 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06757 }
06758
06759 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06760 res = -1;
06761 }
06762
06763 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06764
06765 return res ? AST_MODULE_LOAD_DECLINE : 0;
06766 }
06767
06768 static int reload(void)
06769 {
06770 ast_unload_realtime("queue_members");
06771 reload_queues(1);
06772 return 0;
06773 }
06774
06775 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
06776 .load = load_module,
06777 .unload = unload_module,
06778 .reload = reload,
06779 );
06780