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: 247171 $")
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 member %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
02926
02927 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02928 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02929 res = 1;
02930 } else {
02931 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02932 res = 0;
02933 }
02934
02935 return res;
02936 }
02937
02938
02939
02940
02941
02942
02943
02944 static void update_qe_rule(struct queue_ent *qe)
02945 {
02946 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02947 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02948 char max_penalty_str[20], min_penalty_str[20];
02949
02950 if (max_penalty < 0)
02951 max_penalty = 0;
02952 if (min_penalty < 0)
02953 min_penalty = 0;
02954 if (min_penalty > max_penalty)
02955 min_penalty = max_penalty;
02956 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02957 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02958 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02959 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02960 qe->max_penalty = max_penalty;
02961 qe->min_penalty = min_penalty;
02962 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);
02963 qe->pr = AST_LIST_NEXT(qe->pr, list);
02964 }
02965
02966
02967
02968
02969
02970
02971
02972
02973
02974
02975
02976 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02977 {
02978 int res = 0;
02979
02980
02981 for (;;) {
02982 enum queue_member_status status = QUEUE_NORMAL;
02983 int exit = 0;
02984
02985 if (is_our_turn(qe))
02986 break;
02987
02988
02989 if (qe->expire && (time(NULL) >= qe->expire)) {
02990 *reason = QUEUE_TIMEOUT;
02991 break;
02992 }
02993
02994
02995
02996
02997
02998 for (; !exit || qe->pr; update_qe_rule(qe)) {
02999 status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
03000
03001 if (!qe->pr || status == QUEUE_NORMAL) {
03002 break;
03003 }
03004
03005
03006 if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
03007 ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
03008 ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
03009 continue;
03010 } else {
03011 exit = 1;
03012 }
03013 }
03014
03015 if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
03016 *reason = QUEUE_LEAVEEMPTY;
03017 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03018 leave_queue(qe);
03019 break;
03020 }
03021
03022
03023 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
03024 *reason = QUEUE_LEAVEUNAVAIL;
03025 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03026 leave_queue(qe);
03027 break;
03028 }
03029 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
03030 *reason = QUEUE_LEAVEUNAVAIL;
03031 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03032 leave_queue(qe);
03033 break;
03034 }
03035
03036
03037 if (qe->parent->announcefrequency &&
03038 (res = say_position(qe,ringing)))
03039 break;
03040
03041
03042 if (qe->expire && (time(NULL) >= qe->expire)) {
03043 *reason = QUEUE_TIMEOUT;
03044 break;
03045 }
03046
03047
03048 if (qe->parent->periodicannouncefrequency &&
03049 (res = say_periodic_announcement(qe,ringing)))
03050 break;
03051
03052
03053 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03054 update_qe_rule(qe);
03055 }
03056
03057
03058 if (qe->expire && (time(NULL) >= qe->expire)) {
03059 *reason = QUEUE_TIMEOUT;
03060 break;
03061 }
03062
03063
03064 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03065 if (res > 0 && !valid_exit(qe, res))
03066 res = 0;
03067 else
03068 break;
03069 }
03070
03071
03072 if (qe->expire && (time(NULL) >= qe->expire)) {
03073 *reason = QUEUE_TIMEOUT;
03074 break;
03075 }
03076 }
03077
03078 return res;
03079 }
03080
03081
03082
03083
03084
03085 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
03086 {
03087 struct member *mem;
03088 struct call_queue *qtmp;
03089 struct ao2_iterator queue_iter;
03090
03091 if (shared_lastcall) {
03092 queue_iter = ao2_iterator_init(queues, 0);
03093 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03094 ao2_lock(qtmp);
03095 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03096 time(&mem->lastcall);
03097 mem->calls++;
03098 mem->lastqueue = q;
03099 ao2_ref(mem, -1);
03100 }
03101 ao2_unlock(qtmp);
03102 ao2_ref(qtmp, -1);
03103 }
03104 ao2_iterator_destroy(&queue_iter);
03105 } else {
03106 ao2_lock(q);
03107 time(&member->lastcall);
03108 member->calls++;
03109 member->lastqueue = q;
03110 ao2_unlock(q);
03111 }
03112 ao2_lock(q);
03113 q->callscompleted++;
03114 if (callcompletedinsl)
03115 q->callscompletedinsl++;
03116 ao2_unlock(q);
03117 return 0;
03118 }
03119
03120
03121
03122
03123
03124
03125
03126
03127
03128 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
03129 {
03130 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03131 return -1;
03132
03133 switch (q->strategy) {
03134 case QUEUE_STRATEGY_RINGALL:
03135
03136 tmp->metric = mem->penalty * 1000000;
03137 break;
03138 case QUEUE_STRATEGY_LINEAR:
03139 if (pos < qe->linpos) {
03140 tmp->metric = 1000 + pos;
03141 } else {
03142 if (pos > qe->linpos)
03143
03144 qe->linwrapped = 1;
03145 tmp->metric = pos;
03146 }
03147 tmp->metric += mem->penalty * 1000000;
03148 break;
03149 case QUEUE_STRATEGY_RRMEMORY:
03150 if (pos < q->rrpos) {
03151 tmp->metric = 1000 + pos;
03152 } else {
03153 if (pos > q->rrpos)
03154
03155 q->wrapped = 1;
03156 tmp->metric = pos;
03157 }
03158 tmp->metric += mem->penalty * 1000000;
03159 break;
03160 case QUEUE_STRATEGY_RANDOM:
03161 tmp->metric = ast_random() % 1000;
03162 tmp->metric += mem->penalty * 1000000;
03163 break;
03164 case QUEUE_STRATEGY_WRANDOM:
03165 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03166 break;
03167 case QUEUE_STRATEGY_FEWESTCALLS:
03168 tmp->metric = mem->calls;
03169 tmp->metric += mem->penalty * 1000000;
03170 break;
03171 case QUEUE_STRATEGY_LEASTRECENT:
03172 if (!mem->lastcall)
03173 tmp->metric = 0;
03174 else
03175 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03176 tmp->metric += mem->penalty * 1000000;
03177 break;
03178 default:
03179 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03180 break;
03181 }
03182 return 0;
03183 }
03184
03185 enum agent_complete_reason {
03186 CALLER,
03187 AGENT,
03188 TRANSFER
03189 };
03190
03191
03192 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
03193 const struct ast_channel *peer, const struct member *member, time_t callstart,
03194 char *vars, size_t vars_len, enum agent_complete_reason rsn)
03195 {
03196 const char *reason = NULL;
03197
03198 if (!qe->parent->eventwhencalled)
03199 return;
03200
03201 switch (rsn) {
03202 case CALLER:
03203 reason = "caller";
03204 break;
03205 case AGENT:
03206 reason = "agent";
03207 break;
03208 case TRANSFER:
03209 reason = "transfer";
03210 break;
03211 }
03212
03213 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03214 "Queue: %s\r\n"
03215 "Uniqueid: %s\r\n"
03216 "Channel: %s\r\n"
03217 "Member: %s\r\n"
03218 "MemberName: %s\r\n"
03219 "HoldTime: %ld\r\n"
03220 "TalkTime: %ld\r\n"
03221 "Reason: %s\r\n"
03222 "%s",
03223 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03224 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03225 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03226 }
03227
03228 struct queue_transfer_ds {
03229 struct queue_ent *qe;
03230 struct member *member;
03231 time_t starttime;
03232 int callcompletedinsl;
03233 };
03234
03235 static void queue_transfer_destroy(void *data)
03236 {
03237 struct queue_transfer_ds *qtds = data;
03238 ast_free(qtds);
03239 }
03240
03241
03242
03243 static const struct ast_datastore_info queue_transfer_info = {
03244 .type = "queue_transfer",
03245 .chan_fixup = queue_transfer_fixup,
03246 .destroy = queue_transfer_destroy,
03247 };
03248
03249
03250
03251
03252
03253
03254
03255
03256
03257
03258 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
03259 {
03260 struct queue_transfer_ds *qtds = data;
03261 struct queue_ent *qe = qtds->qe;
03262 struct member *member = qtds->member;
03263 time_t callstart = qtds->starttime;
03264 int callcompletedinsl = qtds->callcompletedinsl;
03265 struct ast_datastore *datastore;
03266
03267 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03268 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03269 (long) (time(NULL) - callstart), qe->opos);
03270
03271 update_queue(qe->parent, member, callcompletedinsl);
03272
03273
03274 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03275 ast_channel_datastore_remove(old_chan, datastore);
03276 } else {
03277 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03278 }
03279 }
03280
03281
03282
03283
03284
03285
03286
03287
03288
03289 static int attended_transfer_occurred(struct ast_channel *chan)
03290 {
03291 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03292 }
03293
03294
03295
03296 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
03297 {
03298 struct ast_datastore *ds;
03299 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03300
03301 if (!qtds) {
03302 ast_log(LOG_WARNING, "Memory allocation error!\n");
03303 return NULL;
03304 }
03305
03306 ast_channel_lock(qe->chan);
03307 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03308 ast_channel_unlock(qe->chan);
03309 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03310 return NULL;
03311 }
03312
03313 qtds->qe = qe;
03314
03315 qtds->member = member;
03316 qtds->starttime = starttime;
03317 qtds->callcompletedinsl = callcompletedinsl;
03318 ds->data = qtds;
03319 ast_channel_datastore_add(qe->chan, ds);
03320 ast_channel_unlock(qe->chan);
03321 return ds;
03322 }
03323
03324 struct queue_end_bridge {
03325 struct call_queue *q;
03326 struct ast_channel *chan;
03327 };
03328
03329 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
03330 {
03331 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03332 ao2_ref(qeb, +1);
03333 qeb->chan = originator;
03334 }
03335
03336 static void end_bridge_callback(void *data)
03337 {
03338 struct queue_end_bridge *qeb = data;
03339 struct call_queue *q = qeb->q;
03340 struct ast_channel *chan = qeb->chan;
03341
03342 if (ao2_ref(qeb, -1) == 1) {
03343 ao2_lock(q);
03344 set_queue_variables(q, chan);
03345 ao2_unlock(q);
03346
03347 queue_t_unref(q, "Expire bridge_config reference");
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
03377
03378 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)
03379 {
03380 struct member *cur;
03381 struct callattempt *outgoing = NULL;
03382 int to, orig;
03383 char oldexten[AST_MAX_EXTENSION]="";
03384 char oldcontext[AST_MAX_CONTEXT]="";
03385 char queuename[256]="";
03386 char interfacevar[256]="";
03387 struct ast_channel *peer;
03388 struct ast_channel *which;
03389 struct callattempt *lpeer;
03390 struct member *member;
03391 struct ast_app *application;
03392 int res = 0, bridge = 0;
03393 int numbusies = 0;
03394 int x=0;
03395 char *announce = NULL;
03396 char digit = 0;
03397 time_t callstart;
03398 time_t now = time(NULL);
03399 struct ast_bridge_config bridge_config;
03400 char nondataquality = 1;
03401 char *agiexec = NULL;
03402 char *macroexec = NULL;
03403 char *gosubexec = NULL;
03404 int ret = 0;
03405 const char *monitorfilename;
03406 const char *monitor_exec;
03407 const char *monitor_options;
03408 char tmpid[256], tmpid2[256];
03409 char meid[1024], meid2[1024];
03410 char mixmonargs[1512];
03411 struct ast_app *mixmonapp = NULL;
03412 char *p;
03413 char vars[2048];
03414 int forwardsallowed = 1;
03415 int callcompletedinsl;
03416 struct ao2_iterator memi;
03417 struct ast_datastore *datastore, *transfer_ds;
03418 struct queue_end_bridge *queue_end_bridge = NULL;
03419 const int need_weight = use_weight;
03420
03421 ast_channel_lock(qe->chan);
03422 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03423 ast_channel_unlock(qe->chan);
03424
03425 memset(&bridge_config, 0, sizeof(bridge_config));
03426 tmpid[0] = 0;
03427 meid[0] = 0;
03428 time(&now);
03429
03430
03431
03432
03433
03434 if (qe->expire && now >= qe->expire) {
03435 res = 0;
03436 goto out;
03437 }
03438
03439 for (; options && *options; options++)
03440 switch (*options) {
03441 case 't':
03442 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03443 break;
03444 case 'T':
03445 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03446 break;
03447 case 'w':
03448 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03449 break;
03450 case 'W':
03451 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03452 break;
03453 case 'c':
03454 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03455 break;
03456 case 'd':
03457 nondataquality = 0;
03458 break;
03459 case 'h':
03460 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03461 break;
03462 case 'H':
03463 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03464 break;
03465 case 'k':
03466 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03467 break;
03468 case 'K':
03469 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03470 break;
03471 case 'n':
03472 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03473 (*tries)++;
03474 else
03475 *tries = qe->parent->membercount;
03476 *noption = 1;
03477 break;
03478 case 'i':
03479 forwardsallowed = 0;
03480 break;
03481 case 'x':
03482 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03483 break;
03484 case 'X':
03485 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03486 break;
03487
03488 }
03489
03490
03491 if (need_weight)
03492 ao2_lock(queues);
03493 ao2_lock(qe->parent);
03494 ast_debug(1, "%s is trying to call a queue member.\n",
03495 qe->chan->name);
03496 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03497 if (!ast_strlen_zero(qe->announce))
03498 announce = qe->announce;
03499 if (!ast_strlen_zero(announceoverride))
03500 announce = announceoverride;
03501
03502 memi = ao2_iterator_init(qe->parent->members, 0);
03503 while ((cur = ao2_iterator_next(&memi))) {
03504 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03505 struct ast_dialed_interface *di;
03506 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03507 if (!tmp) {
03508 ao2_ref(cur, -1);
03509 ao2_unlock(qe->parent);
03510 ao2_iterator_destroy(&memi);
03511 if (need_weight)
03512 ao2_unlock(queues);
03513 goto out;
03514 }
03515 if (!datastore) {
03516 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03517 ao2_ref(cur, -1);
03518 ao2_unlock(qe->parent);
03519 ao2_iterator_destroy(&memi);
03520 if (need_weight)
03521 ao2_unlock(queues);
03522 free(tmp);
03523 goto out;
03524 }
03525 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03526 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03527 ao2_ref(cur, -1);
03528 ao2_unlock(&qe->parent);
03529 ao2_iterator_destroy(&memi);
03530 if (need_weight)
03531 ao2_unlock(queues);
03532 free(tmp);
03533 goto out;
03534 }
03535 datastore->data = dialed_interfaces;
03536 AST_LIST_HEAD_INIT(dialed_interfaces);
03537
03538 ast_channel_lock(qe->chan);
03539 ast_channel_datastore_add(qe->chan, datastore);
03540 ast_channel_unlock(qe->chan);
03541 } else
03542 dialed_interfaces = datastore->data;
03543
03544 AST_LIST_LOCK(dialed_interfaces);
03545 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03546 if (!strcasecmp(cur->interface, di->interface)) {
03547 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
03548 di->interface);
03549 break;
03550 }
03551 }
03552 AST_LIST_UNLOCK(dialed_interfaces);
03553
03554 if (di) {
03555 free(tmp);
03556 continue;
03557 }
03558
03559
03560
03561
03562
03563 if (strncasecmp(cur->interface, "Local/", 6)) {
03564 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03565 ao2_ref(cur, -1);
03566 ao2_unlock(qe->parent);
03567 ao2_iterator_destroy(&memi);
03568 if (need_weight)
03569 ao2_unlock(queues);
03570 free(tmp);
03571 goto out;
03572 }
03573 strcpy(di->interface, cur->interface);
03574
03575 AST_LIST_LOCK(dialed_interfaces);
03576 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03577 AST_LIST_UNLOCK(dialed_interfaces);
03578 }
03579
03580 tmp->stillgoing = -1;
03581 tmp->member = cur;
03582 tmp->oldstatus = cur->status;
03583 tmp->lastcall = cur->lastcall;
03584 tmp->lastqueue = cur->lastqueue;
03585 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03586
03587
03588 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03589
03590
03591
03592 tmp->q_next = outgoing;
03593 outgoing = tmp;
03594
03595 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03596 break;
03597 } else {
03598 ao2_ref(cur, -1);
03599 ast_free(tmp);
03600 }
03601 }
03602 ao2_iterator_destroy(&memi);
03603
03604 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03605
03606 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03607 to = (qe->expire - now) * 1000;
03608 else
03609 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03610 } else {
03611
03612 if (qe->expire && qe->expire<=now) {
03613 to = 0;
03614 } else if (qe->parent->timeout) {
03615 to = qe->parent->timeout * 1000;
03616 } else {
03617 to = -1;
03618 }
03619 }
03620 orig = to;
03621 ++qe->pending;
03622 ao2_unlock(qe->parent);
03623 ring_one(qe, outgoing, &numbusies);
03624 if (need_weight)
03625 ao2_unlock(queues);
03626 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03627
03628
03629
03630
03631
03632
03633 ast_channel_lock(qe->chan);
03634 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03635 ast_datastore_free(datastore);
03636 }
03637 ast_channel_unlock(qe->chan);
03638 ao2_lock(qe->parent);
03639 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03640 store_next_rr(qe, outgoing);
03641 }
03642 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03643 store_next_lin(qe, outgoing);
03644 }
03645 ao2_unlock(qe->parent);
03646 peer = lpeer ? lpeer->chan : NULL;
03647 if (!peer) {
03648 qe->pending = 0;
03649 if (to) {
03650
03651 res = -1;
03652 } else {
03653
03654 res = digit;
03655 }
03656 if (res == -1)
03657 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03658 if (ast_cdr_isset_unanswered()) {
03659
03660
03661 struct callattempt *o;
03662 for (o = outgoing; o; o = o->q_next) {
03663 if (!o->chan) {
03664 continue;
03665 }
03666 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03667 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03668 break;
03669 }
03670 }
03671 }
03672 } else {
03673
03674
03675
03676 if (!strcmp(qe->chan->tech->type, "DAHDI"))
03677 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03678 if (!strcmp(peer->tech->type, "DAHDI"))
03679 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03680
03681 time(&now);
03682 recalc_holdtime(qe, (now - qe->start));
03683 ao2_lock(qe->parent);
03684 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03685 ao2_unlock(qe->parent);
03686 member = lpeer->member;
03687
03688 ao2_ref(member, 1);
03689 hangupcalls(outgoing, peer);
03690 outgoing = NULL;
03691 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03692 int res2;
03693
03694 res2 = ast_autoservice_start(qe->chan);
03695 if (!res2) {
03696 if (qe->parent->memberdelay) {
03697 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03698 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03699 }
03700 if (!res2 && announce) {
03701 play_file(peer, announce);
03702 }
03703 if (!res2 && qe->parent->reportholdtime) {
03704 if (!play_file(peer, qe->parent->sound_reporthold)) {
03705 int holdtime, holdtimesecs;
03706
03707 time(&now);
03708 holdtime = abs((now - qe->start) / 60);
03709 holdtimesecs = abs((now - qe->start) % 60);
03710 if (holdtime > 0) {
03711 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03712 play_file(peer, qe->parent->sound_minutes);
03713 }
03714 if (holdtimesecs > 1) {
03715 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03716 play_file(peer, qe->parent->sound_seconds);
03717 }
03718 }
03719 }
03720 }
03721 res2 |= ast_autoservice_stop(qe->chan);
03722 if (ast_check_hangup(peer)) {
03723
03724 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03725 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03726 if (qe->parent->eventwhencalled)
03727 manager_event(EVENT_FLAG_AGENT, "AgentDump",
03728 "Queue: %s\r\n"
03729 "Uniqueid: %s\r\n"
03730 "Channel: %s\r\n"
03731 "Member: %s\r\n"
03732 "MemberName: %s\r\n"
03733 "%s",
03734 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03735 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03736 ast_hangup(peer);
03737 ao2_ref(member, -1);
03738 goto out;
03739 } else if (res2) {
03740
03741 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03742 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03743 record_abandoned(qe);
03744 ast_cdr_noanswer(qe->chan->cdr);
03745 ast_hangup(peer);
03746 ao2_ref(member, -1);
03747 return -1;
03748 }
03749 }
03750
03751 if (ringing)
03752 ast_indicate(qe->chan,-1);
03753 else
03754 ast_moh_stop(qe->chan);
03755
03756 if (qe->chan->cdr)
03757 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03758
03759 res = ast_channel_make_compatible(qe->chan, peer);
03760 if (res < 0) {
03761 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03762 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03763 record_abandoned(qe);
03764 ast_cdr_failed(qe->chan->cdr);
03765 ast_hangup(peer);
03766 ao2_ref(member, -1);
03767 return -1;
03768 }
03769
03770
03771 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03772 if (play_file(qe->chan, qe->parent->sound_callerannounce))
03773 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03774 }
03775
03776 ao2_lock(qe->parent);
03777
03778
03779 if (qe->parent->setinterfacevar) {
03780 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03781 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03782 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03783 pbx_builtin_setvar_multiple(peer, interfacevar);
03784 }
03785
03786
03787
03788 if (qe->parent->setqueueentryvar) {
03789 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03790 (long) time(NULL) - qe->start, qe->opos);
03791 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03792 pbx_builtin_setvar_multiple(peer, interfacevar);
03793 }
03794
03795
03796 set_queue_variables(qe->parent, qe->chan);
03797 set_queue_variables(qe->parent, peer);
03798 ao2_unlock(qe->parent);
03799
03800 ast_channel_lock(qe->chan);
03801 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03802 monitorfilename = ast_strdupa(monitorfilename);
03803 }
03804 ast_channel_unlock(qe->chan);
03805
03806 if (qe->parent->monfmt && *qe->parent->monfmt) {
03807 if (!qe->parent->montype) {
03808 const char *monexec, *monargs;
03809 ast_debug(1, "Starting Monitor as requested.\n");
03810 ast_channel_lock(qe->chan);
03811 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03812 which = qe->chan;
03813 monexec = monexec ? ast_strdupa(monexec) : NULL;
03814 }
03815 else
03816 which = peer;
03817 ast_channel_unlock(qe->chan);
03818 if (ast_monitor_start) {
03819 if (monitorfilename) {
03820 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03821 } else if (qe->chan->cdr && ast_monitor_start) {
03822 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03823 } else if (ast_monitor_start) {
03824
03825 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03826 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03827 }
03828 }
03829 if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) {
03830 ast_monitor_setjoinfiles(which, 1);
03831 }
03832 } else {
03833 mixmonapp = pbx_findapp("MixMonitor");
03834
03835 if (mixmonapp) {
03836 ast_debug(1, "Starting MixMonitor as requested.\n");
03837 if (!monitorfilename) {
03838 if (qe->chan->cdr)
03839 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03840 else
03841 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03842 } else {
03843 const char *m = monitorfilename;
03844 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
03845 switch (*m) {
03846 case '^':
03847 if (*(m + 1) == '{')
03848 *p = '$';
03849 break;
03850 case ',':
03851 *p++ = '\\';
03852
03853 default:
03854 *p = *m;
03855 }
03856 if (*m == '\0')
03857 break;
03858 }
03859 if (p == tmpid2 + sizeof(tmpid2))
03860 tmpid2[sizeof(tmpid2) - 1] = '\0';
03861
03862 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03863 }
03864
03865 ast_channel_lock(qe->chan);
03866 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03867 monitor_exec = ast_strdupa(monitor_exec);
03868 }
03869 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03870 monitor_options = ast_strdupa(monitor_options);
03871 } else {
03872 monitor_options = "";
03873 }
03874 ast_channel_unlock(qe->chan);
03875
03876 if (monitor_exec) {
03877 const char *m = monitor_exec;
03878 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03879 switch (*m) {
03880 case '^':
03881 if (*(m + 1) == '{')
03882 *p = '$';
03883 break;
03884 case ',':
03885 *p++ = '\\';
03886
03887 default:
03888 *p = *m;
03889 }
03890 if (*m == '\0')
03891 break;
03892 }
03893 if (p == meid2 + sizeof(meid2))
03894 meid2[sizeof(meid2) - 1] = '\0';
03895
03896 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03897 }
03898
03899 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03900
03901 if (!ast_strlen_zero(monitor_exec))
03902 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03903 else
03904 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03905
03906 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03907
03908 if (qe->chan->cdr)
03909 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03910 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03911 if (qe->chan->cdr)
03912 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03913
03914 } else {
03915 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03916 }
03917 }
03918 }
03919
03920 leave_queue(qe);
03921 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03922 ast_debug(1, "app_queue: sendurl=%s.\n", url);
03923 ast_channel_sendurl(peer, url);
03924 }
03925
03926
03927
03928 if (!ast_strlen_zero(macro)) {
03929 macroexec = ast_strdupa(macro);
03930 } else {
03931 if (qe->parent->membermacro)
03932 macroexec = ast_strdupa(qe->parent->membermacro);
03933 }
03934
03935 if (!ast_strlen_zero(macroexec)) {
03936 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03937
03938 res = ast_autoservice_start(qe->chan);
03939 if (res) {
03940 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03941 res = -1;
03942 }
03943
03944 application = pbx_findapp("Macro");
03945
03946 if (application) {
03947 res = pbx_exec(peer, application, macroexec);
03948 ast_debug(1, "Macro exited with status %d\n", res);
03949 res = 0;
03950 } else {
03951 ast_log(LOG_ERROR, "Could not find application Macro\n");
03952 res = -1;
03953 }
03954
03955 if (ast_autoservice_stop(qe->chan) < 0) {
03956 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03957 res = -1;
03958 }
03959 }
03960
03961
03962
03963 if (!ast_strlen_zero(gosub)) {
03964 gosubexec = ast_strdupa(gosub);
03965 } else {
03966 if (qe->parent->membergosub)
03967 gosubexec = ast_strdupa(qe->parent->membergosub);
03968 }
03969
03970 if (!ast_strlen_zero(gosubexec)) {
03971 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03972
03973 res = ast_autoservice_start(qe->chan);
03974 if (res) {
03975 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03976 res = -1;
03977 }
03978
03979 application = pbx_findapp("Gosub");
03980
03981 if (application) {
03982 char *gosub_args, *gosub_argstart;
03983
03984
03985 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03986 ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03987 peer->priority = 0;
03988
03989 gosub_argstart = strchr(gosubexec, ',');
03990 if (gosub_argstart) {
03991 *gosub_argstart = 0;
03992 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03993 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03994 gosub_args = NULL;
03995 }
03996 *gosub_argstart = ',';
03997 } else {
03998 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03999 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04000 gosub_args = NULL;
04001 }
04002 }
04003 if (gosub_args) {
04004 res = pbx_exec(peer, application, gosub_args);
04005 if (!res) {
04006 struct ast_pbx_args args;
04007 memset(&args, 0, sizeof(args));
04008 args.no_hangup_chan = 1;
04009 ast_pbx_run_args(peer, &args);
04010 }
04011 ast_free(gosub_args);
04012 ast_debug(1, "Gosub exited with status %d\n", res);
04013 } else {
04014 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
04015 }
04016 } else {
04017 ast_log(LOG_ERROR, "Could not find application Gosub\n");
04018 res = -1;
04019 }
04020
04021 if (ast_autoservice_stop(qe->chan) < 0) {
04022 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04023 res = -1;
04024 }
04025 }
04026
04027 if (!ast_strlen_zero(agi)) {
04028 ast_debug(1, "app_queue: agi=%s.\n", agi);
04029 application = pbx_findapp("agi");
04030 if (application) {
04031 agiexec = ast_strdupa(agi);
04032 ret = pbx_exec(qe->chan, application, agiexec);
04033 } else
04034 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
04035 }
04036 qe->handled++;
04037 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
04038 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
04039 if (update_cdr && qe->chan->cdr)
04040 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
04041 if (qe->parent->eventwhencalled)
04042 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
04043 "Queue: %s\r\n"
04044 "Uniqueid: %s\r\n"
04045 "Channel: %s\r\n"
04046 "Member: %s\r\n"
04047 "MemberName: %s\r\n"
04048 "Holdtime: %ld\r\n"
04049 "BridgedChannel: %s\r\n"
04050 "Ringtime: %ld\r\n"
04051 "%s",
04052 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04053 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
04054 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04055 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
04056 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
04057
04058 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
04059 queue_end_bridge->q = qe->parent;
04060 queue_end_bridge->chan = qe->chan;
04061 bridge_config.end_bridge_callback = end_bridge_callback;
04062 bridge_config.end_bridge_callback_data = queue_end_bridge;
04063 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
04064
04065
04066
04067
04068 queue_t_ref(qe->parent, "For bridge_config reference");
04069 }
04070
04071 time(&callstart);
04072 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
04073 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
04074
04075
04076
04077
04078 ast_channel_lock(qe->chan);
04079 if (!attended_transfer_occurred(qe->chan)) {
04080 struct ast_datastore *tds;
04081
04082
04083 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
04084 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04085 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
04086 (long) (time(NULL) - callstart), qe->opos);
04087 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04088 } else if (ast_check_hangup(qe->chan)) {
04089 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
04090 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04091 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
04092 } else {
04093 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04094 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04095 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04096 }
04097 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
04098 ast_channel_datastore_remove(qe->chan, tds);
04099 }
04100 update_queue(qe->parent, member, callcompletedinsl);
04101 }
04102
04103 if (transfer_ds) {
04104 ast_datastore_free(transfer_ds);
04105 }
04106 ast_channel_unlock(qe->chan);
04107 ast_hangup(peer);
04108 res = bridge ? bridge : 1;
04109 ao2_ref(member, -1);
04110 }
04111 out:
04112 hangupcalls(outgoing, NULL);
04113
04114 return res;
04115 }
04116
04117 static int wait_a_bit(struct queue_ent *qe)
04118 {
04119
04120 int retrywait = qe->parent->retry * 1000;
04121
04122 int res = ast_waitfordigit(qe->chan, retrywait);
04123 if (res > 0 && !valid_exit(qe, res))
04124 res = 0;
04125
04126 return res;
04127 }
04128
04129 static struct member *interface_exists(struct call_queue *q, const char *interface)
04130 {
04131 struct member *mem;
04132 struct ao2_iterator mem_iter;
04133
04134 if (!q)
04135 return NULL;
04136
04137 mem_iter = ao2_iterator_init(q->members, 0);
04138 while ((mem = ao2_iterator_next(&mem_iter))) {
04139 if (!strcasecmp(interface, mem->interface)) {
04140 ao2_iterator_destroy(&mem_iter);
04141 return mem;
04142 }
04143 ao2_ref(mem, -1);
04144 }
04145 ao2_iterator_destroy(&mem_iter);
04146
04147 return NULL;
04148 }
04149
04150
04151
04152
04153
04154
04155 static void dump_queue_members(struct call_queue *pm_queue)
04156 {
04157 struct member *cur_member;
04158 char value[PM_MAX_LEN];
04159 int value_len = 0;
04160 int res;
04161 struct ao2_iterator mem_iter;
04162
04163 memset(value, 0, sizeof(value));
04164
04165 if (!pm_queue)
04166 return;
04167
04168 mem_iter = ao2_iterator_init(pm_queue->members, 0);
04169 while ((cur_member = ao2_iterator_next(&mem_iter))) {
04170 if (!cur_member->dynamic) {
04171 ao2_ref(cur_member, -1);
04172 continue;
04173 }
04174
04175 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04176 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04177
04178 ao2_ref(cur_member, -1);
04179
04180 if (res != strlen(value + value_len)) {
04181 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04182 break;
04183 }
04184 value_len += res;
04185 }
04186 ao2_iterator_destroy(&mem_iter);
04187
04188 if (value_len && !cur_member) {
04189 if (ast_db_put(pm_family, pm_queue->name, value))
04190 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04191 } else
04192
04193 ast_db_del(pm_family, pm_queue->name);
04194 }
04195
04196
04197
04198
04199
04200
04201
04202 static int remove_from_queue(const char *queuename, const char *interface)
04203 {
04204 struct call_queue *q, tmpq = {
04205 .name = queuename,
04206 };
04207 struct member *mem, tmpmem;
04208 int res = RES_NOSUCHQUEUE;
04209
04210 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04211 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
04212 ao2_lock(queues);
04213 ao2_lock(q);
04214 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04215
04216 if (!mem->dynamic) {
04217 ao2_ref(mem, -1);
04218 ao2_unlock(q);
04219 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
04220 ao2_unlock(queues);
04221 return RES_NOT_DYNAMIC;
04222 }
04223 q->membercount--;
04224 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04225 "Queue: %s\r\n"
04226 "Location: %s\r\n"
04227 "MemberName: %s\r\n",
04228 q->name, mem->interface, mem->membername);
04229 ao2_unlink(q->members, mem);
04230 remove_from_interfaces(mem->state_interface, 0);
04231 ao2_ref(mem, -1);
04232
04233 if (queue_persistent_members)
04234 dump_queue_members(q);
04235
04236 res = RES_OKAY;
04237 } else {
04238 res = RES_EXISTS;
04239 }
04240 ao2_unlock(q);
04241 ao2_unlock(queues);
04242 queue_t_unref(q, "Expiring temporary reference");
04243 }
04244
04245 return res;
04246 }
04247
04248
04249
04250
04251
04252
04253
04254
04255 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
04256 {
04257 struct call_queue *q;
04258 struct member *new_member, *old_member;
04259 int res = RES_NOSUCHQUEUE;
04260
04261
04262
04263 if (!(q = load_realtime_queue(queuename)))
04264 return res;
04265
04266 ao2_lock(queues);
04267
04268 ao2_lock(q);
04269 if ((old_member = interface_exists(q, interface)) == NULL) {
04270 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04271 add_to_interfaces(new_member->state_interface);
04272 new_member->dynamic = 1;
04273 ao2_link(q->members, new_member);
04274 q->membercount++;
04275 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04276 "Queue: %s\r\n"
04277 "Location: %s\r\n"
04278 "MemberName: %s\r\n"
04279 "Membership: %s\r\n"
04280 "Penalty: %d\r\n"
04281 "CallsTaken: %d\r\n"
04282 "LastCall: %d\r\n"
04283 "Status: %d\r\n"
04284 "Paused: %d\r\n",
04285 q->name, new_member->interface, new_member->membername,
04286 "dynamic",
04287 new_member->penalty, new_member->calls, (int) new_member->lastcall,
04288 new_member->status, new_member->paused);
04289
04290 ao2_ref(new_member, -1);
04291 new_member = NULL;
04292
04293 if (dump)
04294 dump_queue_members(q);
04295
04296 res = RES_OKAY;
04297 } else {
04298 res = RES_OUTOFMEMORY;
04299 }
04300 } else {
04301 ao2_ref(old_member, -1);
04302 res = RES_EXISTS;
04303 }
04304 ao2_unlock(q);
04305 ao2_unlock(queues);
04306
04307 return res;
04308 }
04309
04310 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
04311 {
04312 int found = 0;
04313 struct call_queue *q;
04314 struct member *mem;
04315 struct ao2_iterator queue_iter;
04316 int failed;
04317
04318
04319
04320 if (ast_strlen_zero(queuename))
04321 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04322
04323 queue_iter = ao2_iterator_init(queues, 0);
04324 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04325 ao2_lock(q);
04326 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04327 if ((mem = interface_exists(q, interface))) {
04328 if (mem->paused == paused) {
04329 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04330 }
04331
04332 failed = 0;
04333 if (mem->realtime) {
04334 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04335 }
04336
04337 if (failed) {
04338 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04339 ao2_ref(mem, -1);
04340 ao2_unlock(q);
04341 queue_t_unref(q, "Done with iterator");
04342 continue;
04343 }
04344 found++;
04345 mem->paused = paused;
04346
04347 if (queue_persistent_members)
04348 dump_queue_members(q);
04349
04350 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04351
04352 if (!ast_strlen_zero(reason)) {
04353 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04354 "Queue: %s\r\n"
04355 "Location: %s\r\n"
04356 "MemberName: %s\r\n"
04357 "Paused: %d\r\n"
04358 "Reason: %s\r\n",
04359 q->name, mem->interface, mem->membername, paused, reason);
04360 } else {
04361 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04362 "Queue: %s\r\n"
04363 "Location: %s\r\n"
04364 "MemberName: %s\r\n"
04365 "Paused: %d\r\n",
04366 q->name, mem->interface, mem->membername, paused);
04367 }
04368 ao2_ref(mem, -1);
04369 }
04370 }
04371
04372 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04373 ao2_unlock(q);
04374 queue_t_unref(q, "Done with iterator");
04375 break;
04376 }
04377
04378 ao2_unlock(q);
04379 queue_t_unref(q, "Done with iterator");
04380 }
04381 ao2_iterator_destroy(&queue_iter);
04382
04383 return found ? RESULT_SUCCESS : RESULT_FAILURE;
04384 }
04385
04386
04387 static int set_member_penalty(char *queuename, char *interface, int penalty)
04388 {
04389 int foundinterface = 0, foundqueue = 0;
04390 struct call_queue *q;
04391 struct member *mem;
04392 struct ao2_iterator queue_iter;
04393
04394 if (penalty < 0) {
04395 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04396 return RESULT_FAILURE;
04397 }
04398
04399 queue_iter = ao2_iterator_init(queues, 0);
04400 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04401 ao2_lock(q);
04402 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04403 foundqueue++;
04404 if ((mem = interface_exists(q, interface))) {
04405 foundinterface++;
04406 mem->penalty = penalty;
04407
04408 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04409 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04410 "Queue: %s\r\n"
04411 "Location: %s\r\n"
04412 "Penalty: %d\r\n",
04413 q->name, mem->interface, penalty);
04414 ao2_ref(mem, -1);
04415 }
04416 }
04417 ao2_unlock(q);
04418 queue_t_unref(q, "Done with iterator");
04419 }
04420 ao2_iterator_destroy(&queue_iter);
04421
04422 if (foundinterface) {
04423 return RESULT_SUCCESS;
04424 } else if (!foundqueue) {
04425 ast_log (LOG_ERROR, "Invalid queuename\n");
04426 } else {
04427 ast_log (LOG_ERROR, "Invalid interface\n");
04428 }
04429
04430 return RESULT_FAILURE;
04431 }
04432
04433
04434
04435
04436 static int get_member_penalty(char *queuename, char *interface)
04437 {
04438 int foundqueue = 0, penalty;
04439 struct call_queue *q, tmpq = {
04440 .name = queuename,
04441 };
04442 struct member *mem;
04443
04444 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
04445 foundqueue = 1;
04446 ao2_lock(q);
04447 if ((mem = interface_exists(q, interface))) {
04448 penalty = mem->penalty;
04449 ao2_ref(mem, -1);
04450 ao2_unlock(q);
04451 queue_t_unref(q, "Search complete");
04452 return penalty;
04453 }
04454 ao2_unlock(q);
04455 queue_t_unref(q, "Search complete");
04456 }
04457
04458
04459 if (foundqueue)
04460 ast_log (LOG_ERROR, "Invalid queuename\n");
04461 else
04462 ast_log (LOG_ERROR, "Invalid interface\n");
04463
04464 return RESULT_FAILURE;
04465 }
04466
04467
04468 static void reload_queue_members(void)
04469 {
04470 char *cur_ptr;
04471 const char *queue_name;
04472 char *member;
04473 char *interface;
04474 char *membername = NULL;
04475 char *state_interface;
04476 char *penalty_tok;
04477 int penalty = 0;
04478 char *paused_tok;
04479 int paused = 0;
04480 struct ast_db_entry *db_tree;
04481 struct ast_db_entry *entry;
04482 struct call_queue *cur_queue;
04483 char queue_data[PM_MAX_LEN];
04484
04485 ao2_lock(queues);
04486
04487
04488 db_tree = ast_db_gettree(pm_family, NULL);
04489 for (entry = db_tree; entry; entry = entry->next) {
04490
04491 queue_name = entry->key + strlen(pm_family) + 2;
04492
04493 {
04494 struct call_queue tmpq = {
04495 .name = queue_name,
04496 };
04497 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
04498 }
04499
04500 if (!cur_queue)
04501 cur_queue = load_realtime_queue(queue_name);
04502
04503 if (!cur_queue) {
04504
04505
04506 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04507 ast_db_del(pm_family, queue_name);
04508 continue;
04509 }
04510
04511 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04512 queue_t_unref(cur_queue, "Expire reload reference");
04513 continue;
04514 }
04515
04516 cur_ptr = queue_data;
04517 while ((member = strsep(&cur_ptr, ",|"))) {
04518 if (ast_strlen_zero(member))
04519 continue;
04520
04521 interface = strsep(&member, ";");
04522 penalty_tok = strsep(&member, ";");
04523 paused_tok = strsep(&member, ";");
04524 membername = strsep(&member, ";");
04525 state_interface = strsep(&member, ";");
04526
04527 if (!penalty_tok) {
04528 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04529 break;
04530 }
04531 penalty = strtol(penalty_tok, NULL, 10);
04532 if (errno == ERANGE) {
04533 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04534 break;
04535 }
04536
04537 if (!paused_tok) {
04538 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04539 break;
04540 }
04541 paused = strtol(paused_tok, NULL, 10);
04542 if ((errno == ERANGE) || paused < 0 || paused > 1) {
04543 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04544 break;
04545 }
04546
04547 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
04548
04549 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04550 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04551 break;
04552 }
04553 }
04554 queue_t_unref(cur_queue, "Expire reload reference");
04555 }
04556
04557 ao2_unlock(queues);
04558 if (db_tree) {
04559 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04560 ast_db_freetree(db_tree);
04561 }
04562 }
04563
04564
04565 static int pqm_exec(struct ast_channel *chan, void *data)
04566 {
04567 char *parse;
04568 AST_DECLARE_APP_ARGS(args,
04569 AST_APP_ARG(queuename);
04570 AST_APP_ARG(interface);
04571 AST_APP_ARG(options);
04572 AST_APP_ARG(reason);
04573 );
04574
04575 if (ast_strlen_zero(data)) {
04576 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04577 return -1;
04578 }
04579
04580 parse = ast_strdupa(data);
04581
04582 AST_STANDARD_APP_ARGS(args, parse);
04583
04584 if (ast_strlen_zero(args.interface)) {
04585 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04586 return -1;
04587 }
04588
04589 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04590 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04591 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04592 return 0;
04593 }
04594
04595 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04596
04597 return 0;
04598 }
04599
04600
04601 static int upqm_exec(struct ast_channel *chan, void *data)
04602 {
04603 char *parse;
04604 AST_DECLARE_APP_ARGS(args,
04605 AST_APP_ARG(queuename);
04606 AST_APP_ARG(interface);
04607 AST_APP_ARG(options);
04608 AST_APP_ARG(reason);
04609 );
04610
04611 if (ast_strlen_zero(data)) {
04612 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04613 return -1;
04614 }
04615
04616 parse = ast_strdupa(data);
04617
04618 AST_STANDARD_APP_ARGS(args, parse);
04619
04620 if (ast_strlen_zero(args.interface)) {
04621 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04622 return -1;
04623 }
04624
04625 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04626 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04627 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04628 return 0;
04629 }
04630
04631 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04632
04633 return 0;
04634 }
04635
04636
04637 static int rqm_exec(struct ast_channel *chan, void *data)
04638 {
04639 int res=-1;
04640 char *parse, *temppos = NULL;
04641 AST_DECLARE_APP_ARGS(args,
04642 AST_APP_ARG(queuename);
04643 AST_APP_ARG(interface);
04644 AST_APP_ARG(options);
04645 );
04646
04647
04648 if (ast_strlen_zero(data)) {
04649 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04650 return -1;
04651 }
04652
04653 parse = ast_strdupa(data);
04654
04655 AST_STANDARD_APP_ARGS(args, parse);
04656
04657 if (ast_strlen_zero(args.interface)) {
04658 args.interface = ast_strdupa(chan->name);
04659 temppos = strrchr(args.interface, '-');
04660 if (temppos)
04661 *temppos = '\0';
04662 }
04663
04664 switch (remove_from_queue(args.queuename, args.interface)) {
04665 case RES_OKAY:
04666 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04667 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04668 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04669 res = 0;
04670 break;
04671 case RES_EXISTS:
04672 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04673 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04674 res = 0;
04675 break;
04676 case RES_NOSUCHQUEUE:
04677 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04678 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04679 res = 0;
04680 break;
04681 case RES_NOT_DYNAMIC:
04682 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04683 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04684 res = 0;
04685 break;
04686 }
04687
04688 return res;
04689 }
04690
04691
04692 static int aqm_exec(struct ast_channel *chan, void *data)
04693 {
04694 int res=-1;
04695 char *parse, *temppos = NULL;
04696 AST_DECLARE_APP_ARGS(args,
04697 AST_APP_ARG(queuename);
04698 AST_APP_ARG(interface);
04699 AST_APP_ARG(penalty);
04700 AST_APP_ARG(options);
04701 AST_APP_ARG(membername);
04702 AST_APP_ARG(state_interface);
04703 );
04704 int penalty = 0;
04705
04706 if (ast_strlen_zero(data)) {
04707 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04708 return -1;
04709 }
04710
04711 parse = ast_strdupa(data);
04712
04713 AST_STANDARD_APP_ARGS(args, parse);
04714
04715 if (ast_strlen_zero(args.interface)) {
04716 args.interface = ast_strdupa(chan->name);
04717 temppos = strrchr(args.interface, '-');
04718 if (temppos)
04719 *temppos = '\0';
04720 }
04721
04722 if (!ast_strlen_zero(args.penalty)) {
04723 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04724 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04725 penalty = 0;
04726 }
04727 }
04728
04729 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04730 case RES_OKAY:
04731 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04732 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04733 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04734 res = 0;
04735 break;
04736 case RES_EXISTS:
04737 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04738 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04739 res = 0;
04740 break;
04741 case RES_NOSUCHQUEUE:
04742 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04743 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04744 res = 0;
04745 break;
04746 case RES_OUTOFMEMORY:
04747 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04748 break;
04749 }
04750
04751 return res;
04752 }
04753
04754
04755 static int ql_exec(struct ast_channel *chan, void *data)
04756 {
04757 char *parse;
04758
04759 AST_DECLARE_APP_ARGS(args,
04760 AST_APP_ARG(queuename);
04761 AST_APP_ARG(uniqueid);
04762 AST_APP_ARG(membername);
04763 AST_APP_ARG(event);
04764 AST_APP_ARG(params);
04765 );
04766
04767 if (ast_strlen_zero(data)) {
04768 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04769 return -1;
04770 }
04771
04772 parse = ast_strdupa(data);
04773
04774 AST_STANDARD_APP_ARGS(args, parse);
04775
04776 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04777 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04778 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04779 return -1;
04780 }
04781
04782 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
04783 "%s", args.params ? args.params : "");
04784
04785 return 0;
04786 }
04787
04788
04789 static void copy_rules(struct queue_ent *qe, const char *rulename)
04790 {
04791 struct penalty_rule *pr_iter;
04792 struct rule_list *rl_iter;
04793 const char *tmp = rulename;
04794 if (ast_strlen_zero(tmp)) {
04795 return;
04796 }
04797 AST_LIST_LOCK(&rule_lists);
04798 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04799 if (!strcasecmp(rl_iter->name, tmp))
04800 break;
04801 }
04802 if (rl_iter) {
04803 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04804 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04805 if (!new_pr) {
04806 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04807 AST_LIST_UNLOCK(&rule_lists);
04808 break;
04809 }
04810 new_pr->time = pr_iter->time;
04811 new_pr->max_value = pr_iter->max_value;
04812 new_pr->min_value = pr_iter->min_value;
04813 new_pr->max_relative = pr_iter->max_relative;
04814 new_pr->min_relative = pr_iter->min_relative;
04815 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04816 }
04817 }
04818 AST_LIST_UNLOCK(&rule_lists);
04819 }
04820
04821
04822
04823
04824
04825
04826
04827
04828
04829
04830
04831
04832
04833 static int queue_exec(struct ast_channel *chan, void *data)
04834 {
04835 int res=-1;
04836 int ringing=0;
04837 const char *user_priority;
04838 const char *max_penalty_str;
04839 const char *min_penalty_str;
04840 int prio;
04841 int qcontinue = 0;
04842 int max_penalty, min_penalty;
04843 enum queue_result reason = QUEUE_UNKNOWN;
04844
04845 int tries = 0;
04846 int noption = 0;
04847 char *parse;
04848 int makeannouncement = 0;
04849 AST_DECLARE_APP_ARGS(args,
04850 AST_APP_ARG(queuename);
04851 AST_APP_ARG(options);
04852 AST_APP_ARG(url);
04853 AST_APP_ARG(announceoverride);
04854 AST_APP_ARG(queuetimeoutstr);
04855 AST_APP_ARG(agi);
04856 AST_APP_ARG(macro);
04857 AST_APP_ARG(gosub);
04858 AST_APP_ARG(rule);
04859 );
04860
04861 struct queue_ent qe = { 0 };
04862
04863 if (ast_strlen_zero(data)) {
04864 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
04865 return -1;
04866 }
04867
04868 parse = ast_strdupa(data);
04869 AST_STANDARD_APP_ARGS(args, parse);
04870
04871
04872 qe.start = time(NULL);
04873
04874
04875 if (!ast_strlen_zero(args.queuetimeoutstr))
04876 qe.expire = qe.start + atoi(args.queuetimeoutstr);
04877 else
04878 qe.expire = 0;
04879
04880
04881 ast_channel_lock(chan);
04882 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04883 if (user_priority) {
04884 if (sscanf(user_priority, "%30d", &prio) == 1) {
04885 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04886 } else {
04887 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04888 user_priority, chan->name);
04889 prio = 0;
04890 }
04891 } else {
04892 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04893 prio = 0;
04894 }
04895
04896
04897
04898 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04899 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04900 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04901 } else {
04902 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04903 max_penalty_str, chan->name);
04904 max_penalty = 0;
04905 }
04906 } else {
04907 max_penalty = 0;
04908 }
04909
04910 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04911 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
04912 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04913 } else {
04914 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04915 min_penalty_str, chan->name);
04916 min_penalty = 0;
04917 }
04918 } else {
04919 min_penalty = 0;
04920 }
04921 ast_channel_unlock(chan);
04922
04923 if (args.options && (strchr(args.options, 'r')))
04924 ringing = 1;
04925
04926 if (args.options && (strchr(args.options, 'c')))
04927 qcontinue = 1;
04928
04929 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04930 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04931
04932 qe.chan = chan;
04933 qe.prio = prio;
04934 qe.max_penalty = max_penalty;
04935 qe.min_penalty = min_penalty;
04936 qe.last_pos_said = 0;
04937 qe.last_pos = 0;
04938 qe.last_periodic_announce_time = time(NULL);
04939 qe.last_periodic_announce_sound = 0;
04940 qe.valid_digits = 0;
04941 if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04942 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04943 set_queue_result(chan, reason);
04944 return 0;
04945 }
04946 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04947 S_OR(chan->cid.cid_num, ""));
04948 check_turns:
04949 if (ringing) {
04950 ast_indicate(chan, AST_CONTROL_RINGING);
04951 } else {
04952 ast_moh_start(chan, qe.moh, NULL);
04953 }
04954
04955
04956 res = wait_our_turn(&qe, ringing, &reason);
04957 if (res) {
04958 goto stop;
04959 }
04960
04961 makeannouncement = 0;
04962
04963 for (;;) {
04964
04965
04966
04967
04968
04969 enum queue_member_status status = QUEUE_NORMAL;
04970 int exit = 0;
04971
04972
04973 if (qe.expire && (time(NULL) >= qe.expire)) {
04974 record_abandoned(&qe);
04975 ast_cdr_noanswer(qe.chan->cdr);
04976 reason = QUEUE_TIMEOUT;
04977 res = 0;
04978 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
04979 qe.pos, qe.opos, (long) time(NULL) - qe.start);
04980 break;
04981 }
04982
04983 if (makeannouncement) {
04984
04985 if (qe.parent->announcefrequency)
04986 if ((res = say_position(&qe,ringing)))
04987 goto stop;
04988 }
04989 makeannouncement = 1;
04990
04991
04992 if (qe.parent->periodicannouncefrequency)
04993 if ((res = say_periodic_announcement(&qe,ringing)))
04994 goto stop;
04995
04996
04997 if (qe.expire && (time(NULL) >= qe.expire)) {
04998 record_abandoned(&qe);
04999 ast_cdr_noanswer(qe.chan->cdr);
05000 reason = QUEUE_TIMEOUT;
05001 res = 0;
05002 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05003 break;
05004 }
05005
05006
05007 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05008 update_qe_rule(&qe);
05009 }
05010
05011
05012 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05013 if (res) {
05014 goto stop;
05015 }
05016
05017
05018 if (noption && tries >= qe.parent->membercount) {
05019 ast_verb(3, "Exiting on time-out cycle\n");
05020 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05021 record_abandoned(&qe);
05022 ast_cdr_noanswer(qe.chan->cdr);
05023 reason = QUEUE_TIMEOUT;
05024 res = 0;
05025 break;
05026 }
05027
05028 for (; !exit || qe.pr; update_qe_rule(&qe)) {
05029 status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
05030
05031 if (!qe.pr || status == QUEUE_NORMAL) {
05032 break;
05033 }
05034
05035 if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
05036 ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
05037 ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
05038 continue;
05039 } else {
05040 exit = 1;
05041 }
05042 }
05043
05044
05045 if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
05046 record_abandoned(&qe);
05047 ast_cdr_noanswer(qe.chan->cdr);
05048 reason = QUEUE_LEAVEEMPTY;
05049 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05050 res = 0;
05051 break;
05052 }
05053
05054
05055 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
05056 record_abandoned(&qe);
05057 reason = QUEUE_LEAVEUNAVAIL;
05058 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05059 res = 0;
05060 break;
05061 }
05062 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
05063 record_abandoned(&qe);
05064 reason = QUEUE_LEAVEUNAVAIL;
05065 res = 0;
05066 break;
05067 }
05068
05069
05070 if (qe.expire && (time(NULL) >= qe.expire)) {
05071 record_abandoned(&qe);
05072 reason = QUEUE_TIMEOUT;
05073 res = 0;
05074 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05075 break;
05076 }
05077
05078
05079 update_realtime_members(qe.parent);
05080
05081 res = wait_a_bit(&qe);
05082 if (res)
05083 goto stop;
05084
05085
05086
05087
05088
05089 if (!is_our_turn(&qe)) {
05090 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05091 goto check_turns;
05092 }
05093 }
05094
05095 stop:
05096 if (res) {
05097 if (res < 0) {
05098 if (!qe.handled) {
05099 record_abandoned(&qe);
05100 ast_cdr_noanswer(qe.chan->cdr);
05101 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05102 "%d|%d|%ld", qe.pos, qe.opos,
05103 (long) time(NULL) - qe.start);
05104 res = -1;
05105 } else if (qcontinue) {
05106 reason = QUEUE_CONTINUE;
05107 res = 0;
05108 }
05109 } else if (qe.valid_digits) {
05110 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05111 "%s|%d", qe.digits, qe.pos);
05112 }
05113 }
05114
05115
05116 if (res >= 0) {
05117 res = 0;
05118 if (ringing) {
05119 ast_indicate(chan, -1);
05120 } else {
05121 ast_moh_stop(chan);
05122 }
05123 ast_stopstream(chan);
05124 }
05125
05126 set_queue_variables(qe.parent, qe.chan);
05127
05128 leave_queue(&qe);
05129 if (reason != QUEUE_UNKNOWN)
05130 set_queue_result(chan, reason);
05131
05132 if (qe.parent) {
05133
05134
05135
05136 qe.parent = queue_unref(qe.parent);
05137 }
05138
05139 return res;
05140 }
05141
05142
05143
05144
05145
05146
05147 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05148 {
05149 int res = -1;
05150 struct call_queue *q, tmpq = {
05151 .name = data,
05152 };
05153
05154 char interfacevar[256] = "";
05155 float sl = 0;
05156
05157 if (ast_strlen_zero(data)) {
05158 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05159 return -1;
05160 }
05161
05162 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
05163 ao2_lock(q);
05164 if (q->setqueuevar) {
05165 sl = 0;
05166 res = 0;
05167
05168 if (q->callscompleted > 0) {
05169 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05170 }
05171
05172 snprintf(interfacevar, sizeof(interfacevar),
05173 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05174 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
05175
05176 pbx_builtin_setvar_multiple(chan, interfacevar);
05177 }
05178
05179 ao2_unlock(q);
05180 queue_t_unref(q, "Done with temporary reference in QUEUE() function");
05181 } else {
05182 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05183 }
05184
05185 snprintf(buf, len, "%d", res);
05186
05187 return 0;
05188 }
05189
05190
05191
05192
05193
05194
05195 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05196 {
05197 int count = 0;
05198 struct member *m;
05199 struct ao2_iterator mem_iter;
05200 struct call_queue *q;
05201 char *option;
05202
05203 if (ast_strlen_zero(data)) {
05204 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05205 return -1;
05206 }
05207
05208 if ((option = strchr(data, ',')))
05209 *option++ = '\0';
05210 else
05211 option = "logged";
05212 if ((q = load_realtime_queue(data))) {
05213 ao2_lock(q);
05214 if (!strcasecmp(option, "logged")) {
05215 mem_iter = ao2_iterator_init(q->members, 0);
05216 while ((m = ao2_iterator_next(&mem_iter))) {
05217
05218 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05219 count++;
05220 }
05221 ao2_ref(m, -1);
05222 }
05223 ao2_iterator_destroy(&mem_iter);
05224 } else if (!strcasecmp(option, "free")) {
05225 mem_iter = ao2_iterator_init(q->members, 0);
05226 while ((m = ao2_iterator_next(&mem_iter))) {
05227
05228 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05229 count++;
05230 }
05231 ao2_ref(m, -1);
05232 }
05233 ao2_iterator_destroy(&mem_iter);
05234 } else
05235 count = q->membercount;
05236 ao2_unlock(q);
05237 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
05238 } else
05239 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05240
05241 snprintf(buf, len, "%d", count);
05242
05243 return 0;
05244 }
05245
05246
05247
05248
05249
05250
05251 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05252 {
05253 int count = 0;
05254 struct member *m;
05255 struct call_queue *q;
05256 struct ao2_iterator mem_iter;
05257 static int depflag = 1;
05258
05259 if (depflag) {
05260 depflag = 0;
05261 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");
05262 }
05263
05264 if (ast_strlen_zero(data)) {
05265 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05266 return -1;
05267 }
05268
05269 if ((q = load_realtime_queue(data))) {
05270 ao2_lock(q);
05271 mem_iter = ao2_iterator_init(q->members, 0);
05272 while ((m = ao2_iterator_next(&mem_iter))) {
05273
05274 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05275 count++;
05276 }
05277 ao2_ref(m, -1);
05278 }
05279 ao2_iterator_destroy(&mem_iter);
05280 ao2_unlock(q);
05281 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
05282 } else
05283 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05284
05285 snprintf(buf, len, "%d", count);
05286
05287 return 0;
05288 }
05289
05290
05291 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05292 {
05293 int count = 0;
05294 struct call_queue *q, tmpq = {
05295 .name = data,
05296 };
05297 struct ast_variable *var = NULL;
05298
05299 buf[0] = '\0';
05300
05301 if (ast_strlen_zero(data)) {
05302 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05303 return -1;
05304 }
05305
05306 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
05307 ao2_lock(q);
05308 count = q->count;
05309 ao2_unlock(q);
05310 queue_t_unref(q, "Done with find for QUEUE_WAITING_COUNT");
05311 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05312
05313
05314
05315
05316 count = 0;
05317 ast_variables_destroy(var);
05318 } else
05319 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05320
05321 snprintf(buf, len, "%d", count);
05322
05323 return 0;
05324 }
05325
05326
05327 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05328 {
05329 struct call_queue *q, tmpq = {
05330 .name = data,
05331 };
05332 struct member *m;
05333
05334
05335 buf[0] = '\0';
05336
05337 if (ast_strlen_zero(data)) {
05338 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05339 return -1;
05340 }
05341
05342 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
05343 int buflen = 0, count = 0;
05344 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05345
05346 ao2_lock(q);
05347 while ((m = ao2_iterator_next(&mem_iter))) {
05348
05349 if (count++) {
05350 strncat(buf + buflen, ",", len - buflen - 1);
05351 buflen++;
05352 }
05353 strncat(buf + buflen, m->interface, len - buflen - 1);
05354 buflen += strlen(m->interface);
05355
05356 if (buflen >= len - 2) {
05357 ao2_ref(m, -1);
05358 ast_log(LOG_WARNING, "Truncating list\n");
05359 break;
05360 }
05361 ao2_ref(m, -1);
05362 }
05363 ao2_iterator_destroy(&mem_iter);
05364 ao2_unlock(q);
05365 queue_t_unref(q, "Done with find for QUEUE_MEMBER_LIST()");
05366 } else
05367 ast_log(LOG_WARNING, "queue %s was not found\n", data);
05368
05369
05370 buf[len - 1] = '\0';
05371
05372 return 0;
05373 }
05374
05375
05376 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
05377 {
05378 int penalty;
05379 AST_DECLARE_APP_ARGS(args,
05380 AST_APP_ARG(queuename);
05381 AST_APP_ARG(interface);
05382 );
05383
05384 buf[0] = '\0';
05385
05386 if (ast_strlen_zero(data)) {
05387 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05388 return -1;
05389 }
05390
05391 AST_STANDARD_APP_ARGS(args, data);
05392
05393 if (args.argc < 2) {
05394 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05395 return -1;
05396 }
05397
05398 penalty = get_member_penalty (args.queuename, args.interface);
05399
05400 if (penalty >= 0)
05401 snprintf (buf, len, "%d", penalty);
05402
05403 return 0;
05404 }
05405
05406
05407 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
05408 {
05409 int penalty;
05410 AST_DECLARE_APP_ARGS(args,
05411 AST_APP_ARG(queuename);
05412 AST_APP_ARG(interface);
05413 );
05414
05415 if (ast_strlen_zero(data)) {
05416 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05417 return -1;
05418 }
05419
05420 AST_STANDARD_APP_ARGS(args, data);
05421
05422 if (args.argc < 2) {
05423 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05424 return -1;
05425 }
05426
05427 penalty = atoi(value);
05428
05429 if (ast_strlen_zero(args.interface)) {
05430 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05431 return -1;
05432 }
05433
05434
05435 if (set_member_penalty(args.queuename, args.interface, penalty)) {
05436 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05437 return -1;
05438 }
05439
05440 return 0;
05441 }
05442
05443 static struct ast_custom_function queuevar_function = {
05444 .name = "QUEUE_VARIABLES",
05445 .synopsis = "Return Queue information in variables",
05446 .syntax = "QUEUE_VARIABLES(<queuename>)",
05447 .desc =
05448 "Makes the following queue variables available.\n"
05449 "QUEUEMAX maxmimum number of calls allowed\n"
05450 "QUEUESTRATEGY the strategy of the queue\n"
05451 "QUEUECALLS number of calls currently in the queue\n"
05452 "QUEUEHOLDTIME current average hold time\n"
05453 "QUEUECOMPLETED number of completed calls for the queue\n"
05454 "QUEUEABANDONED number of abandoned calls\n"
05455 "QUEUESRVLEVEL queue service level\n"
05456 "QUEUESRVLEVELPERF current service level performance\n"
05457 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
05458 .read = queue_function_var,
05459 };
05460
05461 static struct ast_custom_function queuemembercount_function = {
05462 .name = "QUEUE_MEMBER",
05463 .synopsis = "Count number of members answering a queue",
05464 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
05465 .desc =
05466 "Returns the number of members currently associated with the specified queue.\n"
05467 "One of three options may be passed to determine the count returned:\n"
05468 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
05469 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
05470 "\"count\" - Returns the total number of members for the specified queue\n",
05471 .read = queue_function_qac,
05472 };
05473
05474 static struct ast_custom_function queuemembercount_dep = {
05475 .name = "QUEUE_MEMBER_COUNT",
05476 .synopsis = "Count number of members answering a queue",
05477 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
05478 .desc =
05479 "Returns the number of members currently associated with the specified queue.\n\n"
05480 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
05481 .read = queue_function_qac_dep,
05482 };
05483
05484 static struct ast_custom_function queuewaitingcount_function = {
05485 .name = "QUEUE_WAITING_COUNT",
05486 .synopsis = "Count number of calls currently waiting in a queue",
05487 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
05488 .desc =
05489 "Returns the number of callers currently waiting in the specified queue.\n",
05490 .read = queue_function_queuewaitingcount,
05491 };
05492
05493 static struct ast_custom_function queuememberlist_function = {
05494 .name = "QUEUE_MEMBER_LIST",
05495 .synopsis = "Returns a list of interfaces on a queue",
05496 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
05497 .desc =
05498 "Returns a comma-separated list of members associated with the specified queue.\n",
05499 .read = queue_function_queuememberlist,
05500 };
05501
05502 static struct ast_custom_function queuememberpenalty_function = {
05503 .name = "QUEUE_MEMBER_PENALTY",
05504 .synopsis = "Gets or sets queue members penalty.",
05505 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
05506 .desc =
05507 "Gets or sets queue members penalty\n",
05508 .read = queue_function_memberpenalty_read,
05509 .write = queue_function_memberpenalty_write,
05510 };
05511
05512 static int reload_queue_rules(int reload)
05513 {
05514 struct ast_config *cfg;
05515 struct rule_list *rl_iter, *new_rl;
05516 struct penalty_rule *pr_iter;
05517 char *rulecat = NULL;
05518 struct ast_variable *rulevar = NULL;
05519 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05520
05521 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05522 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05523 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05524 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05525 return AST_MODULE_LOAD_SUCCESS;
05526 } else {
05527 AST_LIST_LOCK(&rule_lists);
05528 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05529 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05530 ast_free(pr_iter);
05531 ast_free(rl_iter);
05532 }
05533 while ((rulecat = ast_category_browse(cfg, rulecat))) {
05534 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05535 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05536 AST_LIST_UNLOCK(&rule_lists);
05537 return AST_MODULE_LOAD_FAILURE;
05538 } else {
05539 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05540 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05541 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05542 if(!strcasecmp(rulevar->name, "penaltychange")) {
05543 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05544 } else {
05545 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05546 }
05547 }
05548 }
05549 AST_LIST_UNLOCK(&rule_lists);
05550 }
05551
05552 ast_config_destroy(cfg);
05553
05554 return AST_MODULE_LOAD_SUCCESS;
05555 }
05556
05557
05558 static int reload_queues(int reload)
05559 {
05560 struct call_queue *q;
05561 struct ast_config *cfg;
05562 char *cat, *tmp;
05563 struct ast_variable *var;
05564 struct member *cur, *newm;
05565 struct ao2_iterator mem_iter;
05566 int new;
05567 const char *general_val = NULL;
05568 char *parse;
05569 char *interface, *state_interface;
05570 char *membername = NULL;
05571 int penalty;
05572 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05573 struct ao2_iterator queue_iter;
05574 AST_DECLARE_APP_ARGS(args,
05575 AST_APP_ARG(interface);
05576 AST_APP_ARG(penalty);
05577 AST_APP_ARG(membername);
05578 AST_APP_ARG(state_interface);
05579 );
05580
05581
05582 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05583 return AST_MODULE_LOAD_FAILURE;
05584
05585 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05586 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05587 return 0;
05588 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05589 return 0;
05590 ao2_lock(queues);
05591 use_weight=0;
05592
05593 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
05594 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05595 if (!q->realtime) {
05596 q->dead = 1;
05597 q->found = 0;
05598 }
05599 queue_t_unref(q, "Done with iterator");
05600 }
05601
05602
05603 cat = NULL;
05604 while ((cat = ast_category_browse(cfg, cat)) ) {
05605 if (!strcasecmp(cat, "general")) {
05606
05607 queue_keep_stats = 0;
05608 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05609 queue_keep_stats = ast_true(general_val);
05610 queue_persistent_members = 0;
05611 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05612 queue_persistent_members = ast_true(general_val);
05613 autofill_default = 0;
05614 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05615 autofill_default = ast_true(general_val);
05616 montype_default = 0;
05617 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05618 if (!strcasecmp(general_val, "mixmonitor"))
05619 montype_default = 1;
05620 }
05621 update_cdr = 0;
05622 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05623 update_cdr = ast_true(general_val);
05624 shared_lastcall = 0;
05625 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05626 shared_lastcall = ast_true(general_val);
05627 } else {
05628
05629 struct call_queue tmpq = {
05630 .name = cat,
05631 };
05632 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Verify whether we exist or not"))) {
05633
05634 if (!(q = alloc_queue(cat))) {
05635
05636 }
05637 new = 1;
05638 } else
05639 new = 0;
05640 if (q) {
05641 const char *tmpvar = NULL;
05642 if (!new)
05643 ao2_lock(q);
05644
05645 if (q->found) {
05646 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05647 if (!new) {
05648 ao2_unlock(q);
05649 queue_t_unref(q, "We exist! Expiring temporary pointer");
05650 }
05651 continue;
05652 }
05653
05654
05655
05656 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05657 q->strategy = strat2int(tmpvar);
05658 if (q->strategy < 0) {
05659 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05660 tmpvar, q->name);
05661 q->strategy = QUEUE_STRATEGY_RINGALL;
05662 }
05663 } else
05664 q->strategy = QUEUE_STRATEGY_RINGALL;
05665
05666 init_queue(q);
05667 if (!queue_keep_stats)
05668 clear_queue(q);
05669 mem_iter = ao2_iterator_init(q->members, 0);
05670 while ((cur = ao2_iterator_next(&mem_iter))) {
05671 if (!cur->dynamic) {
05672 cur->delme = 1;
05673 }
05674 ao2_ref(cur, -1);
05675 }
05676 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05677 if (!strcasecmp(var->name, "member")) {
05678 struct member tmpmem;
05679 membername = NULL;
05680
05681 if (ast_strlen_zero(var->value)) {
05682 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05683 continue;
05684 }
05685
05686
05687 if (!(parse = ast_strdup(var->value))) {
05688 continue;
05689 }
05690
05691 AST_STANDARD_APP_ARGS(args, parse);
05692
05693 interface = args.interface;
05694 if (!ast_strlen_zero(args.penalty)) {
05695 tmp = args.penalty;
05696 while (*tmp && *tmp < 33) tmp++;
05697 penalty = atoi(tmp);
05698 if (penalty < 0) {
05699 penalty = 0;
05700 }
05701 } else
05702 penalty = 0;
05703
05704 if (!ast_strlen_zero(args.membername)) {
05705 membername = args.membername;
05706 while (*membername && *membername < 33) membername++;
05707 }
05708
05709 if (!ast_strlen_zero(args.state_interface)) {
05710 state_interface = args.state_interface;
05711 while (*state_interface && *state_interface < 33) state_interface++;
05712 } else
05713 state_interface = interface;
05714
05715
05716 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05717 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05718
05719 if (cur && strcasecmp(cur->state_interface, state_interface)) {
05720 remove_from_interfaces(cur->state_interface, 0);
05721 }
05722 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05723 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05724 add_to_interfaces(state_interface);
05725 ao2_link(q->members, newm);
05726 ao2_ref(newm, -1);
05727 newm = NULL;
05728
05729 if (cur)
05730 ao2_ref(cur, -1);
05731 else {
05732 q->membercount++;
05733 }
05734 ast_free(parse);
05735 } else {
05736 queue_set_param(q, var->name, var->value, var->lineno, 1);
05737 }
05738 }
05739
05740
05741 mem_iter = ao2_iterator_init(q->members, 0);
05742 while ((cur = ao2_iterator_next(&mem_iter))) {
05743 if (! cur->delme) {
05744 ao2_ref(cur, -1);
05745 continue;
05746 }
05747 q->membercount--;
05748 ao2_unlink(q->members, cur);
05749 remove_from_interfaces(cur->interface, 0);
05750 ao2_ref(cur, -1);
05751 }
05752
05753 if (new) {
05754 queues_t_link(queues, q, "Add queue to container");
05755 } else
05756 ao2_unlock(q);
05757 queue_t_unref(q, "Added queue to container, deleting creation pointer");
05758 }
05759 }
05760 }
05761 ast_config_destroy(cfg);
05762 queue_iter = ao2_iterator_init(queues, 0);
05763 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05764 if (q->dead) {
05765 queues_t_unlink(queues, q, "Remove queue from container because marked as dead");
05766 } else {
05767 ao2_lock(q);
05768 mem_iter = ao2_iterator_init(q->members, 0);
05769 while ((cur = ao2_iterator_next(&mem_iter))) {
05770 if (cur->dynamic)
05771 q->membercount++;
05772 cur->status = ast_device_state(cur->state_interface);
05773 ao2_ref(cur, -1);
05774 }
05775 ao2_unlock(q);
05776 }
05777 queue_t_unref(q, "Done with iterator");
05778 }
05779 ao2_unlock(queues);
05780 return 1;
05781 }
05782
05783
05784 static void do_print(struct mansession *s, int fd, const char *str)
05785 {
05786 if (s)
05787 astman_append(s, "%s\r\n", str);
05788 else
05789 ast_cli(fd, "%s\n", str);
05790 }
05791
05792
05793
05794
05795
05796
05797
05798 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
05799 {
05800 struct call_queue *q;
05801 struct ast_str *out = ast_str_alloca(240);
05802 int found = 0;
05803 time_t now = time(NULL);
05804 struct ao2_iterator queue_iter;
05805 struct ao2_iterator mem_iter;
05806
05807 if (argc != 2 && argc != 3)
05808 return CLI_SHOWUSAGE;
05809
05810 if (argc == 3) {
05811 if ((q = load_realtime_queue(argv[2]))) {
05812 queue_t_unref(q, "Expiring queue loaded from realtime");
05813 }
05814 } else if (ast_check_realtime("queues")) {
05815
05816
05817
05818 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05819 char *queuename;
05820 if (cfg) {
05821 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05822 if ((q = load_realtime_queue(queuename))) {
05823 queue_t_unref(q, "Expiring queue loaded from realtime");
05824 }
05825 }
05826 ast_config_destroy(cfg);
05827 }
05828 }
05829
05830 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
05831 ao2_lock(queues);
05832 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05833 float sl;
05834 struct call_queue *realtime_queue = NULL;
05835
05836 ao2_lock(q);
05837
05838
05839
05840
05841 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05842 ao2_unlock(q);
05843 queue_t_unref(q, "Done with iterator");
05844 continue;
05845 } else if (q->realtime) {
05846 queue_t_unref(realtime_queue, "Expire queue loaded from realtime");
05847 }
05848 if (argc == 3 && strcasecmp(q->name, argv[2])) {
05849 ao2_unlock(q);
05850 queue_t_unref(q, "Done with iterator");
05851 continue;
05852 }
05853 found = 1;
05854
05855 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
05856 if (q->maxlen)
05857 ast_str_append(&out, 0, "%d", q->maxlen);
05858 else
05859 ast_str_append(&out, 0, "unlimited");
05860 sl = 0;
05861 if (q->callscompleted > 0)
05862 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05863 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05864 int2strat(q->strategy), q->holdtime, q->weight,
05865 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05866 do_print(s, fd, out->str);
05867 if (!ao2_container_count(q->members))
05868 do_print(s, fd, " No Members");
05869 else {
05870 struct member *mem;
05871
05872 do_print(s, fd, " Members: ");
05873 mem_iter = ao2_iterator_init(q->members, 0);
05874 while ((mem = ao2_iterator_next(&mem_iter))) {
05875 ast_str_set(&out, 0, " %s", mem->membername);
05876 if (strcasecmp(mem->membername, mem->interface)) {
05877 ast_str_append(&out, 0, " (%s)", mem->interface);
05878 }
05879 if (mem->penalty)
05880 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05881 ast_str_append(&out, 0, "%s%s%s (%s)",
05882 mem->dynamic ? " (dynamic)" : "",
05883 mem->realtime ? " (realtime)" : "",
05884 mem->paused ? " (paused)" : "",
05885 devstate2str(mem->status));
05886 if (mem->calls)
05887 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05888 mem->calls, (long) (time(NULL) - mem->lastcall));
05889 else
05890 ast_str_append(&out, 0, " has taken no calls yet");
05891 do_print(s, fd, out->str);
05892 ao2_ref(mem, -1);
05893 }
05894 ao2_iterator_destroy(&mem_iter);
05895 }
05896 if (!q->head)
05897 do_print(s, fd, " No Callers");
05898 else {
05899 struct queue_ent *qe;
05900 int pos = 1;
05901
05902 do_print(s, fd, " Callers: ");
05903 for (qe = q->head; qe; qe = qe->next) {
05904 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
05905 pos++, qe->chan->name, (long) (now - qe->start) / 60,
05906 (long) (now - qe->start) % 60, qe->prio);
05907 do_print(s, fd, out->str);
05908 }
05909 }
05910 do_print(s, fd, "");
05911 ao2_unlock(q);
05912 queue_t_unref(q, "Done with iterator");
05913 }
05914 ao2_iterator_destroy(&queue_iter);
05915 ao2_unlock(queues);
05916 if (!found) {
05917 if (argc == 3)
05918 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05919 else
05920 ast_str_set(&out, 0, "No queues.");
05921 do_print(s, fd, out->str);
05922 }
05923 return CLI_SUCCESS;
05924 }
05925
05926 static char *complete_queue(const char *line, const char *word, int pos, int state)
05927 {
05928 struct call_queue *q;
05929 char *ret = NULL;
05930 int which = 0;
05931 int wordlen = strlen(word);
05932 struct ao2_iterator queue_iter;
05933
05934 queue_iter = ao2_iterator_init(queues, 0);
05935 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05936 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05937 ret = ast_strdup(q->name);
05938 queue_t_unref(q, "Done with iterator");
05939 break;
05940 }
05941 queue_t_unref(q, "Done with iterator");
05942 }
05943 ao2_iterator_destroy(&queue_iter);
05944
05945 return ret;
05946 }
05947
05948 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
05949 {
05950 if (pos == 2)
05951 return complete_queue(line, word, pos, state);
05952 return NULL;
05953 }
05954
05955 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
05956 {
05957 switch ( cmd ) {
05958 case CLI_INIT:
05959 e->command = "queue show";
05960 e->usage =
05961 "Usage: queue show\n"
05962 " Provides summary information on a specified queue.\n";
05963 return NULL;
05964 case CLI_GENERATE:
05965 return complete_queue_show(a->line, a->word, a->pos, a->n);
05966 }
05967
05968 return __queues_show(NULL, a->fd, a->argc, a->argv);
05969 }
05970
05971
05972
05973
05974 static int manager_queues_show(struct mansession *s, const struct message *m)
05975 {
05976 char *a[] = { "queue", "show" };
05977
05978 __queues_show(s, -1, 2, a);
05979 astman_append(s, "\r\n\r\n");
05980
05981 return RESULT_SUCCESS;
05982 }
05983
05984 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
05985 {
05986 const char *rule = astman_get_header(m, "Rule");
05987 struct rule_list *rl_iter;
05988 struct penalty_rule *pr_iter;
05989
05990 AST_LIST_LOCK(&rule_lists);
05991 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05992 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05993 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05994 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05995 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 );
05996 }
05997 if (!ast_strlen_zero(rule))
05998 break;
05999 }
06000 }
06001 AST_LIST_UNLOCK(&rule_lists);
06002
06003 astman_append(s, "\r\n\r\n");
06004
06005 return RESULT_SUCCESS;
06006 }
06007
06008
06009 static int manager_queues_summary(struct mansession *s, const struct message *m)
06010 {
06011 time_t now;
06012 int qmemcount = 0;
06013 int qmemavail = 0;
06014 int qchancount = 0;
06015 int qlongestholdtime = 0;
06016 const char *id = astman_get_header(m, "ActionID");
06017 const char *queuefilter = astman_get_header(m, "Queue");
06018 char idText[256] = "";
06019 struct call_queue *q;
06020 struct queue_ent *qe;
06021 struct member *mem;
06022 struct ao2_iterator queue_iter;
06023 struct ao2_iterator mem_iter;
06024
06025 astman_send_ack(s, m, "Queue summary will follow");
06026 time(&now);
06027 if (!ast_strlen_zero(id))
06028 snprintf(idText, 256, "ActionID: %s\r\n", id);
06029 queue_iter = ao2_iterator_init(queues, 0);
06030 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06031 ao2_lock(q);
06032
06033
06034 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06035
06036 qmemcount = 0;
06037 qmemavail = 0;
06038 qchancount = 0;
06039 qlongestholdtime = 0;
06040
06041
06042 mem_iter = ao2_iterator_init(q->members, 0);
06043 while ((mem = ao2_iterator_next(&mem_iter))) {
06044 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
06045 ++qmemcount;
06046 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
06047 ++qmemavail;
06048 }
06049 }
06050 ao2_ref(mem, -1);
06051 }
06052 ao2_iterator_destroy(&mem_iter);
06053 for (qe = q->head; qe; qe = qe->next) {
06054 if ((now - qe->start) > qlongestholdtime) {
06055 qlongestholdtime = now - qe->start;
06056 }
06057 ++qchancount;
06058 }
06059 astman_append(s, "Event: QueueSummary\r\n"
06060 "Queue: %s\r\n"
06061 "LoggedIn: %d\r\n"
06062 "Available: %d\r\n"
06063 "Callers: %d\r\n"
06064 "HoldTime: %d\r\n"
06065 "LongestHoldTime: %d\r\n"
06066 "%s"
06067 "\r\n",
06068 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
06069 }
06070 ao2_unlock(q);
06071 queue_t_unref(q, "Done with iterator");
06072 }
06073 ao2_iterator_destroy(&queue_iter);
06074 astman_append(s,
06075 "Event: QueueSummaryComplete\r\n"
06076 "%s"
06077 "\r\n", idText);
06078
06079 return RESULT_SUCCESS;
06080 }
06081
06082
06083 static int manager_queues_status(struct mansession *s, const struct message *m)
06084 {
06085 time_t now;
06086 int pos;
06087 const char *id = astman_get_header(m,"ActionID");
06088 const char *queuefilter = astman_get_header(m,"Queue");
06089 const char *memberfilter = astman_get_header(m,"Member");
06090 char idText[256] = "";
06091 struct call_queue *q;
06092 struct queue_ent *qe;
06093 float sl = 0;
06094 struct member *mem;
06095 struct ao2_iterator queue_iter;
06096 struct ao2_iterator mem_iter;
06097
06098 astman_send_ack(s, m, "Queue status will follow");
06099 time(&now);
06100 if (!ast_strlen_zero(id))
06101 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
06102
06103 queue_iter = ao2_iterator_init(queues, 0);
06104 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06105 ao2_lock(q);
06106
06107
06108 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06109 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
06110 astman_append(s, "Event: QueueParams\r\n"
06111 "Queue: %s\r\n"
06112 "Max: %d\r\n"
06113 "Strategy: %s\r\n"
06114 "Calls: %d\r\n"
06115 "Holdtime: %d\r\n"
06116 "Completed: %d\r\n"
06117 "Abandoned: %d\r\n"
06118 "ServiceLevel: %d\r\n"
06119 "ServicelevelPerf: %2.1f\r\n"
06120 "Weight: %d\r\n"
06121 "%s"
06122 "\r\n",
06123 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06124 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06125
06126 mem_iter = ao2_iterator_init(q->members, 0);
06127 while ((mem = ao2_iterator_next(&mem_iter))) {
06128 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06129 astman_append(s, "Event: QueueMember\r\n"
06130 "Queue: %s\r\n"
06131 "Name: %s\r\n"
06132 "Location: %s\r\n"
06133 "Membership: %s\r\n"
06134 "Penalty: %d\r\n"
06135 "CallsTaken: %d\r\n"
06136 "LastCall: %d\r\n"
06137 "Status: %d\r\n"
06138 "Paused: %d\r\n"
06139 "%s"
06140 "\r\n",
06141 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06142 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06143 }
06144 ao2_ref(mem, -1);
06145 }
06146 ao2_iterator_destroy(&mem_iter);
06147
06148 pos = 1;
06149 for (qe = q->head; qe; qe = qe->next) {
06150 astman_append(s, "Event: QueueEntry\r\n"
06151 "Queue: %s\r\n"
06152 "Position: %d\r\n"
06153 "Channel: %s\r\n"
06154 "CallerIDNum: %s\r\n"
06155 "CallerIDName: %s\r\n"
06156 "Wait: %ld\r\n"
06157 "%s"
06158 "\r\n",
06159 q->name, pos++, qe->chan->name,
06160 S_OR(qe->chan->cid.cid_num, "unknown"),
06161 S_OR(qe->chan->cid.cid_name, "unknown"),
06162 (long) (now - qe->start), idText);
06163 }
06164 }
06165 ao2_unlock(q);
06166 queue_t_unref(q, "Done with iterator");
06167 }
06168 ao2_iterator_destroy(&queue_iter);
06169
06170 astman_append(s,
06171 "Event: QueueStatusComplete\r\n"
06172 "%s"
06173 "\r\n",idText);
06174
06175 return RESULT_SUCCESS;
06176 }
06177
06178 static int manager_add_queue_member(struct mansession *s, const struct message *m)
06179 {
06180 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06181 int paused, penalty = 0;
06182
06183 queuename = astman_get_header(m, "Queue");
06184 interface = astman_get_header(m, "Interface");
06185 penalty_s = astman_get_header(m, "Penalty");
06186 paused_s = astman_get_header(m, "Paused");
06187 membername = astman_get_header(m, "MemberName");
06188 state_interface = astman_get_header(m, "StateInterface");
06189
06190 if (ast_strlen_zero(queuename)) {
06191 astman_send_error(s, m, "'Queue' not specified.");
06192 return 0;
06193 }
06194
06195 if (ast_strlen_zero(interface)) {
06196 astman_send_error(s, m, "'Interface' not specified.");
06197 return 0;
06198 }
06199
06200 if (ast_strlen_zero(penalty_s))
06201 penalty = 0;
06202 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
06203 penalty = 0;
06204
06205 if (ast_strlen_zero(paused_s))
06206 paused = 0;
06207 else
06208 paused = abs(ast_true(paused_s));
06209
06210 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06211 case RES_OKAY:
06212 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06213 astman_send_ack(s, m, "Added interface to queue");
06214 break;
06215 case RES_EXISTS:
06216 astman_send_error(s, m, "Unable to add interface: Already there");
06217 break;
06218 case RES_NOSUCHQUEUE:
06219 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06220 break;
06221 case RES_OUTOFMEMORY:
06222 astman_send_error(s, m, "Out of memory");
06223 break;
06224 }
06225
06226 return 0;
06227 }
06228
06229 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
06230 {
06231 const char *queuename, *interface;
06232
06233 queuename = astman_get_header(m, "Queue");
06234 interface = astman_get_header(m, "Interface");
06235
06236 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06237 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06238 return 0;
06239 }
06240
06241 switch (remove_from_queue(queuename, interface)) {
06242 case RES_OKAY:
06243 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06244 astman_send_ack(s, m, "Removed interface from queue");
06245 break;
06246 case RES_EXISTS:
06247 astman_send_error(s, m, "Unable to remove interface: Not there");
06248 break;
06249 case RES_NOSUCHQUEUE:
06250 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06251 break;
06252 case RES_OUTOFMEMORY:
06253 astman_send_error(s, m, "Out of memory");
06254 break;
06255 case RES_NOT_DYNAMIC:
06256 astman_send_error(s, m, "Member not dynamic");
06257 break;
06258 }
06259
06260 return 0;
06261 }
06262
06263 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
06264 {
06265 const char *queuename, *interface, *paused_s, *reason;
06266 int paused;
06267
06268 interface = astman_get_header(m, "Interface");
06269 paused_s = astman_get_header(m, "Paused");
06270 queuename = astman_get_header(m, "Queue");
06271 reason = astman_get_header(m, "Reason");
06272
06273 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06274 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06275 return 0;
06276 }
06277
06278 paused = abs(ast_true(paused_s));
06279
06280 if (set_member_paused(queuename, interface, reason, paused))
06281 astman_send_error(s, m, "Interface not found");
06282 else
06283 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06284 return 0;
06285 }
06286
06287 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
06288 {
06289 const char *queuename, *event, *message, *interface, *uniqueid;
06290
06291 queuename = astman_get_header(m, "Queue");
06292 uniqueid = astman_get_header(m, "UniqueId");
06293 interface = astman_get_header(m, "Interface");
06294 event = astman_get_header(m, "Event");
06295 message = astman_get_header(m, "Message");
06296
06297 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06298 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06299 return 0;
06300 }
06301
06302 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06303 astman_send_ack(s, m, "Event added successfully");
06304
06305 return 0;
06306 }
06307
06308 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
06309 {
06310
06311 switch (pos) {
06312 case 3:
06313 return NULL;
06314 case 4:
06315 return state == 0 ? ast_strdup("to") : NULL;
06316 case 5:
06317 return complete_queue(line, word, pos, state);
06318 case 6:
06319 return state == 0 ? ast_strdup("penalty") : NULL;
06320 case 7:
06321 if (state < 100) {
06322 char *num;
06323 if ((num = ast_malloc(3))) {
06324 sprintf(num, "%d", state);
06325 }
06326 return num;
06327 } else {
06328 return NULL;
06329 }
06330 case 8:
06331 return state == 0 ? ast_strdup("as") : NULL;
06332 case 9:
06333 return NULL;
06334 default:
06335 return NULL;
06336 }
06337 }
06338
06339 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
06340 {
06341 const char *queuename, *interface, *penalty_s;
06342 int penalty;
06343
06344 interface = astman_get_header(m, "Interface");
06345 penalty_s = astman_get_header(m, "Penalty");
06346
06347 queuename = astman_get_header(m, "Queue");
06348
06349 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06350 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06351 return 0;
06352 }
06353
06354 penalty = atoi(penalty_s);
06355
06356 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06357 astman_send_error(s, m, "Invalid interface, queuename or penalty");
06358 else
06359 astman_send_ack(s, m, "Interface penalty set successfully");
06360
06361 return 0;
06362 }
06363
06364 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06365 {
06366 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06367 int penalty;
06368
06369 switch ( cmd ) {
06370 case CLI_INIT:
06371 e->command = "queue add member";
06372 e->usage =
06373 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06374 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n";
06375 return NULL;
06376 case CLI_GENERATE:
06377 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06378 }
06379
06380 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06381 return CLI_SHOWUSAGE;
06382 } else if (strcmp(a->argv[4], "to")) {
06383 return CLI_SHOWUSAGE;
06384 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06385 return CLI_SHOWUSAGE;
06386 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06387 return CLI_SHOWUSAGE;
06388 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06389 return CLI_SHOWUSAGE;
06390 }
06391
06392 queuename = a->argv[5];
06393 interface = a->argv[3];
06394 if (a->argc >= 8) {
06395 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
06396 if (penalty < 0) {
06397 ast_cli(a->fd, "Penalty must be >= 0\n");
06398 penalty = 0;
06399 }
06400 } else {
06401 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06402 penalty = 0;
06403 }
06404 } else {
06405 penalty = 0;
06406 }
06407
06408 if (a->argc >= 10) {
06409 membername = a->argv[9];
06410 }
06411
06412 if (a->argc >= 12) {
06413 state_interface = a->argv[11];
06414 }
06415
06416 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06417 case RES_OKAY:
06418 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06419 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06420 return CLI_SUCCESS;
06421 case RES_EXISTS:
06422 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06423 return CLI_FAILURE;
06424 case RES_NOSUCHQUEUE:
06425 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06426 return CLI_FAILURE;
06427 case RES_OUTOFMEMORY:
06428 ast_cli(a->fd, "Out of memory\n");
06429 return CLI_FAILURE;
06430 case RES_NOT_DYNAMIC:
06431 ast_cli(a->fd, "Member not dynamic\n");
06432 return CLI_FAILURE;
06433 default:
06434 return CLI_FAILURE;
06435 }
06436 }
06437
06438 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
06439 {
06440 int which = 0;
06441 struct call_queue *q;
06442 struct member *m;
06443 struct ao2_iterator queue_iter;
06444 struct ao2_iterator mem_iter;
06445 int wordlen = strlen(word);
06446
06447
06448 if (pos > 5 || pos < 3)
06449 return NULL;
06450 if (pos == 4)
06451 return (state == 0 ? ast_strdup("from") : NULL);
06452
06453 if (pos == 5)
06454 return complete_queue(line, word, pos, state);
06455
06456
06457 queue_iter = ao2_iterator_init(queues, 0);
06458 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06459 ao2_lock(q);
06460 mem_iter = ao2_iterator_init(q->members, 0);
06461 while ((m = ao2_iterator_next(&mem_iter))) {
06462 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06463 char *tmp;
06464 ao2_unlock(q);
06465 tmp = ast_strdup(m->interface);
06466 ao2_ref(m, -1);
06467 queue_t_unref(q, "Done with iterator");
06468 ao2_iterator_destroy(&mem_iter);
06469 ao2_iterator_destroy(&queue_iter);
06470 return tmp;
06471 }
06472 ao2_ref(m, -1);
06473 }
06474 ao2_iterator_destroy(&mem_iter);
06475 ao2_unlock(q);
06476 queue_t_unref(q, "Done with iterator");
06477 }
06478 ao2_iterator_destroy(&queue_iter);
06479
06480 return NULL;
06481 }
06482
06483 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06484 {
06485 char *queuename, *interface;
06486
06487 switch (cmd) {
06488 case CLI_INIT:
06489 e->command = "queue remove member";
06490 e->usage =
06491 "Usage: queue remove member <channel> from <queue>\n"
06492 " Remove a specific channel from a queue.\n";
06493 return NULL;
06494 case CLI_GENERATE:
06495 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06496 }
06497
06498 if (a->argc != 6) {
06499 return CLI_SHOWUSAGE;
06500 } else if (strcmp(a->argv[4], "from")) {
06501 return CLI_SHOWUSAGE;
06502 }
06503
06504 queuename = a->argv[5];
06505 interface = a->argv[3];
06506
06507 switch (remove_from_queue(queuename, interface)) {
06508 case RES_OKAY:
06509 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06510 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06511 return CLI_SUCCESS;
06512 case RES_EXISTS:
06513 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06514 return CLI_FAILURE;
06515 case RES_NOSUCHQUEUE:
06516 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06517 return CLI_FAILURE;
06518 case RES_OUTOFMEMORY:
06519 ast_cli(a->fd, "Out of memory\n");
06520 return CLI_FAILURE;
06521 default:
06522 return CLI_FAILURE;
06523 }
06524 }
06525
06526 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
06527 {
06528
06529 switch (pos) {
06530 case 3:
06531 return NULL;
06532 case 4:
06533 return state == 0 ? ast_strdup("queue") : NULL;
06534 case 5:
06535 return complete_queue(line, word, pos, state);
06536 case 6:
06537 return state == 0 ? ast_strdup("reason") : NULL;
06538 case 7:
06539 return NULL;
06540 default:
06541 return NULL;
06542 }
06543 }
06544
06545 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06546 {
06547 char *queuename, *interface, *reason;
06548 int paused;
06549
06550 switch (cmd) {
06551 case CLI_INIT:
06552 e->command = "queue {pause|unpause} member";
06553 e->usage =
06554 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06555 " Pause or unpause a queue member. Not specifying a particular queue\n"
06556 " will pause or unpause a member across all queues to which the member\n"
06557 " belongs.\n";
06558 return NULL;
06559 case CLI_GENERATE:
06560 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06561 }
06562
06563 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06564 return CLI_SHOWUSAGE;
06565 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06566 return CLI_SHOWUSAGE;
06567 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06568 return CLI_SHOWUSAGE;
06569 }
06570
06571
06572 interface = a->argv[3];
06573 queuename = a->argc >= 6 ? a->argv[5] : NULL;
06574 reason = a->argc == 8 ? a->argv[7] : NULL;
06575 paused = !strcasecmp(a->argv[1], "pause");
06576
06577 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06578 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06579 if (!ast_strlen_zero(queuename))
06580 ast_cli(a->fd, " in queue '%s'", queuename);
06581 if (!ast_strlen_zero(reason))
06582 ast_cli(a->fd, " for reason '%s'", reason);
06583 ast_cli(a->fd, "\n");
06584 return CLI_SUCCESS;
06585 } else {
06586 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06587 if (!ast_strlen_zero(queuename))
06588 ast_cli(a->fd, " in queue '%s'", queuename);
06589 if (!ast_strlen_zero(reason))
06590 ast_cli(a->fd, " for reason '%s'", reason);
06591 ast_cli(a->fd, "\n");
06592 return CLI_FAILURE;
06593 }
06594 }
06595
06596 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
06597 {
06598
06599 switch (pos) {
06600 case 4:
06601 if (state == 0) {
06602 return ast_strdup("on");
06603 } else {
06604 return NULL;
06605 }
06606 case 6:
06607 if (state == 0) {
06608 return ast_strdup("in");
06609 } else {
06610 return NULL;
06611 }
06612 case 7:
06613 return complete_queue(line, word, pos, state);
06614 default:
06615 return NULL;
06616 }
06617 }
06618
06619 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06620 {
06621 char *queuename = NULL, *interface;
06622 int penalty = 0;
06623
06624 switch (cmd) {
06625 case CLI_INIT:
06626 e->command = "queue set penalty";
06627 e->usage =
06628 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06629 " Set a member's penalty in the queue specified. If no queue is specified\n"
06630 " then that interface's penalty is set in all queues to which that interface is a member\n";
06631 return NULL;
06632 case CLI_GENERATE:
06633 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06634 }
06635
06636 if (a->argc != 6 && a->argc != 8) {
06637 return CLI_SHOWUSAGE;
06638 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06639 return CLI_SHOWUSAGE;
06640 }
06641
06642 if (a->argc == 8)
06643 queuename = a->argv[7];
06644 interface = a->argv[5];
06645 penalty = atoi(a->argv[3]);
06646
06647 switch (set_member_penalty(queuename, interface, penalty)) {
06648 case RESULT_SUCCESS:
06649 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06650 return CLI_SUCCESS;
06651 case RESULT_FAILURE:
06652 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06653 return CLI_FAILURE;
06654 default:
06655 return CLI_FAILURE;
06656 }
06657 }
06658
06659 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
06660 {
06661 int which = 0;
06662 struct rule_list *rl_iter;
06663 int wordlen = strlen(word);
06664 char *ret = NULL;
06665 if (pos != 3) {
06666 return NULL;
06667 }
06668
06669 AST_LIST_LOCK(&rule_lists);
06670 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06671 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06672 ret = ast_strdup(rl_iter->name);
06673 break;
06674 }
06675 }
06676 AST_LIST_UNLOCK(&rule_lists);
06677
06678 return ret;
06679 }
06680
06681 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06682 {
06683 char *rule;
06684 struct rule_list *rl_iter;
06685 struct penalty_rule *pr_iter;
06686 switch (cmd) {
06687 case CLI_INIT:
06688 e->command = "queue rules show";
06689 e->usage =
06690 "Usage: queue rules show [rulename]\n"
06691 " Show the list of rules associated with rulename. If no\n"
06692 " rulename is specified, list all rules defined in queuerules.conf\n";
06693 return NULL;
06694 case CLI_GENERATE:
06695 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06696 }
06697
06698 if (a->argc != 3 && a->argc != 4)
06699 return CLI_SHOWUSAGE;
06700
06701 rule = a->argc == 4 ? a->argv[3] : "";
06702 AST_LIST_LOCK(&rule_lists);
06703 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06704 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06705 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06706 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06707 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);
06708 }
06709 }
06710 }
06711 AST_LIST_UNLOCK(&rule_lists);
06712 return CLI_SUCCESS;
06713 }
06714
06715 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06716 {
06717 switch (cmd) {
06718 case CLI_INIT:
06719 e->command = "queue rules reload";
06720 e->usage =
06721 "Usage: queue rules reload\n"
06722 " Reloads rules defined in queuerules.conf\n";
06723 return NULL;
06724 case CLI_GENERATE:
06725 return NULL;
06726 }
06727 reload_queue_rules(1);
06728 return CLI_SUCCESS;
06729 }
06730
06731 static const char qpm_cmd_usage[] =
06732 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
06733
06734 static const char qum_cmd_usage[] =
06735 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
06736
06737 static const char qsmp_cmd_usage[] =
06738 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
06739
06740 static struct ast_cli_entry cli_queue[] = {
06741 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
06742 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
06743 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
06744 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
06745 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
06746 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
06747 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
06748 };
06749
06750 static int unload_module(void)
06751 {
06752 int res;
06753 struct ast_context *con;
06754 struct ao2_iterator q_iter;
06755 struct call_queue *q = NULL;
06756
06757 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06758 res = ast_manager_unregister("QueueStatus");
06759 res |= ast_manager_unregister("Queues");
06760 res |= ast_manager_unregister("QueueRule");
06761 res |= ast_manager_unregister("QueueSummary");
06762 res |= ast_manager_unregister("QueueAdd");
06763 res |= ast_manager_unregister("QueueRemove");
06764 res |= ast_manager_unregister("QueuePause");
06765 res |= ast_manager_unregister("QueueLog");
06766 res |= ast_manager_unregister("QueuePenalty");
06767 res |= ast_unregister_application(app_aqm);
06768 res |= ast_unregister_application(app_rqm);
06769 res |= ast_unregister_application(app_pqm);
06770 res |= ast_unregister_application(app_upqm);
06771 res |= ast_unregister_application(app_ql);
06772 res |= ast_unregister_application(app);
06773 res |= ast_custom_function_unregister(&queuevar_function);
06774 res |= ast_custom_function_unregister(&queuemembercount_function);
06775 res |= ast_custom_function_unregister(&queuemembercount_dep);
06776 res |= ast_custom_function_unregister(&queuememberlist_function);
06777 res |= ast_custom_function_unregister(&queuewaitingcount_function);
06778 res |= ast_custom_function_unregister(&queuememberpenalty_function);
06779
06780 if (device_state_sub)
06781 ast_event_unsubscribe(device_state_sub);
06782
06783 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06784 ast_context_remove_extension2(con, "s", 1, NULL, 0);
06785 ast_context_destroy(con, "app_queue");
06786 }
06787
06788 clear_and_free_interfaces();
06789
06790 q_iter = ao2_iterator_init(queues, 0);
06791 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
06792 queues_t_unlink(queues, q, "Remove queue from container due to unload");
06793 queue_t_unref(q, "Done with iterator");
06794 }
06795 ao2_iterator_destroy(&q_iter);
06796 ao2_ref(queues, -1);
06797 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06798 ast_unload_realtime("queue_members");
06799 return res;
06800 }
06801
06802 static int load_module(void)
06803 {
06804 int res;
06805 struct ast_context *con;
06806
06807 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06808
06809 if (!reload_queues(0))
06810 return AST_MODULE_LOAD_DECLINE;
06811
06812 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06813 if (!con)
06814 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06815 else
06816 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06817
06818 if (queue_persistent_members)
06819 reload_queue_members();
06820
06821 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06822 res = ast_register_application(app, queue_exec, synopsis, descrip);
06823 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06824 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06825 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06826 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06827 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06828 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06829 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06830 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06831 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06832 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06833 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06834 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06835 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
06836 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06837 res |= ast_custom_function_register(&queuevar_function);
06838 res |= ast_custom_function_register(&queuemembercount_function);
06839 res |= ast_custom_function_register(&queuemembercount_dep);
06840 res |= ast_custom_function_register(&queuememberlist_function);
06841 res |= ast_custom_function_register(&queuewaitingcount_function);
06842 res |= ast_custom_function_register(&queuememberpenalty_function);
06843
06844 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06845 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06846 }
06847
06848 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06849 res = -1;
06850 }
06851
06852 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06853
06854 return res ? AST_MODULE_LOAD_DECLINE : 0;
06855 }
06856
06857 static int reload(void)
06858 {
06859 ast_unload_realtime("queue_members");
06860 reload_queues(1);
06861 return 0;
06862 }
06863
06864 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
06865 .load = load_module,
06866 .unload = unload_module,
06867 .reload = reload,
06868 );
06869