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