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
00064 #include "asterisk.h"
00065
00066 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369262 $")
00067
00068 #include <sys/time.h>
00069 #include <sys/signal.h>
00070 #include <netinet/in.h>
00071 #include <ctype.h>
00072
00073 #include "asterisk/lock.h"
00074 #include "asterisk/file.h"
00075 #include "asterisk/channel.h"
00076 #include "asterisk/pbx.h"
00077 #include "asterisk/app.h"
00078 #include "asterisk/linkedlists.h"
00079 #include "asterisk/module.h"
00080 #include "asterisk/translate.h"
00081 #include "asterisk/say.h"
00082 #include "asterisk/features.h"
00083 #include "asterisk/musiconhold.h"
00084 #include "asterisk/cli.h"
00085 #include "asterisk/manager.h"
00086 #include "asterisk/config.h"
00087 #include "asterisk/monitor.h"
00088 #include "asterisk/utils.h"
00089 #include "asterisk/causes.h"
00090 #include "asterisk/astdb.h"
00091 #include "asterisk/devicestate.h"
00092 #include "asterisk/stringfields.h"
00093 #include "asterisk/event.h"
00094 #include "asterisk/astobj2.h"
00095 #include "asterisk/strings.h"
00096 #include "asterisk/global_datastores.h"
00097 #include "asterisk/taskprocessor.h"
00098 #include "asterisk/aoc.h"
00099 #include "asterisk/callerid.h"
00100 #include "asterisk/cel.h"
00101 #include "asterisk/data.h"
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836 enum {
00837 QUEUE_STRATEGY_RINGALL = 0,
00838 QUEUE_STRATEGY_LEASTRECENT,
00839 QUEUE_STRATEGY_FEWESTCALLS,
00840 QUEUE_STRATEGY_RANDOM,
00841 QUEUE_STRATEGY_RRMEMORY,
00842 QUEUE_STRATEGY_LINEAR,
00843 QUEUE_STRATEGY_WRANDOM,
00844 QUEUE_STRATEGY_RRORDERED,
00845 };
00846
00847 enum {
00848 QUEUE_AUTOPAUSE_OFF = 0,
00849 QUEUE_AUTOPAUSE_ON,
00850 QUEUE_AUTOPAUSE_ALL
00851 };
00852
00853 enum queue_reload_mask {
00854 QUEUE_RELOAD_PARAMETERS = (1 << 0),
00855 QUEUE_RELOAD_MEMBER = (1 << 1),
00856 QUEUE_RELOAD_RULES = (1 << 2),
00857 QUEUE_RESET_STATS = (1 << 3),
00858 };
00859
00860 static const struct strategy {
00861 int strategy;
00862 const char *name;
00863 } strategies[] = {
00864 { QUEUE_STRATEGY_RINGALL, "ringall" },
00865 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00866 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00867 { QUEUE_STRATEGY_RANDOM, "random" },
00868 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00869 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
00870 { QUEUE_STRATEGY_LINEAR, "linear" },
00871 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
00872 { QUEUE_STRATEGY_RRORDERED, "rrordered"},
00873 };
00874
00875 static const struct autopause {
00876 int autopause;
00877 const char *name;
00878 } autopausesmodes [] = {
00879 { QUEUE_AUTOPAUSE_OFF,"no" },
00880 { QUEUE_AUTOPAUSE_ON, "yes" },
00881 { QUEUE_AUTOPAUSE_ALL,"all" },
00882 };
00883
00884
00885 static struct ast_taskprocessor *devicestate_tps;
00886
00887 #define DEFAULT_RETRY 5
00888 #define DEFAULT_TIMEOUT 15
00889 #define RECHECK 1
00890 #define MAX_PERIODIC_ANNOUNCEMENTS 10
00891 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
00892
00893 #define MAX_QUEUE_BUCKETS 53
00894
00895 #define RES_OKAY 0
00896 #define RES_EXISTS (-1)
00897 #define RES_OUTOFMEMORY (-2)
00898 #define RES_NOSUCHQUEUE (-3)
00899 #define RES_NOT_DYNAMIC (-4)
00900
00901 static char *app = "Queue";
00902
00903 static char *app_aqm = "AddQueueMember" ;
00904
00905 static char *app_rqm = "RemoveQueueMember" ;
00906
00907 static char *app_pqm = "PauseQueueMember" ;
00908
00909 static char *app_upqm = "UnpauseQueueMember" ;
00910
00911 static char *app_ql = "QueueLog" ;
00912
00913
00914 static const char * const pm_family = "Queue/PersistentMembers";
00915
00916 #define PM_MAX_LEN 8192
00917
00918
00919 static int queue_persistent_members = 0;
00920
00921
00922 static int use_weight = 0;
00923
00924
00925 static int autofill_default = 1;
00926
00927
00928 static int montype_default = 0;
00929
00930
00931 static int shared_lastcall = 1;
00932
00933
00934 static struct ast_event_sub *device_state_sub;
00935
00936
00937 static int update_cdr = 0;
00938
00939 enum queue_result {
00940 QUEUE_UNKNOWN = 0,
00941 QUEUE_TIMEOUT = 1,
00942 QUEUE_JOINEMPTY = 2,
00943 QUEUE_LEAVEEMPTY = 3,
00944 QUEUE_JOINUNAVAIL = 4,
00945 QUEUE_LEAVEUNAVAIL = 5,
00946 QUEUE_FULL = 6,
00947 QUEUE_CONTINUE = 7,
00948 };
00949
00950 static const struct {
00951 enum queue_result id;
00952 char *text;
00953 } queue_results[] = {
00954 { QUEUE_UNKNOWN, "UNKNOWN" },
00955 { QUEUE_TIMEOUT, "TIMEOUT" },
00956 { QUEUE_JOINEMPTY,"JOINEMPTY" },
00957 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00958 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00959 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00960 { QUEUE_FULL, "FULL" },
00961 { QUEUE_CONTINUE, "CONTINUE" },
00962 };
00963
00964 enum queue_timeout_priority {
00965 TIMEOUT_PRIORITY_APP,
00966 TIMEOUT_PRIORITY_CONF,
00967 };
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981 struct callattempt {
00982 struct callattempt *q_next;
00983 struct callattempt *call_next;
00984 struct ast_channel *chan;
00985 char interface[256];
00986 int metric;
00987 time_t lastcall;
00988 struct call_queue *lastqueue;
00989 struct member *member;
00990
00991 struct ast_party_connected_line connected;
00992
00993 unsigned int pending_connected_update:1;
00994
00995 unsigned int block_connected_update:1;
00996
00997 unsigned int dial_callerid_absent:1;
00998
00999 unsigned int stillgoing:1;
01000 struct ast_aoc_decoded *aoc_s_rate_list;
01001 };
01002
01003
01004 struct queue_ent {
01005 struct call_queue *parent;
01006 char moh[80];
01007 char announce[PATH_MAX];
01008 char context[AST_MAX_CONTEXT];
01009 char digits[AST_MAX_EXTENSION];
01010 int valid_digits;
01011 int pos;
01012 int prio;
01013 int last_pos_said;
01014 int ring_when_ringing;
01015 time_t last_periodic_announce_time;
01016 int last_periodic_announce_sound;
01017 time_t last_pos;
01018 int opos;
01019 int handled;
01020 int pending;
01021 int max_penalty;
01022 int min_penalty;
01023 int linpos;
01024 int linwrapped;
01025 time_t start;
01026 time_t expire;
01027 int cancel_answered_elsewhere;
01028 struct ast_channel *chan;
01029 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules;
01030 struct penalty_rule *pr;
01031 struct queue_ent *next;
01032 };
01033
01034 struct member {
01035 char interface[80];
01036 char state_exten[AST_MAX_EXTENSION];
01037 char state_context[AST_MAX_CONTEXT];
01038 char state_interface[80];
01039 char membername[80];
01040 int penalty;
01041 int calls;
01042 int dynamic;
01043 int realtime;
01044 int status;
01045 int paused;
01046 time_t lastcall;
01047 struct call_queue *lastqueue;
01048 unsigned int dead:1;
01049 unsigned int delme:1;
01050 char rt_uniqueid[80];
01051 };
01052
01053 enum empty_conditions {
01054 QUEUE_EMPTY_PENALTY = (1 << 0),
01055 QUEUE_EMPTY_PAUSED = (1 << 1),
01056 QUEUE_EMPTY_INUSE = (1 << 2),
01057 QUEUE_EMPTY_RINGING = (1 << 3),
01058 QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01059 QUEUE_EMPTY_INVALID = (1 << 5),
01060 QUEUE_EMPTY_UNKNOWN = (1 << 6),
01061 QUEUE_EMPTY_WRAPUP = (1 << 7),
01062 };
01063
01064
01065 #define ANNOUNCEHOLDTIME_ALWAYS 1
01066 #define ANNOUNCEHOLDTIME_ONCE 2
01067 #define QUEUE_EVENT_VARIABLES 3
01068
01069 struct penalty_rule {
01070 int time;
01071 int max_value;
01072 int min_value;
01073 int max_relative;
01074 int min_relative;
01075 AST_LIST_ENTRY(penalty_rule) list;
01076 };
01077
01078 #define ANNOUNCEPOSITION_YES 1
01079 #define ANNOUNCEPOSITION_NO 2
01080 #define ANNOUNCEPOSITION_MORE_THAN 3
01081 #define ANNOUNCEPOSITION_LIMIT 4
01082
01083 struct call_queue {
01084 AST_DECLARE_STRING_FIELDS(
01085
01086 AST_STRING_FIELD(name);
01087
01088 AST_STRING_FIELD(moh);
01089
01090 AST_STRING_FIELD(announce);
01091
01092 AST_STRING_FIELD(context);
01093
01094 AST_STRING_FIELD(membermacro);
01095
01096 AST_STRING_FIELD(membergosub);
01097
01098 AST_STRING_FIELD(defaultrule);
01099
01100 AST_STRING_FIELD(sound_next);
01101
01102 AST_STRING_FIELD(sound_thereare);
01103
01104 AST_STRING_FIELD(sound_calls);
01105
01106 AST_STRING_FIELD(queue_quantity1);
01107
01108 AST_STRING_FIELD(queue_quantity2);
01109
01110 AST_STRING_FIELD(sound_holdtime);
01111
01112 AST_STRING_FIELD(sound_minutes);
01113
01114 AST_STRING_FIELD(sound_minute);
01115
01116 AST_STRING_FIELD(sound_seconds);
01117
01118 AST_STRING_FIELD(sound_thanks);
01119
01120 AST_STRING_FIELD(sound_callerannounce);
01121
01122 AST_STRING_FIELD(sound_reporthold);
01123 );
01124
01125 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
01126 unsigned int dead:1;
01127 unsigned int eventwhencalled:2;
01128 unsigned int ringinuse:1;
01129 unsigned int setinterfacevar:1;
01130 unsigned int setqueuevar:1;
01131 unsigned int setqueueentryvar:1;
01132 unsigned int reportholdtime:1;
01133 unsigned int wrapped:1;
01134 unsigned int timeoutrestart:1;
01135 unsigned int announceholdtime:2;
01136 unsigned int announceposition:3;
01137 int strategy:4;
01138 unsigned int maskmemberstatus:1;
01139 unsigned int realtime:1;
01140 unsigned int found:1;
01141 unsigned int relativeperiodicannounce:1;
01142 enum empty_conditions joinempty;
01143 enum empty_conditions leavewhenempty;
01144 int announcepositionlimit;
01145 int announcefrequency;
01146 int minannouncefrequency;
01147 int periodicannouncefrequency;
01148 int numperiodicannounce;
01149 int randomperiodicannounce;
01150 int roundingseconds;
01151 int holdtime;
01152 int talktime;
01153 int callscompleted;
01154 int callsabandoned;
01155 int servicelevel;
01156 int callscompletedinsl;
01157 char monfmt[8];
01158 int montype;
01159 int count;
01160 int maxlen;
01161 int wrapuptime;
01162 int penaltymemberslimit;
01163
01164 int retry;
01165 int timeout;
01166 int weight;
01167 int autopause;
01168 int timeoutpriority;
01169
01170
01171 int rrpos;
01172 int memberdelay;
01173 int autofill;
01174
01175 struct ao2_container *members;
01176 struct queue_ent *head;
01177 AST_LIST_ENTRY(call_queue) list;
01178 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules;
01179 };
01180
01181 struct rule_list {
01182 char name[80];
01183 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
01184 AST_LIST_ENTRY(rule_list) list;
01185 };
01186
01187 static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
01188
01189 static struct ao2_container *queues;
01190
01191 static void update_realtime_members(struct call_queue *q);
01192 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
01193
01194 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
01195
01196 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
01197 {
01198 int i;
01199
01200 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01201 if (queue_results[i].id == res) {
01202 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01203 return;
01204 }
01205 }
01206 }
01207
01208 static const char *int2strat(int strategy)
01209 {
01210 int x;
01211
01212 for (x = 0; x < ARRAY_LEN(strategies); x++) {
01213 if (strategy == strategies[x].strategy)
01214 return strategies[x].name;
01215 }
01216
01217 return "<unknown>";
01218 }
01219
01220 static int strat2int(const char *strategy)
01221 {
01222 int x;
01223
01224 for (x = 0; x < ARRAY_LEN(strategies); x++) {
01225 if (!strcasecmp(strategy, strategies[x].name))
01226 return strategies[x].strategy;
01227 }
01228
01229 return -1;
01230 }
01231
01232 static int autopause2int(const char *autopause)
01233 {
01234 int x;
01235
01236 if (ast_strlen_zero(autopause))
01237 return QUEUE_AUTOPAUSE_OFF;
01238
01239
01240 if(ast_true(autopause))
01241 return QUEUE_AUTOPAUSE_ON;
01242
01243 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01244 if (!strcasecmp(autopause, autopausesmodes[x].name))
01245 return autopausesmodes[x].autopause;
01246 }
01247
01248
01249 return QUEUE_AUTOPAUSE_OFF;
01250 }
01251
01252 static int queue_hash_cb(const void *obj, const int flags)
01253 {
01254 const struct call_queue *q = obj;
01255
01256 return ast_str_case_hash(q->name);
01257 }
01258
01259 static int queue_cmp_cb(void *obj, void *arg, int flags)
01260 {
01261 struct call_queue *q = obj, *q2 = arg;
01262 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01263 }
01264
01265 #ifdef REF_DEBUG_ONLY_QUEUES
01266 #define queue_ref(a) __ao2_ref_debug(a,1,"",__FILE__,__LINE__,__PRETTY_FUNCTION__)
01267 #define queue_unref(a) __ao2_ref_debug(a,-1,"",__FILE__,__LINE__,__PRETTY_FUNCTION__)
01268 #define queue_t_ref(a,b) __ao2_ref_debug(a,1,b,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01269 #define queue_t_unref(a,b) __ao2_ref_debug(a,-1,b,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01270 #define queues_t_link(c,q,tag) __ao2_link_debug(c,q,tag,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01271 #define queues_t_unlink(c,q,tag) __ao2_unlink_debug(c,q,tag,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01272 #else
01273 #define queue_t_ref(a,b) queue_ref(a)
01274 #define queue_t_unref(a,b) queue_unref(a)
01275 #define queues_t_link(c,q,tag) ao2_t_link(c,q,tag)
01276 #define queues_t_unlink(c,q,tag) ao2_t_unlink(c,q,tag)
01277 static inline struct call_queue *queue_ref(struct call_queue *q)
01278 {
01279 ao2_ref(q, 1);
01280 return q;
01281 }
01282
01283 static inline struct call_queue *queue_unref(struct call_queue *q)
01284 {
01285 ao2_ref(q, -1);
01286 return NULL;
01287 }
01288 #endif
01289
01290
01291 static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
01292 {
01293 char interfacevar[256]="";
01294 float sl = 0;
01295
01296 ao2_lock(q);
01297
01298 if (q->setqueuevar) {
01299 sl = 0;
01300 if (q->callscompleted > 0)
01301 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01302
01303 snprintf(interfacevar, sizeof(interfacevar),
01304 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01305 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
01306
01307 ao2_unlock(q);
01308
01309 pbx_builtin_setvar_multiple(chan, interfacevar);
01310 } else {
01311 ao2_unlock(q);
01312 }
01313 }
01314
01315
01316 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
01317 {
01318 struct queue_ent *cur;
01319
01320 if (!q || !new)
01321 return;
01322 if (prev) {
01323 cur = prev->next;
01324 prev->next = new;
01325 } else {
01326 cur = q->head;
01327 q->head = new;
01328 }
01329 new->next = cur;
01330
01331
01332
01333
01334 queue_ref(q);
01335 new->parent = q;
01336 new->pos = ++(*pos);
01337 new->opos = *pos;
01338 }
01339
01340
01341
01342
01343
01344
01345
01346 static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
01347 {
01348 struct member *member;
01349 struct ao2_iterator mem_iter;
01350
01351 ao2_lock(q);
01352 mem_iter = ao2_iterator_init(q->members, 0);
01353 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01354 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
01355 if (conditions & QUEUE_EMPTY_PENALTY) {
01356 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01357 continue;
01358 }
01359 }
01360
01361 switch (member->status) {
01362 case AST_DEVICE_INVALID:
01363 if (conditions & QUEUE_EMPTY_INVALID) {
01364 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01365 break;
01366 }
01367 goto default_case;
01368 case AST_DEVICE_UNAVAILABLE:
01369 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01370 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01371 break;
01372 }
01373 goto default_case;
01374 case AST_DEVICE_INUSE:
01375 if (conditions & QUEUE_EMPTY_INUSE) {
01376 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01377 break;
01378 }
01379 goto default_case;
01380 case AST_DEVICE_RINGING:
01381 if (conditions & QUEUE_EMPTY_RINGING) {
01382 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01383 break;
01384 }
01385 goto default_case;
01386 case AST_DEVICE_UNKNOWN:
01387 if (conditions & QUEUE_EMPTY_UNKNOWN) {
01388 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01389 break;
01390 }
01391
01392 default:
01393 default_case:
01394 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01395 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01396 break;
01397 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01398 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
01399 break;
01400 } else {
01401 ao2_ref(member, -1);
01402 ao2_iterator_destroy(&mem_iter);
01403 ao2_unlock(q);
01404 ast_debug(4, "%s is available.\n", member->membername);
01405 return 0;
01406 }
01407 break;
01408 }
01409 }
01410 ao2_iterator_destroy(&mem_iter);
01411
01412 ao2_unlock(q);
01413 return -1;
01414 }
01415
01416 struct statechange {
01417 AST_LIST_ENTRY(statechange) entry;
01418 int state;
01419 char dev[0];
01420 };
01421
01422
01423
01424
01425
01426
01427 static int update_status(struct call_queue *q, struct member *m, const int status)
01428 {
01429 m->status = status;
01430
01431 if (q->maskmemberstatus)
01432 return 0;
01433
01434 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01435 "Queue: %s\r\n"
01436 "Location: %s\r\n"
01437 "MemberName: %s\r\n"
01438 "Membership: %s\r\n"
01439 "Penalty: %d\r\n"
01440 "CallsTaken: %d\r\n"
01441 "LastCall: %d\r\n"
01442 "Status: %d\r\n"
01443 "Paused: %d\r\n",
01444 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01445 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01446 );
01447
01448 return 0;
01449 }
01450
01451
01452 static int handle_statechange(void *datap)
01453 {
01454 struct statechange *sc = datap;
01455 struct ao2_iterator miter, qiter;
01456 struct member *m;
01457 struct call_queue *q;
01458 char interface[80], *slash_pos;
01459 int found = 0;
01460
01461 qiter = ao2_iterator_init(queues, 0);
01462 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01463 ao2_lock(q);
01464
01465 miter = ao2_iterator_init(q->members, 0);
01466 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01467 ast_copy_string(interface, m->state_interface, sizeof(interface));
01468
01469 if ((slash_pos = strchr(interface, '/')))
01470 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01471 *slash_pos = '\0';
01472
01473 if (!strcasecmp(interface, sc->dev)) {
01474 found = 1;
01475 update_status(q, m, sc->state);
01476 ao2_ref(m, -1);
01477 break;
01478 }
01479 }
01480 ao2_iterator_destroy(&miter);
01481
01482 ao2_unlock(q);
01483 queue_t_unref(q, "Done with iterator");
01484 }
01485 ao2_iterator_destroy(&qiter);
01486
01487 if (found)
01488 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01489 else
01490 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, ast_devstate2str(sc->state));
01491
01492 ast_free(sc);
01493 return 0;
01494 }
01495
01496 static void device_state_cb(const struct ast_event *event, void *unused)
01497 {
01498 enum ast_device_state state;
01499 const char *device;
01500 struct statechange *sc;
01501 size_t datapsize;
01502
01503 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01504 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01505
01506 if (ast_strlen_zero(device)) {
01507 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01508 return;
01509 }
01510 datapsize = sizeof(*sc) + strlen(device) + 1;
01511 if (!(sc = ast_calloc(1, datapsize))) {
01512 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01513 return;
01514 }
01515 sc->state = state;
01516 strcpy(sc->dev, device);
01517 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01518 ast_free(sc);
01519 }
01520 }
01521
01522
01523 static int extensionstate2devicestate(int state)
01524 {
01525 switch (state) {
01526 case AST_EXTENSION_NOT_INUSE:
01527 state = AST_DEVICE_NOT_INUSE;
01528 break;
01529 case AST_EXTENSION_INUSE:
01530 state = AST_DEVICE_INUSE;
01531 break;
01532 case AST_EXTENSION_BUSY:
01533 state = AST_DEVICE_BUSY;
01534 break;
01535 case AST_EXTENSION_RINGING:
01536 state = AST_DEVICE_RINGING;
01537 break;
01538 case AST_EXTENSION_ONHOLD:
01539 state = AST_DEVICE_ONHOLD;
01540 break;
01541 case AST_EXTENSION_UNAVAILABLE:
01542 state = AST_DEVICE_UNAVAILABLE;
01543 break;
01544 case AST_EXTENSION_REMOVED:
01545 case AST_EXTENSION_DEACTIVATED:
01546 default:
01547 state = AST_DEVICE_INVALID;
01548 break;
01549 }
01550
01551 return state;
01552 }
01553
01554 static int extension_state_cb(char *context, char *exten, enum ast_extension_states state, void *data)
01555 {
01556 struct ao2_iterator miter, qiter;
01557 struct member *m;
01558 struct call_queue *q;
01559 int found = 0, device_state = extensionstate2devicestate(state);
01560
01561 qiter = ao2_iterator_init(queues, 0);
01562 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01563 ao2_lock(q);
01564
01565 miter = ao2_iterator_init(q->members, 0);
01566 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01567 if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01568 update_status(q, m, device_state);
01569 ao2_ref(m, -1);
01570 found = 1;
01571 break;
01572 }
01573 }
01574 ao2_iterator_destroy(&miter);
01575
01576 ao2_unlock(q);
01577 queue_t_unref(q, "Done with iterator");
01578 }
01579 ao2_iterator_destroy(&qiter);
01580
01581 if (found) {
01582 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01583 } else {
01584 ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
01585 exten, context, device_state, ast_devstate2str(device_state));
01586 }
01587
01588 return 0;
01589 }
01590
01591
01592 static int get_queue_member_status(struct member *cur)
01593 {
01594 return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
01595 }
01596
01597
01598 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
01599 {
01600 struct member *cur;
01601
01602 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01603 cur->penalty = penalty;
01604 cur->paused = paused;
01605 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01606 if (!ast_strlen_zero(state_interface))
01607 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01608 else
01609 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01610 if (!ast_strlen_zero(membername))
01611 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01612 else
01613 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01614 if (!strchr(cur->interface, '/'))
01615 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01616 if (!strncmp(cur->state_interface, "hint:", 5)) {
01617 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01618 char *exten = strsep(&context, "@") + 5;
01619
01620 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01621 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01622 }
01623 cur->status = get_queue_member_status(cur);
01624 }
01625
01626 return cur;
01627 }
01628
01629
01630 static int compress_char(const char c)
01631 {
01632 if (c < 32)
01633 return 0;
01634 else if (c > 96)
01635 return c - 64;
01636 else
01637 return c - 32;
01638 }
01639
01640 static int member_hash_fn(const void *obj, const int flags)
01641 {
01642 const struct member *mem = obj;
01643 const char *chname = strchr(mem->interface, '/');
01644 int ret = 0, i;
01645 if (!chname)
01646 chname = mem->interface;
01647 for (i = 0; i < 5 && chname[i]; i++)
01648 ret += compress_char(chname[i]) << (i * 6);
01649 return ret;
01650 }
01651
01652 static int member_cmp_fn(void *obj1, void *obj2, int flags)
01653 {
01654 struct member *mem1 = obj1, *mem2 = obj2;
01655 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01656 }
01657
01658
01659
01660
01661
01662 static void init_queue(struct call_queue *q)
01663 {
01664 int i;
01665 struct penalty_rule *pr_iter;
01666
01667 q->dead = 0;
01668 q->retry = DEFAULT_RETRY;
01669 q->timeout = DEFAULT_TIMEOUT;
01670 q->maxlen = 0;
01671 q->announcefrequency = 0;
01672 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01673 q->announceholdtime = 1;
01674 q->announcepositionlimit = 10;
01675 q->announceposition = ANNOUNCEPOSITION_YES;
01676 q->roundingseconds = 0;
01677 q->servicelevel = 0;
01678 q->ringinuse = 1;
01679 q->setinterfacevar = 0;
01680 q->setqueuevar = 0;
01681 q->setqueueentryvar = 0;
01682 q->autofill = autofill_default;
01683 q->montype = montype_default;
01684 q->monfmt[0] = '\0';
01685 q->reportholdtime = 0;
01686 q->wrapuptime = 0;
01687 q->penaltymemberslimit = 0;
01688 q->joinempty = 0;
01689 q->leavewhenempty = 0;
01690 q->memberdelay = 0;
01691 q->maskmemberstatus = 0;
01692 q->eventwhencalled = 0;
01693 q->weight = 0;
01694 q->timeoutrestart = 0;
01695 q->periodicannouncefrequency = 0;
01696 q->randomperiodicannounce = 0;
01697 q->numperiodicannounce = 0;
01698 q->autopause = QUEUE_AUTOPAUSE_OFF;
01699 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01700 if (!q->members) {
01701 if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01702
01703 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01704 else
01705 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01706 }
01707 q->found = 1;
01708
01709 ast_string_field_set(q, sound_next, "queue-youarenext");
01710 ast_string_field_set(q, sound_thereare, "queue-thereare");
01711 ast_string_field_set(q, sound_calls, "queue-callswaiting");
01712 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01713 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01714 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01715 ast_string_field_set(q, sound_minutes, "queue-minutes");
01716 ast_string_field_set(q, sound_minute, "queue-minute");
01717 ast_string_field_set(q, sound_seconds, "queue-seconds");
01718 ast_string_field_set(q, sound_thanks, "queue-thankyou");
01719 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01720
01721 if (!q->sound_periodicannounce[0]) {
01722 q->sound_periodicannounce[0] = ast_str_create(32);
01723 }
01724
01725 if (q->sound_periodicannounce[0]) {
01726 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01727 }
01728
01729 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01730 if (q->sound_periodicannounce[i])
01731 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01732 }
01733
01734 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01735 ast_free(pr_iter);
01736 }
01737
01738 static void clear_queue(struct call_queue *q)
01739 {
01740 q->holdtime = 0;
01741 q->callscompleted = 0;
01742 q->callsabandoned = 0;
01743 q->callscompletedinsl = 0;
01744 q->talktime = 0;
01745
01746 if (q->members) {
01747 struct member *mem;
01748 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01749 while ((mem = ao2_iterator_next(&mem_iter))) {
01750 mem->calls = 0;
01751 mem->lastcall = 0;
01752 ao2_ref(mem, -1);
01753 }
01754 ao2_iterator_destroy(&mem_iter);
01755 }
01756 }
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767 static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
01768 {
01769 char *timestr, *maxstr, *minstr, *contentdup;
01770 struct penalty_rule *rule = NULL, *rule_iter;
01771 struct rule_list *rl_iter;
01772 int penaltychangetime, inserted = 0;
01773
01774 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01775 return -1;
01776 }
01777
01778 contentdup = ast_strdupa(content);
01779
01780 if (!(maxstr = strchr(contentdup, ','))) {
01781 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01782 ast_free(rule);
01783 return -1;
01784 }
01785
01786 *maxstr++ = '\0';
01787 timestr = contentdup;
01788
01789 if ((penaltychangetime = atoi(timestr)) < 0) {
01790 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01791 ast_free(rule);
01792 return -1;
01793 }
01794
01795 rule->time = penaltychangetime;
01796
01797 if ((minstr = strchr(maxstr,',')))
01798 *minstr++ = '\0';
01799
01800
01801
01802 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01803 rule->max_relative = 1;
01804 }
01805
01806 rule->max_value = atoi(maxstr);
01807
01808 if (!ast_strlen_zero(minstr)) {
01809 if (*minstr == '+' || *minstr == '-')
01810 rule->min_relative = 1;
01811 rule->min_value = atoi(minstr);
01812 } else
01813 rule->min_relative = 1;
01814
01815
01816 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01817 if (strcasecmp(rl_iter->name, list_name))
01818 continue;
01819
01820 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01821 if (rule->time < rule_iter->time) {
01822 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01823 inserted = 1;
01824 break;
01825 }
01826 }
01827 AST_LIST_TRAVERSE_SAFE_END;
01828
01829 if (!inserted) {
01830 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01831 inserted = 1;
01832 }
01833
01834 break;
01835 }
01836
01837 if (!inserted) {
01838 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
01839 ast_free(rule);
01840 return -1;
01841 }
01842 return 0;
01843 }
01844
01845 static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
01846 {
01847 char *value_copy = ast_strdupa(value);
01848 char *option = NULL;
01849 while ((option = strsep(&value_copy, ","))) {
01850 if (!strcasecmp(option, "paused")) {
01851 *empty |= QUEUE_EMPTY_PAUSED;
01852 } else if (!strcasecmp(option, "penalty")) {
01853 *empty |= QUEUE_EMPTY_PENALTY;
01854 } else if (!strcasecmp(option, "inuse")) {
01855 *empty |= QUEUE_EMPTY_INUSE;
01856 } else if (!strcasecmp(option, "ringing")) {
01857 *empty |= QUEUE_EMPTY_RINGING;
01858 } else if (!strcasecmp(option, "invalid")) {
01859 *empty |= QUEUE_EMPTY_INVALID;
01860 } else if (!strcasecmp(option, "wrapup")) {
01861 *empty |= QUEUE_EMPTY_WRAPUP;
01862 } else if (!strcasecmp(option, "unavailable")) {
01863 *empty |= QUEUE_EMPTY_UNAVAILABLE;
01864 } else if (!strcasecmp(option, "unknown")) {
01865 *empty |= QUEUE_EMPTY_UNKNOWN;
01866 } else if (!strcasecmp(option, "loose")) {
01867 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01868 } else if (!strcasecmp(option, "strict")) {
01869 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01870 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01871 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01872 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01873 *empty = 0;
01874 } else {
01875 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01876 }
01877 }
01878 }
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
01889 {
01890 if (!strcasecmp(param, "musicclass") ||
01891 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01892 ast_string_field_set(q, moh, val);
01893 } else if (!strcasecmp(param, "announce")) {
01894 ast_string_field_set(q, announce, val);
01895 } else if (!strcasecmp(param, "context")) {
01896 ast_string_field_set(q, context, val);
01897 } else if (!strcasecmp(param, "timeout")) {
01898 q->timeout = atoi(val);
01899 if (q->timeout < 0)
01900 q->timeout = DEFAULT_TIMEOUT;
01901 } else if (!strcasecmp(param, "ringinuse")) {
01902 q->ringinuse = ast_true(val);
01903 } else if (!strcasecmp(param, "setinterfacevar")) {
01904 q->setinterfacevar = ast_true(val);
01905 } else if (!strcasecmp(param, "setqueuevar")) {
01906 q->setqueuevar = ast_true(val);
01907 } else if (!strcasecmp(param, "setqueueentryvar")) {
01908 q->setqueueentryvar = ast_true(val);
01909 } else if (!strcasecmp(param, "monitor-format")) {
01910 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01911 } else if (!strcasecmp(param, "membermacro")) {
01912 ast_string_field_set(q, membermacro, val);
01913 } else if (!strcasecmp(param, "membergosub")) {
01914 ast_string_field_set(q, membergosub, val);
01915 } else if (!strcasecmp(param, "queue-youarenext")) {
01916 ast_string_field_set(q, sound_next, val);
01917 } else if (!strcasecmp(param, "queue-thereare")) {
01918 ast_string_field_set(q, sound_thereare, val);
01919 } else if (!strcasecmp(param, "queue-callswaiting")) {
01920 ast_string_field_set(q, sound_calls, val);
01921 } else if (!strcasecmp(param, "queue-quantity1")) {
01922 ast_string_field_set(q, queue_quantity1, val);
01923 } else if (!strcasecmp(param, "queue-quantity2")) {
01924 ast_string_field_set(q, queue_quantity2, val);
01925 } else if (!strcasecmp(param, "queue-holdtime")) {
01926 ast_string_field_set(q, sound_holdtime, val);
01927 } else if (!strcasecmp(param, "queue-minutes")) {
01928 ast_string_field_set(q, sound_minutes, val);
01929 } else if (!strcasecmp(param, "queue-minute")) {
01930 ast_string_field_set(q, sound_minute, val);
01931 } else if (!strcasecmp(param, "queue-seconds")) {
01932 ast_string_field_set(q, sound_seconds, val);
01933 } else if (!strcasecmp(param, "queue-thankyou")) {
01934 ast_string_field_set(q, sound_thanks, val);
01935 } else if (!strcasecmp(param, "queue-callerannounce")) {
01936 ast_string_field_set(q, sound_callerannounce, val);
01937 } else if (!strcasecmp(param, "queue-reporthold")) {
01938 ast_string_field_set(q, sound_reporthold, val);
01939 } else if (!strcasecmp(param, "announce-frequency")) {
01940 q->announcefrequency = atoi(val);
01941 } else if (!strcasecmp(param, "min-announce-frequency")) {
01942 q->minannouncefrequency = atoi(val);
01943 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01944 } else if (!strcasecmp(param, "announce-round-seconds")) {
01945 q->roundingseconds = atoi(val);
01946
01947 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01948 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01949 if (linenum >= 0) {
01950 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01951 "using 0 instead for queue '%s' at line %d of queues.conf\n",
01952 val, param, q->name, linenum);
01953 } else {
01954 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01955 "using 0 instead for queue '%s'\n", val, param, q->name);
01956 }
01957 q->roundingseconds=0;
01958 }
01959 } else if (!strcasecmp(param, "announce-holdtime")) {
01960 if (!strcasecmp(val, "once"))
01961 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01962 else if (ast_true(val))
01963 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01964 else
01965 q->announceholdtime = 0;
01966 } else if (!strcasecmp(param, "announce-position")) {
01967 if (!strcasecmp(val, "limit"))
01968 q->announceposition = ANNOUNCEPOSITION_LIMIT;
01969 else if (!strcasecmp(val, "more"))
01970 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01971 else if (ast_true(val))
01972 q->announceposition = ANNOUNCEPOSITION_YES;
01973 else
01974 q->announceposition = ANNOUNCEPOSITION_NO;
01975 } else if (!strcasecmp(param, "announce-position-limit")) {
01976 q->announcepositionlimit = atoi(val);
01977 } else if (!strcasecmp(param, "periodic-announce")) {
01978 if (strchr(val, ',')) {
01979 char *s, *buf = ast_strdupa(val);
01980 unsigned int i = 0;
01981
01982 while ((s = strsep(&buf, ",|"))) {
01983 if (!q->sound_periodicannounce[i])
01984 q->sound_periodicannounce[i] = ast_str_create(16);
01985 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01986 i++;
01987 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01988 break;
01989 }
01990 q->numperiodicannounce = i;
01991 } else {
01992 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01993 q->numperiodicannounce = 1;
01994 }
01995 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01996 q->periodicannouncefrequency = atoi(val);
01997 } else if (!strcasecmp(param, "relative-periodic-announce")) {
01998 q->relativeperiodicannounce = ast_true(val);
01999 } else if (!strcasecmp(param, "random-periodic-announce")) {
02000 q->randomperiodicannounce = ast_true(val);
02001 } else if (!strcasecmp(param, "retry")) {
02002 q->retry = atoi(val);
02003 if (q->retry <= 0)
02004 q->retry = DEFAULT_RETRY;
02005 } else if (!strcasecmp(param, "wrapuptime")) {
02006 q->wrapuptime = atoi(val);
02007 } else if (!strcasecmp(param, "penaltymemberslimit")) {
02008 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
02009 q->penaltymemberslimit = 0;
02010 }
02011 } else if (!strcasecmp(param, "autofill")) {
02012 q->autofill = ast_true(val);
02013 } else if (!strcasecmp(param, "monitor-type")) {
02014 if (!strcasecmp(val, "mixmonitor"))
02015 q->montype = 1;
02016 } else if (!strcasecmp(param, "autopause")) {
02017 q->autopause = autopause2int(val);
02018 } else if (!strcasecmp(param, "maxlen")) {
02019 q->maxlen = atoi(val);
02020 if (q->maxlen < 0)
02021 q->maxlen = 0;
02022 } else if (!strcasecmp(param, "servicelevel")) {
02023 q->servicelevel= atoi(val);
02024 } else if (!strcasecmp(param, "strategy")) {
02025 int strategy;
02026
02027
02028 if (failunknown) {
02029 return;
02030 }
02031 strategy = strat2int(val);
02032 if (strategy < 0) {
02033 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02034 val, q->name);
02035 q->strategy = QUEUE_STRATEGY_RINGALL;
02036 }
02037 if (strategy == q->strategy) {
02038 return;
02039 }
02040 if (strategy == QUEUE_STRATEGY_LINEAR) {
02041 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02042 return;
02043 }
02044 q->strategy = strategy;
02045 } else if (!strcasecmp(param, "joinempty")) {
02046 parse_empty_options(val, &q->joinempty, 1);
02047 } else if (!strcasecmp(param, "leavewhenempty")) {
02048 parse_empty_options(val, &q->leavewhenempty, 0);
02049 } else if (!strcasecmp(param, "eventmemberstatus")) {
02050 q->maskmemberstatus = !ast_true(val);
02051 } else if (!strcasecmp(param, "eventwhencalled")) {
02052 if (!strcasecmp(val, "vars")) {
02053 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02054 } else {
02055 q->eventwhencalled = ast_true(val) ? 1 : 0;
02056 }
02057 } else if (!strcasecmp(param, "reportholdtime")) {
02058 q->reportholdtime = ast_true(val);
02059 } else if (!strcasecmp(param, "memberdelay")) {
02060 q->memberdelay = atoi(val);
02061 } else if (!strcasecmp(param, "weight")) {
02062 q->weight = atoi(val);
02063 } else if (!strcasecmp(param, "timeoutrestart")) {
02064 q->timeoutrestart = ast_true(val);
02065 } else if (!strcasecmp(param, "defaultrule")) {
02066 ast_string_field_set(q, defaultrule, val);
02067 } else if (!strcasecmp(param, "timeoutpriority")) {
02068 if (!strcasecmp(val, "conf")) {
02069 q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02070 } else {
02071 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02072 }
02073 } else if (failunknown) {
02074 if (linenum >= 0) {
02075 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02076 q->name, param, linenum);
02077 } else {
02078 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02079 }
02080 }
02081 }
02082
02083
02084
02085
02086
02087
02088
02089 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)
02090 {
02091 struct member *m;
02092 struct ao2_iterator mem_iter;
02093 int penalty = 0;
02094 int paused = 0;
02095 int found = 0;
02096
02097 if (ast_strlen_zero(rt_uniqueid)) {
02098 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02099 return;
02100 }
02101
02102 if (penalty_str) {
02103 penalty = atoi(penalty_str);
02104 if (penalty < 0)
02105 penalty = 0;
02106 }
02107
02108 if (paused_str) {
02109 paused = atoi(paused_str);
02110 if (paused < 0)
02111 paused = 0;
02112 }
02113
02114
02115 mem_iter = ao2_iterator_init(q->members, 0);
02116 while ((m = ao2_iterator_next(&mem_iter))) {
02117 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02118 m->dead = 0;
02119 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02120 if (paused_str)
02121 m->paused = paused;
02122 if (strcasecmp(state_interface, m->state_interface)) {
02123 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02124 }
02125 m->penalty = penalty;
02126 found = 1;
02127 ao2_ref(m, -1);
02128 break;
02129 }
02130 ao2_ref(m, -1);
02131 }
02132 ao2_iterator_destroy(&mem_iter);
02133
02134
02135 if (!found) {
02136 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02137 m->dead = 0;
02138 m->realtime = 1;
02139 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02140 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02141 ao2_link(q->members, m);
02142 ao2_ref(m, -1);
02143 m = NULL;
02144 }
02145 }
02146 }
02147
02148
02149 static void free_members(struct call_queue *q, int all)
02150 {
02151
02152 struct member *cur;
02153 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02154
02155 while ((cur = ao2_iterator_next(&mem_iter))) {
02156 if (all || !cur->dynamic) {
02157 ao2_unlink(q->members, cur);
02158 }
02159 ao2_ref(cur, -1);
02160 }
02161 ao2_iterator_destroy(&mem_iter);
02162 }
02163
02164
02165 static void destroy_queue(void *obj)
02166 {
02167 struct call_queue *q = obj;
02168 int i;
02169
02170 free_members(q, 1);
02171 ast_string_field_free_memory(q);
02172 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02173 if (q->sound_periodicannounce[i])
02174 free(q->sound_periodicannounce[i]);
02175 }
02176 ao2_ref(q->members, -1);
02177 }
02178
02179 static struct call_queue *alloc_queue(const char *queuename)
02180 {
02181 struct call_queue *q;
02182
02183 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02184 if (ast_string_field_init(q, 64)) {
02185 queue_t_unref(q, "String field allocation failed");
02186 return NULL;
02187 }
02188 ast_string_field_set(q, name, queuename);
02189 }
02190 return q;
02191 }
02192
02193
02194
02195
02196
02197
02198
02199
02200
02201
02202
02203 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
02204 {
02205 struct ast_variable *v;
02206 struct call_queue *q, tmpq = {
02207 .name = queuename,
02208 };
02209 struct member *m;
02210 struct ao2_iterator mem_iter;
02211 char *interface = NULL;
02212 const char *tmp_name;
02213 char *tmp;
02214 char tmpbuf[64];
02215
02216
02217 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02218 ao2_lock(q);
02219 if (!q->realtime) {
02220 if (q->dead) {
02221 ao2_unlock(q);
02222 queue_t_unref(q, "Queue is dead; can't return it");
02223 return NULL;
02224 } else {
02225 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02226 ao2_unlock(q);
02227 return q;
02228 }
02229 }
02230 } else if (!member_config)
02231
02232 return NULL;
02233
02234
02235 if (!queue_vars) {
02236
02237 if (q) {
02238
02239
02240
02241 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02242
02243 q->dead = 1;
02244
02245 queues_t_unlink(queues, q, "Unused; removing from container");
02246 ao2_unlock(q);
02247 queue_t_unref(q, "Queue is dead; can't return it");
02248 }
02249 return NULL;
02250 }
02251
02252
02253 if (!q) {
02254 struct ast_variable *tmpvar = NULL;
02255 if (!(q = alloc_queue(queuename)))
02256 return NULL;
02257 ao2_lock(q);
02258 clear_queue(q);
02259 q->realtime = 1;
02260
02261
02262
02263 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02264 if (!strcasecmp(tmpvar->name, "strategy")) {
02265 q->strategy = strat2int(tmpvar->value);
02266 if (q->strategy < 0) {
02267 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02268 tmpvar->value, q->name);
02269 q->strategy = QUEUE_STRATEGY_RINGALL;
02270 }
02271 break;
02272 }
02273 }
02274
02275 if (!tmpvar)
02276 q->strategy = QUEUE_STRATEGY_RINGALL;
02277 queues_t_link(queues, q, "Add queue to container");
02278 }
02279 init_queue(q);
02280
02281 memset(tmpbuf, 0, sizeof(tmpbuf));
02282 for (v = queue_vars; v; v = v->next) {
02283
02284 if (strchr(v->name, '_')) {
02285 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02286 tmp_name = tmpbuf;
02287 tmp = tmpbuf;
02288 while ((tmp = strchr(tmp, '_')))
02289 *tmp++ = '-';
02290 } else
02291 tmp_name = v->name;
02292
02293
02294
02295
02296 queue_set_param(q, tmp_name, v->value, -1, 0);
02297 }
02298
02299
02300 mem_iter = ao2_iterator_init(q->members, 0);
02301 while ((m = ao2_iterator_next(&mem_iter))) {
02302 if (m->realtime)
02303 m->dead = 1;
02304 ao2_ref(m, -1);
02305 }
02306 ao2_iterator_destroy(&mem_iter);
02307
02308 while ((interface = ast_category_browse(member_config, interface))) {
02309 rt_handle_member_record(q, interface,
02310 ast_variable_retrieve(member_config, interface, "uniqueid"),
02311 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02312 ast_variable_retrieve(member_config, interface, "penalty"),
02313 ast_variable_retrieve(member_config, interface, "paused"),
02314 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02315 }
02316
02317
02318 mem_iter = ao2_iterator_init(q->members, 0);
02319 while ((m = ao2_iterator_next(&mem_iter))) {
02320 if (m->dead) {
02321 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02322 ao2_unlink(q->members, m);
02323 }
02324 ao2_ref(m, -1);
02325 }
02326 ao2_iterator_destroy(&mem_iter);
02327
02328 ao2_unlock(q);
02329
02330 return q;
02331 }
02332
02333
02334 static struct call_queue *load_realtime_queue(const char *queuename)
02335 {
02336 struct ast_variable *queue_vars;
02337 struct ast_config *member_config = NULL;
02338 struct call_queue *q = NULL, tmpq = {
02339 .name = queuename,
02340 };
02341 int prev_weight = 0;
02342
02343
02344 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02345
02346 if (!q || q->realtime) {
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02357 if (queue_vars) {
02358 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02359 if (!member_config) {
02360 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
02361 member_config = ast_config_new();
02362 }
02363 }
02364 if (q) {
02365 prev_weight = q->weight ? 1 : 0;
02366 queue_t_unref(q, "Need to find realtime queue");
02367 }
02368
02369 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02370 ast_config_destroy(member_config);
02371 ast_variables_destroy(queue_vars);
02372
02373
02374 if (q) {
02375 if (!q->weight && prev_weight) {
02376 ast_atomic_fetchadd_int(&use_weight, -1);
02377 }
02378 if (q->weight && !prev_weight) {
02379 ast_atomic_fetchadd_int(&use_weight, +1);
02380 }
02381 }
02382
02383 } else {
02384 update_realtime_members(q);
02385 }
02386 return q;
02387 }
02388
02389 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
02390 {
02391 int ret = -1;
02392
02393 if (ast_strlen_zero(mem->rt_uniqueid))
02394 return ret;
02395
02396 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02397 ret = 0;
02398
02399 return ret;
02400 }
02401
02402
02403 static void update_realtime_members(struct call_queue *q)
02404 {
02405 struct ast_config *member_config = NULL;
02406 struct member *m;
02407 char *interface = NULL;
02408 struct ao2_iterator mem_iter;
02409
02410 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02411
02412 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02413 return;
02414 }
02415
02416 ao2_lock(q);
02417
02418
02419 mem_iter = ao2_iterator_init(q->members, 0);
02420 while ((m = ao2_iterator_next(&mem_iter))) {
02421 if (m->realtime)
02422 m->dead = 1;
02423 ao2_ref(m, -1);
02424 }
02425 ao2_iterator_destroy(&mem_iter);
02426
02427 while ((interface = ast_category_browse(member_config, interface))) {
02428 rt_handle_member_record(q, interface,
02429 ast_variable_retrieve(member_config, interface, "uniqueid"),
02430 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02431 ast_variable_retrieve(member_config, interface, "penalty"),
02432 ast_variable_retrieve(member_config, interface, "paused"),
02433 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02434 }
02435
02436
02437 mem_iter = ao2_iterator_init(q->members, 0);
02438 while ((m = ao2_iterator_next(&mem_iter))) {
02439 if (m->dead) {
02440 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02441 ao2_unlink(q->members, m);
02442 }
02443 ao2_ref(m, -1);
02444 }
02445 ao2_iterator_destroy(&mem_iter);
02446 ao2_unlock(q);
02447 ast_config_destroy(member_config);
02448 }
02449
02450 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
02451 {
02452 struct call_queue *q;
02453 struct queue_ent *cur, *prev = NULL;
02454 int res = -1;
02455 int pos = 0;
02456 int inserted = 0;
02457
02458 if (!(q = load_realtime_queue(queuename)))
02459 return res;
02460
02461 ao2_lock(q);
02462
02463
02464 if (q->joinempty) {
02465 int status = 0;
02466 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
02467 *reason = QUEUE_JOINEMPTY;
02468 ao2_unlock(q);
02469 queue_t_unref(q, "Done with realtime queue");
02470 return res;
02471 }
02472 }
02473 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02474 *reason = QUEUE_FULL;
02475 else if (*reason == QUEUE_UNKNOWN) {
02476
02477
02478
02479 inserted = 0;
02480 prev = NULL;
02481 cur = q->head;
02482 while (cur) {
02483
02484
02485
02486 if ((!inserted) && (qe->prio > cur->prio)) {
02487 insert_entry(q, prev, qe, &pos);
02488 inserted = 1;
02489 }
02490
02491
02492
02493 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02494 insert_entry(q, prev, qe, &pos);
02495 inserted = 1;
02496
02497 if (position < pos) {
02498 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02499 }
02500 }
02501 cur->pos = ++pos;
02502 prev = cur;
02503 cur = cur->next;
02504 }
02505
02506 if (!inserted)
02507 insert_entry(q, prev, qe, &pos);
02508 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02509 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02510 ast_copy_string(qe->context, q->context, sizeof(qe->context));
02511 q->count++;
02512 res = 0;
02513 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02514 "Channel: %s\r\n"
02515 "CallerIDNum: %s\r\n"
02516 "CallerIDName: %s\r\n"
02517 "ConnectedLineNum: %s\r\n"
02518 "ConnectedLineName: %s\r\n"
02519 "Queue: %s\r\n"
02520 "Position: %d\r\n"
02521 "Count: %d\r\n"
02522 "Uniqueid: %s\r\n",
02523 qe->chan->name,
02524 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
02525 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02526 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
02527 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
02528 q->name, qe->pos, q->count, qe->chan->uniqueid );
02529 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02530 }
02531 ao2_unlock(q);
02532 queue_t_unref(q, "Done with realtime queue");
02533
02534 return res;
02535 }
02536
02537 static int play_file(struct ast_channel *chan, const char *filename)
02538 {
02539 int res;
02540
02541 if (ast_strlen_zero(filename)) {
02542 return 0;
02543 }
02544
02545 if (!ast_fileexists(filename, NULL, chan->language)) {
02546 return 0;
02547 }
02548
02549 ast_stopstream(chan);
02550
02551 res = ast_streamfile(chan, filename, chan->language);
02552 if (!res)
02553 res = ast_waitstream(chan, AST_DIGIT_ANY);
02554
02555 ast_stopstream(chan);
02556
02557 return res;
02558 }
02559
02560
02561
02562
02563
02564
02565 static int valid_exit(struct queue_ent *qe, char digit)
02566 {
02567 int digitlen = strlen(qe->digits);
02568
02569
02570 if (digitlen < sizeof(qe->digits) - 2) {
02571 qe->digits[digitlen] = digit;
02572 qe->digits[digitlen + 1] = '\0';
02573 } else {
02574 qe->digits[0] = '\0';
02575 return 0;
02576 }
02577
02578
02579 if (ast_strlen_zero(qe->context))
02580 return 0;
02581
02582
02583 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02584 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02585 qe->digits[0] = '\0';
02586 return 0;
02587 }
02588
02589
02590 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02591 qe->valid_digits = 1;
02592
02593 return 1;
02594 }
02595
02596 return 0;
02597 }
02598
02599 static int say_position(struct queue_ent *qe, int ringing)
02600 {
02601 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02602 int say_thanks = 1;
02603 time_t now;
02604
02605
02606 time(&now);
02607 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02608 return 0;
02609
02610
02611 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02612 return 0;
02613
02614 if (ringing) {
02615 ast_indicate(qe->chan,-1);
02616 } else {
02617 ast_moh_stop(qe->chan);
02618 }
02619
02620 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02621 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02622 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02623 qe->pos <= qe->parent->announcepositionlimit))
02624 announceposition = 1;
02625
02626
02627 if (announceposition == 1) {
02628
02629 if (qe->pos == 1) {
02630 res = play_file(qe->chan, qe->parent->sound_next);
02631 if (res)
02632 goto playout;
02633 else
02634 goto posout;
02635 } else {
02636 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02637
02638 res = play_file(qe->chan, qe->parent->queue_quantity1);
02639 if (res)
02640 goto playout;
02641 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL);
02642 if (res)
02643 goto playout;
02644 } else {
02645
02646 res = play_file(qe->chan, qe->parent->sound_thereare);
02647 if (res)
02648 goto playout;
02649 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL);
02650 if (res)
02651 goto playout;
02652 }
02653 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02654
02655 res = play_file(qe->chan, qe->parent->queue_quantity2);
02656 if (res)
02657 goto playout;
02658 } else {
02659 res = play_file(qe->chan, qe->parent->sound_calls);
02660 if (res)
02661 goto playout;
02662 }
02663 }
02664 }
02665
02666 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02667
02668
02669 if (qe->parent->roundingseconds) {
02670 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02671 avgholdsecs *= qe->parent->roundingseconds;
02672 } else {
02673 avgholdsecs = 0;
02674 }
02675
02676 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02677
02678
02679
02680 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02681 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02682 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02683 res = play_file(qe->chan, qe->parent->sound_holdtime);
02684 if (res)
02685 goto playout;
02686
02687 if (avgholdmins >= 1) {
02688 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02689 if (res)
02690 goto playout;
02691
02692 if (avgholdmins == 1) {
02693 res = play_file(qe->chan, qe->parent->sound_minute);
02694 if (res)
02695 goto playout;
02696 } else {
02697 res = play_file(qe->chan, qe->parent->sound_minutes);
02698 if (res)
02699 goto playout;
02700 }
02701 }
02702 if (avgholdsecs >= 1) {
02703 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02704 if (res)
02705 goto playout;
02706
02707 res = play_file(qe->chan, qe->parent->sound_seconds);
02708 if (res)
02709 goto playout;
02710 }
02711 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02712 say_thanks = 0;
02713 }
02714
02715 posout:
02716 if (qe->parent->announceposition) {
02717 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02718 qe->chan->name, qe->parent->name, qe->pos);
02719 }
02720 if (say_thanks) {
02721 res = play_file(qe->chan, qe->parent->sound_thanks);
02722 }
02723 playout:
02724
02725 if ((res > 0 && !valid_exit(qe, res)))
02726 res = 0;
02727
02728
02729 qe->last_pos = now;
02730 qe->last_pos_said = qe->pos;
02731
02732
02733 if (!res) {
02734 if (ringing) {
02735 ast_indicate(qe->chan, AST_CONTROL_RINGING);
02736 } else {
02737 ast_moh_start(qe->chan, qe->moh, NULL);
02738 }
02739 }
02740 return res;
02741 }
02742
02743 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
02744 {
02745 int oldvalue;
02746
02747
02748
02749
02750
02751 ao2_lock(qe->parent);
02752 oldvalue = qe->parent->holdtime;
02753 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02754 ao2_unlock(qe->parent);
02755 }
02756
02757
02758
02759
02760
02761
02762 static void leave_queue(struct queue_ent *qe)
02763 {
02764 struct call_queue *q;
02765 struct queue_ent *current, *prev = NULL;
02766 struct penalty_rule *pr_iter;
02767 int pos = 0;
02768
02769 if (!(q = qe->parent))
02770 return;
02771 queue_t_ref(q, "Copy queue pointer from queue entry");
02772 ao2_lock(q);
02773
02774 prev = NULL;
02775 for (current = q->head; current; current = current->next) {
02776 if (current == qe) {
02777 char posstr[20];
02778 q->count--;
02779
02780
02781 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02782 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02783 qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid);
02784 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02785
02786 if (prev)
02787 prev->next = current->next;
02788 else
02789 q->head = current->next;
02790
02791 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02792 ast_free(pr_iter);
02793 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02794 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02795 } else {
02796
02797 current->pos = ++pos;
02798 prev = current;
02799 }
02800 }
02801 ao2_unlock(q);
02802
02803
02804 if (q->realtime) {
02805 struct ast_variable *var;
02806 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02807 q->dead = 1;
02808 } else {
02809 ast_variables_destroy(var);
02810 }
02811 }
02812
02813 if (q->dead) {
02814
02815 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02816 }
02817
02818 queue_t_unref(q, "Expire copied reference");
02819 }
02820
02821
02822
02823
02824
02825
02826
02827
02828
02829
02830 static void callattempt_free(struct callattempt *doomed)
02831 {
02832 if (doomed->member) {
02833 ao2_ref(doomed->member, -1);
02834 }
02835 ast_party_connected_line_free(&doomed->connected);
02836 ast_free(doomed);
02837 }
02838
02839
02840 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
02841 {
02842 struct callattempt *oo;
02843
02844 while (outgoing) {
02845
02846
02847 if (outgoing->chan && (outgoing->chan != exception)) {
02848 if (exception || cancel_answered_elsewhere)
02849 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02850 ast_hangup(outgoing->chan);
02851 }
02852 oo = outgoing;
02853 outgoing = outgoing->q_next;
02854 ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02855 callattempt_free(oo);
02856 }
02857 }
02858
02859
02860
02861
02862
02863
02864
02865
02866
02867 static int num_available_members(struct call_queue *q)
02868 {
02869 struct member *mem;
02870 int avl = 0;
02871 struct ao2_iterator mem_iter;
02872
02873 mem_iter = ao2_iterator_init(q->members, 0);
02874 while ((mem = ao2_iterator_next(&mem_iter))) {
02875 switch (mem->status) {
02876 case AST_DEVICE_INUSE:
02877 if (!q->ringinuse)
02878 break;
02879
02880 case AST_DEVICE_NOT_INUSE:
02881 case AST_DEVICE_UNKNOWN:
02882 if (!mem->paused) {
02883 avl++;
02884 }
02885 break;
02886 }
02887 ao2_ref(mem, -1);
02888
02889
02890
02891
02892
02893
02894
02895
02896
02897
02898
02899 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02900 break;
02901 }
02902 }
02903 ao2_iterator_destroy(&mem_iter);
02904
02905 return avl;
02906 }
02907
02908
02909
02910 static int compare_weight(struct call_queue *rq, struct member *member)
02911 {
02912 struct call_queue *q;
02913 struct member *mem;
02914 int found = 0;
02915 struct ao2_iterator queue_iter;
02916
02917 queue_iter = ao2_iterator_init(queues, 0);
02918 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02919 if (q == rq) {
02920 queue_t_unref(q, "Done with iterator");
02921 continue;
02922 }
02923 ao2_lock(q);
02924 if (q->count && q->members) {
02925 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02926 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02927 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02928 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);
02929 found = 1;
02930 }
02931 ao2_ref(mem, -1);
02932 }
02933 }
02934 ao2_unlock(q);
02935 queue_t_unref(q, "Done with iterator");
02936 if (found) {
02937 break;
02938 }
02939 }
02940 ao2_iterator_destroy(&queue_iter);
02941 return found;
02942 }
02943
02944
02945 static void do_hang(struct callattempt *o)
02946 {
02947 o->stillgoing = 0;
02948 ast_hangup(o->chan);
02949 o->chan = NULL;
02950 }
02951
02952
02953 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
02954 {
02955 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
02956 const char *tmp;
02957
02958 if (pbx_builtin_serialize_variables(chan, &buf)) {
02959 int i, j;
02960
02961
02962 strcpy(vars, "Variable: ");
02963 tmp = ast_str_buffer(buf);
02964
02965 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02966 vars[j] = tmp[i];
02967
02968 if (tmp[i + 1] == '\0')
02969 break;
02970 if (tmp[i] == '\n') {
02971 vars[j++] = '\r';
02972 vars[j++] = '\n';
02973
02974 ast_copy_string(&(vars[j]), "Variable: ", len - j);
02975 j += 9;
02976 }
02977 }
02978 if (j > len - 3)
02979 j = len - 3;
02980 vars[j++] = '\r';
02981 vars[j++] = '\n';
02982 vars[j] = '\0';
02983 } else {
02984
02985 *vars = '\0';
02986 }
02987 return vars;
02988 }
02989
02990
02991
02992
02993
02994
02995
02996
02997
02998
02999
03000
03001
03002
03003
03004 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
03005 {
03006 int res;
03007 int status;
03008 char tech[256];
03009 char *location;
03010 const char *macrocontext, *macroexten;
03011
03012
03013 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
03014 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
03015 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
03016 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
03017 if (qe->chan->cdr)
03018 ast_cdr_busy(qe->chan->cdr);
03019 tmp->stillgoing = 0;
03020 (*busies)++;
03021 return 0;
03022 }
03023
03024 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
03025 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
03026 if (qe->chan->cdr)
03027 ast_cdr_busy(qe->chan->cdr);
03028 tmp->stillgoing = 0;
03029 return 0;
03030 }
03031
03032 if (tmp->member->paused) {
03033 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
03034 if (qe->chan->cdr)
03035 ast_cdr_busy(qe->chan->cdr);
03036 tmp->stillgoing = 0;
03037 return 0;
03038 }
03039 if (use_weight && compare_weight(qe->parent,tmp->member)) {
03040 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
03041 if (qe->chan->cdr)
03042 ast_cdr_busy(qe->chan->cdr);
03043 tmp->stillgoing = 0;
03044 (*busies)++;
03045 return 0;
03046 }
03047
03048 ast_copy_string(tech, tmp->interface, sizeof(tech));
03049 if ((location = strchr(tech, '/')))
03050 *location++ = '\0';
03051 else
03052 location = "";
03053
03054
03055 tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03056 if (!tmp->chan) {
03057 if (qe->chan->cdr)
03058 ast_cdr_busy(qe->chan->cdr);
03059 tmp->stillgoing = 0;
03060
03061 ao2_lock(qe->parent);
03062 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03063 qe->parent->rrpos++;
03064 qe->linpos++;
03065 ao2_unlock(qe->parent);
03066
03067 (*busies)++;
03068 return 0;
03069 }
03070
03071 ast_channel_lock_both(tmp->chan, qe->chan);
03072
03073 if (qe->cancel_answered_elsewhere) {
03074 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03075 }
03076 tmp->chan->appl = "AppQueue";
03077 tmp->chan->data = "(Outgoing Line)";
03078 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03079
03080
03081 if (!tmp->chan->caller.id.number.valid) {
03082 if (qe->chan->connected.id.number.valid) {
03083 struct ast_party_caller caller;
03084
03085 ast_party_caller_set_init(&caller, &tmp->chan->caller);
03086 caller.id = qe->chan->connected.id;
03087 caller.ani = qe->chan->connected.ani;
03088 ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03089 } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03090 ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03091 } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03092 ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL);
03093 }
03094 tmp->dial_callerid_absent = 1;
03095 }
03096
03097 ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03098
03099 tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03100
03101 ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03102
03103
03104 ast_channel_inherit_variables(qe->chan, tmp->chan);
03105 ast_channel_datastore_inherit(qe->chan, tmp->chan);
03106
03107
03108 tmp->chan->adsicpe = qe->chan->adsicpe;
03109
03110
03111 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03112 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03113 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03114 if (!ast_strlen_zero(macroexten))
03115 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03116 else
03117 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03118 if (ast_cdr_isset_unanswered()) {
03119
03120
03121 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03122 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03123 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03124 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03125 strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03126 strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03127 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03128 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03129 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03130 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03131 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03132 }
03133
03134 ast_channel_unlock(tmp->chan);
03135 ast_channel_unlock(qe->chan);
03136
03137
03138 if ((res = ast_call(tmp->chan, location, 0))) {
03139
03140 ast_debug(1, "ast call on peer returned %d\n", res);
03141 ast_verb(3, "Couldn't call %s\n", tmp->interface);
03142 do_hang(tmp);
03143 (*busies)++;
03144 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03145 return 0;
03146 } else if (qe->parent->eventwhencalled) {
03147 char vars[2048];
03148
03149 ast_channel_lock_both(tmp->chan, qe->chan);
03150
03151 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03152 "Queue: %s\r\n"
03153 "AgentCalled: %s\r\n"
03154 "AgentName: %s\r\n"
03155 "ChannelCalling: %s\r\n"
03156 "DestinationChannel: %s\r\n"
03157 "CallerIDNum: %s\r\n"
03158 "CallerIDName: %s\r\n"
03159 "ConnectedLineNum: %s\r\n"
03160 "ConnectedLineName: %s\r\n"
03161 "Context: %s\r\n"
03162 "Extension: %s\r\n"
03163 "Priority: %d\r\n"
03164 "Uniqueid: %s\r\n"
03165 "%s",
03166 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03167 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03168 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03169 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03170 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03171 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03172 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03173
03174 ast_channel_unlock(tmp->chan);
03175 ast_channel_unlock(qe->chan);
03176
03177 ast_verb(3, "Called %s\n", tmp->interface);
03178 }
03179
03180 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03181 return 1;
03182 }
03183
03184
03185 static struct callattempt *find_best(struct callattempt *outgoing)
03186 {
03187 struct callattempt *best = NULL, *cur;
03188
03189 for (cur = outgoing; cur; cur = cur->q_next) {
03190 if (cur->stillgoing &&
03191 !cur->chan &&
03192 (!best || cur->metric < best->metric)) {
03193 best = cur;
03194 }
03195 }
03196
03197 return best;
03198 }
03199
03200
03201
03202
03203
03204
03205
03206
03207
03208
03209
03210 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
03211 {
03212 int ret = 0;
03213
03214 while (ret == 0) {
03215 struct callattempt *best = find_best(outgoing);
03216 if (!best) {
03217 ast_debug(1, "Nobody left to try ringing in queue\n");
03218 break;
03219 }
03220 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03221 struct callattempt *cur;
03222
03223 for (cur = outgoing; cur; cur = cur->q_next) {
03224 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03225 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03226 ret |= ring_entry(qe, cur, busies);
03227 }
03228 }
03229 } else {
03230
03231 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03232 ret = ring_entry(qe, best, busies);
03233 }
03234
03235
03236 if (qe->expire && (time(NULL) >= qe->expire)) {
03237 ast_debug(1, "Queue timed out while ringing members.\n");
03238 ret = 0;
03239 break;
03240 }
03241 }
03242
03243 return ret;
03244 }
03245
03246
03247 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
03248 {
03249 struct callattempt *best = find_best(outgoing);
03250
03251 if (best) {
03252
03253 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03254 qe->parent->rrpos = best->metric % 1000;
03255 } else {
03256
03257 if (qe->parent->wrapped) {
03258
03259 qe->parent->rrpos = 0;
03260 } else {
03261
03262 qe->parent->rrpos++;
03263 }
03264 }
03265 qe->parent->wrapped = 0;
03266
03267 return 0;
03268 }
03269
03270
03271 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
03272 {
03273 struct callattempt *best = find_best(outgoing);
03274
03275 if (best) {
03276
03277 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03278 qe->linpos = best->metric % 1000;
03279 } else {
03280
03281 if (qe->linwrapped) {
03282
03283 qe->linpos = 0;
03284 } else {
03285
03286 qe->linpos++;
03287 }
03288 }
03289 qe->linwrapped = 0;
03290
03291 return 0;
03292 }
03293
03294
03295 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
03296 {
03297 int res = 0;
03298 time_t now;
03299
03300
03301 time(&now);
03302
03303
03304 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03305 return 0;
03306
03307
03308 if (ringing)
03309 ast_indicate(qe->chan,-1);
03310 else
03311 ast_moh_stop(qe->chan);
03312
03313 ast_verb(3, "Playing periodic announcement\n");
03314
03315 if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03316 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03317 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
03318 ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03319 qe->last_periodic_announce_sound = 0;
03320 }
03321
03322
03323 res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03324
03325 if (res > 0 && !valid_exit(qe, res))
03326 res = 0;
03327
03328
03329 if (!res) {
03330 if (ringing)
03331 ast_indicate(qe->chan, AST_CONTROL_RINGING);
03332 else
03333 ast_moh_start(qe->chan, qe->moh, NULL);
03334 }
03335
03336
03337 if (qe->parent->relativeperiodicannounce)
03338 time(&qe->last_periodic_announce_time);
03339 else
03340 qe->last_periodic_announce_time = now;
03341
03342
03343 if (!qe->parent->randomperiodicannounce) {
03344 qe->last_periodic_announce_sound++;
03345 }
03346
03347 return res;
03348 }
03349
03350
03351 static void record_abandoned(struct queue_ent *qe)
03352 {
03353 set_queue_variables(qe->parent, qe->chan);
03354 ao2_lock(qe->parent);
03355 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03356 "Queue: %s\r\n"
03357 "Uniqueid: %s\r\n"
03358 "Position: %d\r\n"
03359 "OriginalPosition: %d\r\n"
03360 "HoldTime: %d\r\n",
03361 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03362
03363 qe->parent->callsabandoned++;
03364 ao2_unlock(qe->parent);
03365 }
03366
03367
03368 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
03369 {
03370 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03371
03372
03373 if (qe->ring_when_ringing) {
03374 ast_indicate(qe->chan, -1);
03375 ast_moh_start(qe->chan, qe->moh, NULL);
03376 }
03377
03378 if (qe->parent->eventwhencalled) {
03379 char vars[2048];
03380
03381 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03382 "Queue: %s\r\n"
03383 "Uniqueid: %s\r\n"
03384 "Channel: %s\r\n"
03385 "Member: %s\r\n"
03386 "MemberName: %s\r\n"
03387 "Ringtime: %d\r\n"
03388 "%s",
03389 qe->parent->name,
03390 qe->chan->uniqueid,
03391 qe->chan->name,
03392 interface,
03393 membername,
03394 rnatime,
03395 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03396 }
03397 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03398 if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03399 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03400 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03401 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03402 interface, qe->parent->name);
03403 } else {
03404 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03405 }
03406 } else {
03407
03408
03409 if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03410 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03411 interface, qe->parent->name);
03412 } else {
03413 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03414 }
03415 }
03416 }
03417 return;
03418 }
03419
03420 #define AST_MAX_WATCHERS 256
03421
03422
03423
03424
03425
03426
03427
03428
03429
03430
03431
03432
03433
03434 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
03435 {
03436 const char *queue = qe->parent->name;
03437 struct callattempt *o, *start = NULL, *prev = NULL;
03438 int status;
03439 int numbusies = prebusies;
03440 int numnochan = 0;
03441 int stillgoing = 0;
03442 int orig = *to;
03443 struct ast_frame *f;
03444 struct callattempt *peer = NULL;
03445 struct ast_channel *winner;
03446 struct ast_channel *in = qe->chan;
03447 char on[80] = "";
03448 char membername[80] = "";
03449 long starttime = 0;
03450 long endtime = 0;
03451 #ifdef HAVE_EPOLL
03452 struct callattempt *epollo;
03453 #endif
03454 struct ast_party_connected_line connected_caller;
03455 char *inchan_name;
03456
03457 ast_party_connected_line_init(&connected_caller);
03458
03459 ast_channel_lock(qe->chan);
03460 inchan_name = ast_strdupa(qe->chan->name);
03461 ast_channel_unlock(qe->chan);
03462
03463 starttime = (long) time(NULL);
03464 #ifdef HAVE_EPOLL
03465 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03466 if (epollo->chan)
03467 ast_poll_channel_add(in, epollo->chan);
03468 }
03469 #endif
03470
03471 while (*to && !peer) {
03472 int numlines, retry, pos = 1;
03473 struct ast_channel *watchers[AST_MAX_WATCHERS];
03474 watchers[0] = in;
03475 start = NULL;
03476
03477 for (retry = 0; retry < 2; retry++) {
03478 numlines = 0;
03479 for (o = outgoing; o; o = o->q_next) {
03480 if (o->stillgoing) {
03481 stillgoing = 1;
03482 if (o->chan) {
03483 if (pos < AST_MAX_WATCHERS) {
03484 watchers[pos++] = o->chan;
03485 }
03486 if (!start)
03487 start = o;
03488 else
03489 prev->call_next = o;
03490 prev = o;
03491 }
03492 }
03493 numlines++;
03494 }
03495 if (pos > 1 || !stillgoing ||
03496 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
03497 break;
03498
03499
03500 ring_one(qe, outgoing, &numbusies);
03501
03502 }
03503 if (pos == 1 ) {
03504 if (numlines == (numbusies + numnochan)) {
03505 ast_debug(1, "Everyone is busy at this time\n");
03506 } else {
03507 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03508 }
03509 *to = 0;
03510 return NULL;
03511 }
03512
03513
03514 winner = ast_waitfor_n(watchers, pos, to);
03515
03516
03517 for (o = start; o; o = o->call_next) {
03518
03519
03520
03521 char ochan_name[AST_CHANNEL_NAME];
03522
03523 if (o->chan) {
03524 ast_channel_lock(o->chan);
03525 ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03526 ast_channel_unlock(o->chan);
03527 }
03528 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
03529 if (!peer) {
03530 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03531 if (!o->block_connected_update) {
03532 if (o->pending_connected_update) {
03533 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03534 ast_channel_update_connected_line(in, &o->connected, NULL);
03535 }
03536 } else if (!o->dial_callerid_absent) {
03537 ast_channel_lock(o->chan);
03538 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03539 ast_channel_unlock(o->chan);
03540 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03541 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03542 ast_channel_update_connected_line(in, &connected_caller, NULL);
03543 }
03544 ast_party_connected_line_free(&connected_caller);
03545 }
03546 }
03547 if (o->aoc_s_rate_list) {
03548 size_t encoded_size;
03549 struct ast_aoc_encoded *encoded;
03550 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03551 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03552 ast_aoc_destroy_encoded(encoded);
03553 }
03554 }
03555 peer = o;
03556 }
03557 } else if (o->chan && (o->chan == winner)) {
03558
03559 ast_copy_string(on, o->member->interface, sizeof(on));
03560 ast_copy_string(membername, o->member->membername, sizeof(membername));
03561
03562
03563 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03564 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03565 numnochan++;
03566 do_hang(o);
03567 winner = NULL;
03568 continue;
03569 } else if (!ast_strlen_zero(o->chan->call_forward)) {
03570 struct ast_channel *original = o->chan;
03571 char tmpchan[256];
03572 char *stuff;
03573 char *tech;
03574
03575 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03576 if ((stuff = strchr(tmpchan, '/'))) {
03577 *stuff++ = '\0';
03578 tech = tmpchan;
03579 } else {
03580 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03581 stuff = tmpchan;
03582 tech = "Local";
03583 }
03584 if (!strcasecmp(tech, "Local")) {
03585
03586
03587
03588
03589
03590 o->block_connected_update = 0;
03591 }
03592
03593 ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03594
03595 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03596
03597 o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03598 if (!o->chan) {
03599 ast_log(LOG_NOTICE,
03600 "Forwarding failed to create channel to dial '%s/%s'\n",
03601 tech, stuff);
03602 o->stillgoing = 0;
03603 numnochan++;
03604 } else {
03605 ast_channel_lock_both(o->chan, original);
03606 ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting);
03607 ast_channel_unlock(o->chan);
03608 ast_channel_unlock(original);
03609
03610 ast_channel_lock_both(o->chan, in);
03611 ast_channel_inherit_variables(in, o->chan);
03612 ast_channel_datastore_inherit(in, o->chan);
03613
03614 if (o->pending_connected_update) {
03615
03616
03617
03618
03619
03620
03621 o->pending_connected_update = 0;
03622 ast_party_connected_line_copy(&o->connected, &in->connected);
03623 }
03624
03625 ast_string_field_set(o->chan, accountcode, in->accountcode);
03626
03627 if (!o->chan->redirecting.from.number.valid
03628 || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03629
03630
03631
03632
03633 ast_party_number_free(&o->chan->redirecting.from.number);
03634 ast_party_number_init(&o->chan->redirecting.from.number);
03635 o->chan->redirecting.from.number.valid = 1;
03636 o->chan->redirecting.from.number.str =
03637 ast_strdup(S_OR(in->macroexten, in->exten));
03638 }
03639
03640 o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03641
03642 o->dial_callerid_absent = !o->chan->caller.id.number.valid
03643 || ast_strlen_zero(o->chan->caller.id.number.str);
03644 ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller);
03645
03646 ast_channel_unlock(in);
03647 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
03648 && !o->block_connected_update) {
03649 struct ast_party_redirecting redirecting;
03650
03651
03652
03653
03654
03655
03656
03657
03658
03659
03660
03661 ast_party_redirecting_init(&redirecting);
03662 ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03663 ast_channel_unlock(o->chan);
03664 if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
03665 ast_channel_update_redirecting(in, &redirecting, NULL);
03666 }
03667 ast_party_redirecting_free(&redirecting);
03668 } else {
03669 ast_channel_unlock(o->chan);
03670 }
03671
03672 if (ast_call(o->chan, stuff, 0)) {
03673 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03674 tech, stuff);
03675 do_hang(o);
03676 numnochan++;
03677 }
03678 }
03679
03680 ast_hangup(winner);
03681 continue;
03682 }
03683 f = ast_read(winner);
03684 if (f) {
03685 if (f->frametype == AST_FRAME_CONTROL) {
03686 switch (f->subclass.integer) {
03687 case AST_CONTROL_ANSWER:
03688
03689 if (!peer) {
03690 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03691 if (!o->block_connected_update) {
03692 if (o->pending_connected_update) {
03693 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03694 ast_channel_update_connected_line(in, &o->connected, NULL);
03695 }
03696 } else if (!o->dial_callerid_absent) {
03697 ast_channel_lock(o->chan);
03698 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03699 ast_channel_unlock(o->chan);
03700 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03701 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03702 ast_channel_update_connected_line(in, &connected_caller, NULL);
03703 }
03704 ast_party_connected_line_free(&connected_caller);
03705 }
03706 }
03707 if (o->aoc_s_rate_list) {
03708 size_t encoded_size;
03709 struct ast_aoc_encoded *encoded;
03710 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03711 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03712 ast_aoc_destroy_encoded(encoded);
03713 }
03714 }
03715 peer = o;
03716 }
03717 break;
03718 case AST_CONTROL_BUSY:
03719 ast_verb(3, "%s is busy\n", ochan_name);
03720 if (in->cdr)
03721 ast_cdr_busy(in->cdr);
03722 do_hang(o);
03723 endtime = (long) time(NULL);
03724 endtime -= starttime;
03725 rna(endtime * 1000, qe, on, membername, 0);
03726 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03727 if (qe->parent->timeoutrestart)
03728 *to = orig;
03729
03730 if (*to > 500) {
03731 ring_one(qe, outgoing, &numbusies);
03732 starttime = (long) time(NULL);
03733 }
03734 }
03735 numbusies++;
03736 break;
03737 case AST_CONTROL_CONGESTION:
03738 ast_verb(3, "%s is circuit-busy\n", ochan_name);
03739 if (in->cdr)
03740 ast_cdr_busy(in->cdr);
03741 endtime = (long) time(NULL);
03742 endtime -= starttime;
03743 rna(endtime * 1000, qe, on, membername, 0);
03744 do_hang(o);
03745 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03746 if (qe->parent->timeoutrestart)
03747 *to = orig;
03748 if (*to > 500) {
03749 ring_one(qe, outgoing, &numbusies);
03750 starttime = (long) time(NULL);
03751 }
03752 }
03753 numbusies++;
03754 break;
03755 case AST_CONTROL_RINGING:
03756 ast_verb(3, "%s is ringing\n", ochan_name);
03757
03758
03759 if (qe->ring_when_ringing) {
03760 ast_moh_stop(qe->chan);
03761 ast_indicate(qe->chan, AST_CONTROL_RINGING);
03762 }
03763 break;
03764 case AST_CONTROL_OFFHOOK:
03765
03766 break;
03767 case AST_CONTROL_CONNECTED_LINE:
03768 if (o->block_connected_update) {
03769 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03770 break;
03771 }
03772 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03773 struct ast_party_connected_line connected;
03774
03775 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03776 ast_party_connected_line_set_init(&connected, &o->connected);
03777 ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03778 ast_party_connected_line_set(&o->connected, &connected, NULL);
03779 ast_party_connected_line_free(&connected);
03780 o->pending_connected_update = 1;
03781 break;
03782 }
03783
03784
03785
03786
03787
03788 o->dial_callerid_absent = 1;
03789
03790 if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03791 ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03792 }
03793 break;
03794 case AST_CONTROL_AOC:
03795 {
03796 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03797 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03798 ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03799 o->aoc_s_rate_list = decoded;
03800 } else {
03801 ast_aoc_destroy_decoded(decoded);
03802 }
03803 }
03804 break;
03805 case AST_CONTROL_REDIRECTING:
03806 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03807
03808
03809
03810
03811 break;
03812 }
03813 if (o->block_connected_update) {
03814 ast_verb(3, "Redirecting update to %s prevented\n",
03815 inchan_name);
03816 break;
03817 }
03818 ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
03819 ochan_name, inchan_name);
03820 if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03821 ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03822 }
03823 break;
03824 default:
03825 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03826 break;
03827 }
03828 }
03829 ast_frfree(f);
03830 } else {
03831 endtime = (long) time(NULL) - starttime;
03832 rna(endtime * 1000, qe, on, membername, 1);
03833 do_hang(o);
03834 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03835 if (qe->parent->timeoutrestart)
03836 *to = orig;
03837 if (*to > 500) {
03838 ring_one(qe, outgoing, &numbusies);
03839 starttime = (long) time(NULL);
03840 }
03841 }
03842 }
03843 }
03844 }
03845
03846
03847 if (winner == in) {
03848 f = ast_read(in);
03849 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03850
03851 *to = -1;
03852 if (f) {
03853 if (f->data.uint32) {
03854 in->hangupcause = f->data.uint32;
03855 }
03856 ast_frfree(f);
03857 }
03858 return NULL;
03859 }
03860
03861
03862
03863
03864
03865
03866
03867
03868 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03869 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03870 *to = 0;
03871 ast_frfree(f);
03872 return NULL;
03873 }
03874 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03875 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03876 *to = 0;
03877 *digit = f->subclass.integer;
03878 ast_frfree(f);
03879 return NULL;
03880 }
03881 ast_frfree(f);
03882 }
03883 if (!*to) {
03884 for (o = start; o; o = o->call_next)
03885 rna(orig, qe, o->interface, o->member->membername, 1);
03886 }
03887 }
03888
03889 #ifdef HAVE_EPOLL
03890 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03891 if (epollo->chan)
03892 ast_poll_channel_del(in, epollo->chan);
03893 }
03894 #endif
03895
03896 return peer;
03897 }
03898
03899
03900
03901
03902
03903
03904
03905
03906
03907
03908
03909
03910 static int is_our_turn(struct queue_ent *qe)
03911 {
03912 struct queue_ent *ch;
03913 int res;
03914 int avl;
03915 int idx = 0;
03916
03917 ao2_lock(qe->parent);
03918
03919 avl = num_available_members(qe->parent);
03920
03921 ch = qe->parent->head;
03922
03923 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03924
03925 while ((idx < avl) && (ch) && (ch != qe)) {
03926 if (!ch->pending)
03927 idx++;
03928 ch = ch->next;
03929 }
03930
03931 ao2_unlock(qe->parent);
03932
03933
03934
03935
03936 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03937 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03938 res = 1;
03939 } else {
03940 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03941 res = 0;
03942 }
03943
03944 return res;
03945 }
03946
03947
03948
03949
03950
03951
03952
03953 static void update_qe_rule(struct queue_ent *qe)
03954 {
03955 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03956 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03957 char max_penalty_str[20], min_penalty_str[20];
03958
03959 if (max_penalty < 0)
03960 max_penalty = 0;
03961 if (min_penalty < 0)
03962 min_penalty = 0;
03963 if (min_penalty > max_penalty)
03964 min_penalty = max_penalty;
03965 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03966 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03967 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03968 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03969 qe->max_penalty = max_penalty;
03970 qe->min_penalty = min_penalty;
03971 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);
03972 qe->pr = AST_LIST_NEXT(qe->pr, list);
03973 }
03974
03975
03976
03977
03978
03979
03980
03981
03982
03983
03984
03985 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
03986 {
03987 int res = 0;
03988
03989
03990 for (;;) {
03991
03992 if (is_our_turn(qe))
03993 break;
03994
03995
03996 if (qe->expire && (time(NULL) >= qe->expire)) {
03997 *reason = QUEUE_TIMEOUT;
03998 break;
03999 }
04000
04001 if (qe->parent->leavewhenempty) {
04002 int status = 0;
04003
04004 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
04005 *reason = QUEUE_LEAVEEMPTY;
04006 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04007 leave_queue(qe);
04008 break;
04009 }
04010 }
04011
04012
04013 if (qe->parent->announcefrequency &&
04014 (res = say_position(qe,ringing)))
04015 break;
04016
04017
04018 if (qe->expire && (time(NULL) >= qe->expire)) {
04019 *reason = QUEUE_TIMEOUT;
04020 break;
04021 }
04022
04023
04024 if (qe->parent->periodicannouncefrequency &&
04025 (res = say_periodic_announcement(qe,ringing)))
04026 break;
04027
04028
04029 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04030 update_qe_rule(qe);
04031 }
04032
04033
04034 if (qe->expire && (time(NULL) >= qe->expire)) {
04035 *reason = QUEUE_TIMEOUT;
04036 break;
04037 }
04038
04039
04040 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04041 if (res > 0 && !valid_exit(qe, res))
04042 res = 0;
04043 else
04044 break;
04045 }
04046
04047
04048 if (qe->expire && (time(NULL) >= qe->expire)) {
04049 *reason = QUEUE_TIMEOUT;
04050 break;
04051 }
04052 }
04053
04054 return res;
04055 }
04056
04057
04058
04059
04060
04061 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
04062 {
04063 int oldtalktime;
04064
04065 struct member *mem;
04066 struct call_queue *qtmp;
04067 struct ao2_iterator queue_iter;
04068
04069 if (shared_lastcall) {
04070 queue_iter = ao2_iterator_init(queues, 0);
04071 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04072 ao2_lock(qtmp);
04073 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04074 time(&mem->lastcall);
04075 mem->calls++;
04076 mem->lastqueue = q;
04077 ao2_ref(mem, -1);
04078 }
04079 ao2_unlock(qtmp);
04080 queue_t_unref(qtmp, "Done with iterator");
04081 }
04082 ao2_iterator_destroy(&queue_iter);
04083 } else {
04084 ao2_lock(q);
04085 time(&member->lastcall);
04086 member->calls++;
04087 member->lastqueue = q;
04088 ao2_unlock(q);
04089 }
04090 ao2_lock(q);
04091 q->callscompleted++;
04092 if (callcompletedinsl)
04093 q->callscompletedinsl++;
04094
04095 oldtalktime = q->talktime;
04096 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04097 ao2_unlock(q);
04098 return 0;
04099 }
04100
04101
04102
04103
04104
04105
04106
04107
04108
04109 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
04110 {
04111
04112 int membercount = ao2_container_count(q->members);
04113 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04114
04115 if (usepenalty) {
04116 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04117 (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04118 return -1;
04119 }
04120 } else {
04121 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04122 membercount, q->penaltymemberslimit);
04123 }
04124
04125 switch (q->strategy) {
04126 case QUEUE_STRATEGY_RINGALL:
04127
04128 tmp->metric = mem->penalty * 1000000 * usepenalty;
04129 break;
04130 case QUEUE_STRATEGY_LINEAR:
04131 if (pos < qe->linpos) {
04132 tmp->metric = 1000 + pos;
04133 } else {
04134 if (pos > qe->linpos)
04135
04136 qe->linwrapped = 1;
04137 tmp->metric = pos;
04138 }
04139 tmp->metric += mem->penalty * 1000000 * usepenalty;
04140 break;
04141 case QUEUE_STRATEGY_RRORDERED:
04142 case QUEUE_STRATEGY_RRMEMORY:
04143 if (pos < q->rrpos) {
04144 tmp->metric = 1000 + pos;
04145 } else {
04146 if (pos > q->rrpos)
04147
04148 q->wrapped = 1;
04149 tmp->metric = pos;
04150 }
04151 tmp->metric += mem->penalty * 1000000 * usepenalty;
04152 break;
04153 case QUEUE_STRATEGY_RANDOM:
04154 tmp->metric = ast_random() % 1000;
04155 tmp->metric += mem->penalty * 1000000 * usepenalty;
04156 break;
04157 case QUEUE_STRATEGY_WRANDOM:
04158 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04159 break;
04160 case QUEUE_STRATEGY_FEWESTCALLS:
04161 tmp->metric = mem->calls;
04162 tmp->metric += mem->penalty * 1000000 * usepenalty;
04163 break;
04164 case QUEUE_STRATEGY_LEASTRECENT:
04165 if (!mem->lastcall)
04166 tmp->metric = 0;
04167 else
04168 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04169 tmp->metric += mem->penalty * 1000000 * usepenalty;
04170 break;
04171 default:
04172 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04173 break;
04174 }
04175 return 0;
04176 }
04177
04178 enum agent_complete_reason {
04179 CALLER,
04180 AGENT,
04181 TRANSFER
04182 };
04183
04184
04185 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
04186 const struct ast_channel *peer, const struct member *member, time_t callstart,
04187 char *vars, size_t vars_len, enum agent_complete_reason rsn)
04188 {
04189 const char *reason = NULL;
04190
04191 if (!qe->parent->eventwhencalled)
04192 return;
04193
04194 switch (rsn) {
04195 case CALLER:
04196 reason = "caller";
04197 break;
04198 case AGENT:
04199 reason = "agent";
04200 break;
04201 case TRANSFER:
04202 reason = "transfer";
04203 break;
04204 }
04205
04206 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04207 "Queue: %s\r\n"
04208 "Uniqueid: %s\r\n"
04209 "Channel: %s\r\n"
04210 "Member: %s\r\n"
04211 "MemberName: %s\r\n"
04212 "HoldTime: %ld\r\n"
04213 "TalkTime: %ld\r\n"
04214 "Reason: %s\r\n"
04215 "%s",
04216 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04217 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04218 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04219 }
04220
04221 struct queue_transfer_ds {
04222 struct queue_ent *qe;
04223 struct member *member;
04224 time_t starttime;
04225 int callcompletedinsl;
04226 };
04227
04228 static void queue_transfer_destroy(void *data)
04229 {
04230 struct queue_transfer_ds *qtds = data;
04231 ast_free(qtds);
04232 }
04233
04234
04235
04236 static const struct ast_datastore_info queue_transfer_info = {
04237 .type = "queue_transfer",
04238 .chan_fixup = queue_transfer_fixup,
04239 .destroy = queue_transfer_destroy,
04240 };
04241
04242
04243
04244
04245
04246
04247
04248
04249
04250
04251 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
04252 {
04253 struct queue_transfer_ds *qtds = data;
04254 struct queue_ent *qe = qtds->qe;
04255 struct member *member = qtds->member;
04256 time_t callstart = qtds->starttime;
04257 int callcompletedinsl = qtds->callcompletedinsl;
04258 struct ast_datastore *datastore;
04259
04260 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04261 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04262 (long) (time(NULL) - callstart), qe->opos);
04263
04264 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04265
04266
04267 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04268 ast_channel_datastore_remove(old_chan, datastore);
04269 } else {
04270 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04271 }
04272 }
04273
04274
04275
04276
04277
04278
04279
04280
04281
04282 static int attended_transfer_occurred(struct ast_channel *chan)
04283 {
04284 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04285 }
04286
04287
04288
04289 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
04290 {
04291 struct ast_datastore *ds;
04292 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04293
04294 if (!qtds) {
04295 ast_log(LOG_WARNING, "Memory allocation error!\n");
04296 return NULL;
04297 }
04298
04299 ast_channel_lock(qe->chan);
04300 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04301 ast_channel_unlock(qe->chan);
04302 ast_free(qtds);
04303 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04304 return NULL;
04305 }
04306
04307 qtds->qe = qe;
04308
04309 qtds->member = member;
04310 qtds->starttime = starttime;
04311 qtds->callcompletedinsl = callcompletedinsl;
04312 ds->data = qtds;
04313 ast_channel_datastore_add(qe->chan, ds);
04314 ast_channel_unlock(qe->chan);
04315 return ds;
04316 }
04317
04318 struct queue_end_bridge {
04319 struct call_queue *q;
04320 struct ast_channel *chan;
04321 };
04322
04323 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
04324 {
04325 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04326 ao2_ref(qeb, +1);
04327 qeb->chan = originator;
04328 }
04329
04330 static void end_bridge_callback(void *data)
04331 {
04332 struct queue_end_bridge *qeb = data;
04333 struct call_queue *q = qeb->q;
04334 struct ast_channel *chan = qeb->chan;
04335
04336 if (ao2_ref(qeb, -1) == 1) {
04337 set_queue_variables(q, chan);
04338
04339 queue_t_unref(q, "Expire bridge_config reference");
04340 }
04341 }
04342
04343
04344
04345
04346
04347
04348
04349
04350
04351
04352
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366
04367
04368
04369
04370 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)
04371 {
04372 struct member *cur;
04373 struct callattempt *outgoing = NULL;
04374 int to, orig;
04375 char oldexten[AST_MAX_EXTENSION]="";
04376 char oldcontext[AST_MAX_CONTEXT]="";
04377 char queuename[256]="";
04378 char interfacevar[256]="";
04379 struct ast_channel *peer;
04380 struct ast_channel *which;
04381 struct callattempt *lpeer;
04382 struct member *member;
04383 struct ast_app *application;
04384 int res = 0, bridge = 0;
04385 int numbusies = 0;
04386 int x=0;
04387 char *announce = NULL;
04388 char digit = 0;
04389 time_t callstart;
04390 time_t now = time(NULL);
04391 struct ast_bridge_config bridge_config;
04392 char nondataquality = 1;
04393 char *agiexec = NULL;
04394 char *macroexec = NULL;
04395 char *gosubexec = NULL;
04396 const char *monitorfilename;
04397 const char *monitor_exec;
04398 const char *monitor_options;
04399 char tmpid[256], tmpid2[256];
04400 char meid[1024], meid2[1024];
04401 char mixmonargs[1512];
04402 struct ast_app *mixmonapp = NULL;
04403 char *p;
04404 char vars[2048];
04405 int forwardsallowed = 1;
04406 int block_connected_line = 0;
04407 int callcompletedinsl;
04408 struct ao2_iterator memi;
04409 struct ast_datastore *datastore, *transfer_ds;
04410 struct queue_end_bridge *queue_end_bridge = NULL;
04411
04412 ast_channel_lock(qe->chan);
04413 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04414 ast_channel_unlock(qe->chan);
04415
04416 memset(&bridge_config, 0, sizeof(bridge_config));
04417 tmpid[0] = 0;
04418 meid[0] = 0;
04419 time(&now);
04420
04421
04422
04423
04424
04425 if (qe->expire && now >= qe->expire) {
04426 res = 0;
04427 goto out;
04428 }
04429
04430 for (; options && *options; options++)
04431 switch (*options) {
04432 case 't':
04433 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04434 break;
04435 case 'T':
04436 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04437 break;
04438 case 'w':
04439 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04440 break;
04441 case 'W':
04442 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04443 break;
04444 case 'c':
04445 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04446 break;
04447 case 'd':
04448 nondataquality = 0;
04449 break;
04450 case 'h':
04451 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04452 break;
04453 case 'H':
04454 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04455 break;
04456 case 'k':
04457 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04458 break;
04459 case 'K':
04460 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04461 break;
04462 case 'n':
04463 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04464 (*tries)++;
04465 else
04466 *tries = ao2_container_count(qe->parent->members);
04467 *noption = 1;
04468 break;
04469 case 'i':
04470 forwardsallowed = 0;
04471 break;
04472 case 'I':
04473 block_connected_line = 1;
04474 break;
04475 case 'x':
04476 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04477 break;
04478 case 'X':
04479 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04480 break;
04481 case 'C':
04482 qe->cancel_answered_elsewhere = 1;
04483 break;
04484 }
04485
04486
04487
04488
04489 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04490 qe->cancel_answered_elsewhere = 1;
04491 }
04492
04493 ao2_lock(qe->parent);
04494 ast_debug(1, "%s is trying to call a queue member.\n",
04495 qe->chan->name);
04496 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04497 if (!ast_strlen_zero(qe->announce))
04498 announce = qe->announce;
04499 if (!ast_strlen_zero(announceoverride))
04500 announce = announceoverride;
04501
04502 memi = ao2_iterator_init(qe->parent->members, 0);
04503 while ((cur = ao2_iterator_next(&memi))) {
04504 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04505 struct ast_dialed_interface *di;
04506 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04507 if (!tmp) {
04508 ao2_ref(cur, -1);
04509 ao2_iterator_destroy(&memi);
04510 ao2_unlock(qe->parent);
04511 goto out;
04512 }
04513 if (!datastore) {
04514 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04515 callattempt_free(tmp);
04516 ao2_ref(cur, -1);
04517 ao2_iterator_destroy(&memi);
04518 ao2_unlock(qe->parent);
04519 goto out;
04520 }
04521 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04522 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04523 callattempt_free(tmp);
04524 ao2_ref(cur, -1);
04525 ao2_iterator_destroy(&memi);
04526 ao2_unlock(&qe->parent);
04527 goto out;
04528 }
04529 datastore->data = dialed_interfaces;
04530 AST_LIST_HEAD_INIT(dialed_interfaces);
04531
04532 ast_channel_lock(qe->chan);
04533 ast_channel_datastore_add(qe->chan, datastore);
04534 ast_channel_unlock(qe->chan);
04535 } else
04536 dialed_interfaces = datastore->data;
04537
04538 AST_LIST_LOCK(dialed_interfaces);
04539 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04540 if (!strcasecmp(cur->interface, di->interface)) {
04541 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
04542 di->interface);
04543 break;
04544 }
04545 }
04546 AST_LIST_UNLOCK(dialed_interfaces);
04547
04548 if (di) {
04549 callattempt_free(tmp);
04550 ao2_ref(cur, -1);
04551 continue;
04552 }
04553
04554
04555
04556
04557
04558 if (strncasecmp(cur->interface, "Local/", 6)) {
04559 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04560 callattempt_free(tmp);
04561 ao2_ref(cur, -1);
04562 ao2_iterator_destroy(&memi);
04563 ao2_unlock(qe->parent);
04564 goto out;
04565 }
04566 strcpy(di->interface, cur->interface);
04567
04568 AST_LIST_LOCK(dialed_interfaces);
04569 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04570 AST_LIST_UNLOCK(dialed_interfaces);
04571 }
04572
04573
04574
04575
04576
04577
04578
04579 ast_channel_lock(qe->chan);
04580 ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04581 ast_channel_unlock(qe->chan);
04582
04583 tmp->block_connected_update = block_connected_line;
04584 tmp->stillgoing = 1;
04585 tmp->member = cur;
04586 tmp->lastcall = cur->lastcall;
04587 tmp->lastqueue = cur->lastqueue;
04588 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04589
04590
04591 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04592
04593
04594
04595 tmp->q_next = outgoing;
04596 outgoing = tmp;
04597
04598 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04599 break;
04600 } else {
04601 callattempt_free(tmp);
04602 }
04603 }
04604 ao2_iterator_destroy(&memi);
04605
04606 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04607
04608 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04609 to = (qe->expire - now) * 1000;
04610 else
04611 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04612 } else {
04613
04614 if (qe->expire && qe->expire<=now) {
04615 to = 0;
04616 } else if (qe->parent->timeout) {
04617 to = qe->parent->timeout * 1000;
04618 } else {
04619 to = -1;
04620 }
04621 }
04622 orig = to;
04623 ++qe->pending;
04624 ao2_unlock(qe->parent);
04625 ring_one(qe, outgoing, &numbusies);
04626 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
04627 ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
04628 forwardsallowed);
04629
04630
04631
04632
04633
04634
04635 ast_channel_lock(qe->chan);
04636 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04637 ast_datastore_free(datastore);
04638 }
04639 ast_channel_unlock(qe->chan);
04640 ao2_lock(qe->parent);
04641 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
04642 store_next_rr(qe, outgoing);
04643
04644 }
04645 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04646 store_next_lin(qe, outgoing);
04647 }
04648 ao2_unlock(qe->parent);
04649 peer = lpeer ? lpeer->chan : NULL;
04650 if (!peer) {
04651 qe->pending = 0;
04652 if (to) {
04653
04654 res = -1;
04655 } else {
04656
04657 res = digit;
04658 }
04659 if (res == -1)
04660 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
04661 if (ast_cdr_isset_unanswered()) {
04662
04663
04664 struct callattempt *o;
04665 for (o = outgoing; o; o = o->q_next) {
04666 if (!o->chan) {
04667 continue;
04668 }
04669 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04670 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04671 break;
04672 }
04673 }
04674 }
04675 } else {
04676
04677
04678
04679 if (!strcmp(qe->chan->tech->type, "DAHDI"))
04680 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04681 if (!strcmp(peer->tech->type, "DAHDI"))
04682 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04683
04684 time(&now);
04685 recalc_holdtime(qe, (now - qe->start));
04686 ao2_lock(qe->parent);
04687 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04688 ao2_unlock(qe->parent);
04689 member = lpeer->member;
04690
04691 ao2_ref(member, 1);
04692 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04693 outgoing = NULL;
04694 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04695 int res2;
04696
04697 res2 = ast_autoservice_start(qe->chan);
04698 if (!res2) {
04699 if (qe->parent->memberdelay) {
04700 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04701 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04702 }
04703 if (!res2 && announce) {
04704 if (play_file(peer, announce) < 0) {
04705 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name);
04706 }
04707 }
04708 if (!res2 && qe->parent->reportholdtime) {
04709 if (!play_file(peer, qe->parent->sound_reporthold)) {
04710 int holdtime, holdtimesecs;
04711
04712 time(&now);
04713 holdtime = abs((now - qe->start) / 60);
04714 holdtimesecs = abs((now - qe->start) % 60);
04715 if (holdtime > 0) {
04716 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
04717 if (play_file(peer, qe->parent->sound_minutes) < 0) {
04718 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name);
04719 }
04720 }
04721 if (holdtimesecs > 1) {
04722 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
04723 if (play_file(peer, qe->parent->sound_seconds) < 0) {
04724 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name);
04725 }
04726 }
04727 }
04728 }
04729 ast_autoservice_stop(qe->chan);
04730 }
04731 if (ast_check_hangup(peer)) {
04732
04733 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
04734 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
04735 if (qe->parent->eventwhencalled)
04736 manager_event(EVENT_FLAG_AGENT, "AgentDump",
04737 "Queue: %s\r\n"
04738 "Uniqueid: %s\r\n"
04739 "Channel: %s\r\n"
04740 "Member: %s\r\n"
04741 "MemberName: %s\r\n"
04742 "%s",
04743 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04744 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04745 ast_hangup(peer);
04746 ao2_ref(member, -1);
04747 goto out;
04748 } else if (ast_check_hangup(qe->chan)) {
04749
04750 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
04751 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04752 record_abandoned(qe);
04753 ast_hangup(peer);
04754 ao2_ref(member, -1);
04755 return -1;
04756 }
04757 }
04758
04759 if (ringing)
04760 ast_indicate(qe->chan,-1);
04761 else
04762 ast_moh_stop(qe->chan);
04763
04764 if (qe->chan->cdr)
04765 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
04766
04767 res = ast_channel_make_compatible(qe->chan, peer);
04768 if (res < 0) {
04769 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
04770 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
04771 record_abandoned(qe);
04772 ast_cdr_failed(qe->chan->cdr);
04773 ast_hangup(peer);
04774 ao2_ref(member, -1);
04775 return -1;
04776 }
04777
04778
04779 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
04780 if (play_file(qe->chan, qe->parent->sound_callerannounce))
04781 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
04782 }
04783
04784 ao2_lock(qe->parent);
04785
04786
04787 if (qe->parent->setinterfacevar) {
04788 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
04789 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
04790 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04791 pbx_builtin_setvar_multiple(peer, interfacevar);
04792 }
04793
04794
04795
04796 if (qe->parent->setqueueentryvar) {
04797 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
04798 (long) time(NULL) - qe->start, qe->opos);
04799 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04800 pbx_builtin_setvar_multiple(peer, interfacevar);
04801 }
04802
04803 ao2_unlock(qe->parent);
04804
04805
04806 set_queue_variables(qe->parent, qe->chan);
04807 set_queue_variables(qe->parent, peer);
04808
04809 ast_channel_lock(qe->chan);
04810 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04811 monitorfilename = ast_strdupa(monitorfilename);
04812 }
04813 ast_channel_unlock(qe->chan);
04814
04815 if (qe->parent->monfmt && *qe->parent->monfmt) {
04816 if (!qe->parent->montype) {
04817 const char *monexec;
04818 ast_debug(1, "Starting Monitor as requested.\n");
04819 ast_channel_lock(qe->chan);
04820 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
04821 which = qe->chan;
04822 monexec = monexec ? ast_strdupa(monexec) : NULL;
04823 }
04824 else
04825 which = peer;
04826 ast_channel_unlock(qe->chan);
04827 if (monitorfilename) {
04828 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04829 } else if (qe->chan->cdr) {
04830 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04831 } else {
04832
04833 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04834 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04835 }
04836 if (!ast_strlen_zero(monexec)) {
04837 ast_monitor_setjoinfiles(which, 1);
04838 }
04839 } else {
04840 mixmonapp = pbx_findapp("MixMonitor");
04841
04842 if (mixmonapp) {
04843 ast_debug(1, "Starting MixMonitor as requested.\n");
04844 if (!monitorfilename) {
04845 if (qe->chan->cdr)
04846 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04847 else
04848 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04849 } else {
04850 const char *m = monitorfilename;
04851 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04852 switch (*m) {
04853 case '^':
04854 if (*(m + 1) == '{')
04855 *p = '$';
04856 break;
04857 case ',':
04858 *p++ = '\\';
04859
04860 default:
04861 *p = *m;
04862 }
04863 if (*m == '\0')
04864 break;
04865 }
04866 if (p == tmpid2 + sizeof(tmpid2))
04867 tmpid2[sizeof(tmpid2) - 1] = '\0';
04868
04869 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04870 }
04871
04872 ast_channel_lock(qe->chan);
04873 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04874 monitor_exec = ast_strdupa(monitor_exec);
04875 }
04876 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04877 monitor_options = ast_strdupa(monitor_options);
04878 } else {
04879 monitor_options = "";
04880 }
04881 ast_channel_unlock(qe->chan);
04882
04883 if (monitor_exec) {
04884 const char *m = monitor_exec;
04885 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04886 switch (*m) {
04887 case '^':
04888 if (*(m + 1) == '{')
04889 *p = '$';
04890 break;
04891 case ',':
04892 *p++ = '\\';
04893
04894 default:
04895 *p = *m;
04896 }
04897 if (*m == '\0')
04898 break;
04899 }
04900 if (p == meid2 + sizeof(meid2))
04901 meid2[sizeof(meid2) - 1] = '\0';
04902
04903 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04904 }
04905
04906 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04907
04908 if (!ast_strlen_zero(monitor_exec))
04909 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04910 else
04911 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04912
04913 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04914
04915 if (qe->chan->cdr)
04916 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04917 pbx_exec(qe->chan, mixmonapp, mixmonargs);
04918 if (qe->chan->cdr)
04919 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04920
04921 } else {
04922 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04923 }
04924 }
04925 }
04926
04927 leave_queue(qe);
04928 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04929 ast_debug(1, "app_queue: sendurl=%s.\n", url);
04930 ast_channel_sendurl(peer, url);
04931 }
04932
04933
04934
04935 if (!ast_strlen_zero(macro)) {
04936 macroexec = ast_strdupa(macro);
04937 } else {
04938 if (qe->parent->membermacro)
04939 macroexec = ast_strdupa(qe->parent->membermacro);
04940 }
04941
04942 if (!ast_strlen_zero(macroexec)) {
04943 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04944
04945 res = ast_autoservice_start(qe->chan);
04946 if (res) {
04947 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04948 res = -1;
04949 }
04950
04951 application = pbx_findapp("Macro");
04952
04953 if (application) {
04954 res = pbx_exec(peer, application, macroexec);
04955 ast_debug(1, "Macro exited with status %d\n", res);
04956 res = 0;
04957 } else {
04958 ast_log(LOG_ERROR, "Could not find application Macro\n");
04959 res = -1;
04960 }
04961
04962 if (ast_autoservice_stop(qe->chan) < 0) {
04963 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04964 res = -1;
04965 }
04966 }
04967
04968
04969
04970 if (!ast_strlen_zero(gosub)) {
04971 gosubexec = ast_strdupa(gosub);
04972 } else {
04973 if (qe->parent->membergosub)
04974 gosubexec = ast_strdupa(qe->parent->membergosub);
04975 }
04976
04977 if (!ast_strlen_zero(gosubexec)) {
04978 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
04979
04980 res = ast_autoservice_start(qe->chan);
04981 if (res) {
04982 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04983 res = -1;
04984 }
04985
04986 application = pbx_findapp("Gosub");
04987
04988 if (application) {
04989 char *gosub_args, *gosub_argstart;
04990
04991
04992 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
04993 ast_copy_string(peer->exten, "s", sizeof(peer->exten));
04994 peer->priority = 0;
04995
04996 gosub_argstart = strchr(gosubexec, ',');
04997 if (gosub_argstart) {
04998 const char *what_is_s = "s";
04999 *gosub_argstart = 0;
05000 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05001 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05002 what_is_s = "~~s~~";
05003 }
05004 if (asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
05005 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05006 gosub_args = NULL;
05007 }
05008 *gosub_argstart = ',';
05009 } else {
05010 const char *what_is_s = "s";
05011 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05012 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05013 what_is_s = "~~s~~";
05014 }
05015 if (asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
05016 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05017 gosub_args = NULL;
05018 }
05019 }
05020 if (gosub_args) {
05021 res = pbx_exec(peer, application, gosub_args);
05022 if (!res) {
05023 struct ast_pbx_args args;
05024 memset(&args, 0, sizeof(args));
05025 args.no_hangup_chan = 1;
05026 ast_pbx_run_args(peer, &args);
05027 }
05028 ast_free(gosub_args);
05029 ast_debug(1, "Gosub exited with status %d\n", res);
05030 } else {
05031 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
05032 }
05033 } else {
05034 ast_log(LOG_ERROR, "Could not find application Gosub\n");
05035 res = -1;
05036 }
05037
05038 if (ast_autoservice_stop(qe->chan) < 0) {
05039 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05040 res = -1;
05041 }
05042 }
05043
05044 if (!ast_strlen_zero(agi)) {
05045 ast_debug(1, "app_queue: agi=%s.\n", agi);
05046 application = pbx_findapp("agi");
05047 if (application) {
05048 agiexec = ast_strdupa(agi);
05049 pbx_exec(qe->chan, application, agiexec);
05050 } else
05051 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
05052 }
05053 qe->handled++;
05054 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
05055 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
05056
05057 if (qe->chan->cdr) {
05058 struct ast_cdr *cdr;
05059 struct ast_cdr *newcdr;
05060
05061
05062 cdr = qe->chan->cdr;
05063 while (cdr->next) {
05064 cdr = cdr->next;
05065 }
05066
05067
05068 if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) &&
05069 (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) &&
05070 (newcdr = ast_cdr_dup(cdr))) {
05071 ast_channel_lock(qe->chan);
05072 ast_cdr_init(newcdr, qe->chan);
05073 ast_cdr_reset(newcdr, 0);
05074 cdr = ast_cdr_append(cdr, newcdr);
05075 cdr = cdr->next;
05076 ast_channel_unlock(qe->chan);
05077 }
05078
05079 if (update_cdr) {
05080 ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05081 }
05082 }
05083
05084 if (qe->parent->eventwhencalled)
05085 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05086 "Queue: %s\r\n"
05087 "Uniqueid: %s\r\n"
05088 "Channel: %s\r\n"
05089 "Member: %s\r\n"
05090 "MemberName: %s\r\n"
05091 "Holdtime: %ld\r\n"
05092 "BridgedChannel: %s\r\n"
05093 "Ringtime: %ld\r\n"
05094 "%s",
05095 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05096 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05097 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05098 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05099 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05100
05101 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05102 queue_end_bridge->q = qe->parent;
05103 queue_end_bridge->chan = qe->chan;
05104 bridge_config.end_bridge_callback = end_bridge_callback;
05105 bridge_config.end_bridge_callback_data = queue_end_bridge;
05106 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05107
05108
05109
05110
05111 queue_t_ref(qe->parent, "For bridge_config reference");
05112 }
05113
05114 time(&callstart);
05115 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05116 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
05117
05118
05119
05120
05121
05122 ast_channel_lock(qe->chan);
05123 if (!attended_transfer_occurred(qe->chan)) {
05124 struct ast_datastore *tds;
05125
05126
05127 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05128 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05129 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05130 (long) (time(NULL) - callstart), qe->opos);
05131 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05132 } else if (ast_check_hangup(qe->chan)) {
05133 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05134 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05135 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05136 } else {
05137 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
05138 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05139 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
05140 }
05141 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
05142 ast_channel_datastore_remove(qe->chan, tds);
05143 }
05144 ast_channel_unlock(qe->chan);
05145 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05146 } else {
05147 ast_channel_unlock(qe->chan);
05148
05149
05150 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05151 }
05152
05153 if (transfer_ds) {
05154 ast_datastore_free(transfer_ds);
05155 }
05156 ast_hangup(peer);
05157 res = bridge ? bridge : 1;
05158 ao2_ref(member, -1);
05159 }
05160 out:
05161 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05162
05163 return res;
05164 }
05165
05166 static int wait_a_bit(struct queue_ent *qe)
05167 {
05168
05169 int retrywait = qe->parent->retry * 1000;
05170
05171 int res = ast_waitfordigit(qe->chan, retrywait);
05172 if (res > 0 && !valid_exit(qe, res))
05173 res = 0;
05174
05175 return res;
05176 }
05177
05178 static struct member *interface_exists(struct call_queue *q, const char *interface)
05179 {
05180 struct member *mem;
05181 struct ao2_iterator mem_iter;
05182
05183 if (!q)
05184 return NULL;
05185
05186 mem_iter = ao2_iterator_init(q->members, 0);
05187 while ((mem = ao2_iterator_next(&mem_iter))) {
05188 if (!strcasecmp(interface, mem->interface)) {
05189 ao2_iterator_destroy(&mem_iter);
05190 return mem;
05191 }
05192 ao2_ref(mem, -1);
05193 }
05194 ao2_iterator_destroy(&mem_iter);
05195
05196 return NULL;
05197 }
05198
05199
05200
05201
05202
05203
05204 static void dump_queue_members(struct call_queue *pm_queue)
05205 {
05206 struct member *cur_member;
05207 char value[PM_MAX_LEN];
05208 int value_len = 0;
05209 int res;
05210 struct ao2_iterator mem_iter;
05211
05212 memset(value, 0, sizeof(value));
05213
05214 if (!pm_queue)
05215 return;
05216
05217 mem_iter = ao2_iterator_init(pm_queue->members, 0);
05218 while ((cur_member = ao2_iterator_next(&mem_iter))) {
05219 if (!cur_member->dynamic) {
05220 ao2_ref(cur_member, -1);
05221 continue;
05222 }
05223
05224 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
05225 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
05226
05227 ao2_ref(cur_member, -1);
05228
05229 if (res != strlen(value + value_len)) {
05230 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
05231 break;
05232 }
05233 value_len += res;
05234 }
05235 ao2_iterator_destroy(&mem_iter);
05236
05237 if (value_len && !cur_member) {
05238 if (ast_db_put(pm_family, pm_queue->name, value))
05239 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05240 } else
05241
05242 ast_db_del(pm_family, pm_queue->name);
05243 }
05244
05245
05246
05247
05248
05249
05250
05251 static int remove_from_queue(const char *queuename, const char *interface)
05252 {
05253 struct call_queue *q, tmpq = {
05254 .name = queuename,
05255 };
05256 struct member *mem, tmpmem;
05257 int res = RES_NOSUCHQUEUE;
05258
05259 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05260 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05261 ao2_lock(q);
05262 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05263
05264 if (!mem->dynamic) {
05265 ao2_ref(mem, -1);
05266 ao2_unlock(q);
05267 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05268 return RES_NOT_DYNAMIC;
05269 }
05270 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05271 "Queue: %s\r\n"
05272 "Location: %s\r\n"
05273 "MemberName: %s\r\n",
05274 q->name, mem->interface, mem->membername);
05275 ao2_unlink(q->members, mem);
05276 ao2_ref(mem, -1);
05277
05278 if (queue_persistent_members)
05279 dump_queue_members(q);
05280
05281 res = RES_OKAY;
05282 } else {
05283 res = RES_EXISTS;
05284 }
05285 ao2_unlock(q);
05286 queue_t_unref(q, "Expiring temporary reference");
05287 }
05288
05289 return res;
05290 }
05291
05292
05293
05294
05295
05296
05297
05298
05299 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
05300 {
05301 struct call_queue *q;
05302 struct member *new_member, *old_member;
05303 int res = RES_NOSUCHQUEUE;
05304
05305
05306
05307 if (!(q = load_realtime_queue(queuename)))
05308 return res;
05309
05310 ao2_lock(q);
05311 if ((old_member = interface_exists(q, interface)) == NULL) {
05312 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05313 new_member->dynamic = 1;
05314 ao2_link(q->members, new_member);
05315 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05316 "Queue: %s\r\n"
05317 "Location: %s\r\n"
05318 "MemberName: %s\r\n"
05319 "Membership: %s\r\n"
05320 "Penalty: %d\r\n"
05321 "CallsTaken: %d\r\n"
05322 "LastCall: %d\r\n"
05323 "Status: %d\r\n"
05324 "Paused: %d\r\n",
05325 q->name, new_member->interface, new_member->membername,
05326 "dynamic",
05327 new_member->penalty, new_member->calls, (int) new_member->lastcall,
05328 new_member->status, new_member->paused);
05329
05330 ao2_ref(new_member, -1);
05331 new_member = NULL;
05332
05333 if (dump)
05334 dump_queue_members(q);
05335
05336 res = RES_OKAY;
05337 } else {
05338 res = RES_OUTOFMEMORY;
05339 }
05340 } else {
05341 ao2_ref(old_member, -1);
05342 res = RES_EXISTS;
05343 }
05344 ao2_unlock(q);
05345 queue_t_unref(q, "Expiring temporary reference");
05346
05347 return res;
05348 }
05349
05350 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
05351 {
05352 int found = 0;
05353 struct call_queue *q;
05354 struct member *mem;
05355 struct ao2_iterator queue_iter;
05356 int failed;
05357
05358
05359
05360 if (ast_strlen_zero(queuename))
05361 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05362
05363 queue_iter = ao2_iterator_init(queues, 0);
05364 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05365 ao2_lock(q);
05366 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05367 if ((mem = interface_exists(q, interface))) {
05368 if (mem->paused == paused) {
05369 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05370 }
05371
05372 failed = 0;
05373 if (mem->realtime) {
05374 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05375 }
05376
05377 if (failed) {
05378 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05379 ao2_ref(mem, -1);
05380 ao2_unlock(q);
05381 queue_t_unref(q, "Done with iterator");
05382 continue;
05383 }
05384 found++;
05385 mem->paused = paused;
05386
05387 if (queue_persistent_members)
05388 dump_queue_members(q);
05389
05390 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05391
05392 if (!ast_strlen_zero(reason)) {
05393 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05394 "Queue: %s\r\n"
05395 "Location: %s\r\n"
05396 "MemberName: %s\r\n"
05397 "Paused: %d\r\n"
05398 "Reason: %s\r\n",
05399 q->name, mem->interface, mem->membername, paused, reason);
05400 } else {
05401 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05402 "Queue: %s\r\n"
05403 "Location: %s\r\n"
05404 "MemberName: %s\r\n"
05405 "Paused: %d\r\n",
05406 q->name, mem->interface, mem->membername, paused);
05407 }
05408 ao2_ref(mem, -1);
05409 }
05410 }
05411
05412 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05413 ao2_unlock(q);
05414 queue_t_unref(q, "Done with iterator");
05415 break;
05416 }
05417
05418 ao2_unlock(q);
05419 queue_t_unref(q, "Done with iterator");
05420 }
05421 ao2_iterator_destroy(&queue_iter);
05422
05423 return found ? RESULT_SUCCESS : RESULT_FAILURE;
05424 }
05425
05426
05427 static int set_member_penalty(const char *queuename, const char *interface, int penalty)
05428 {
05429 int foundinterface = 0, foundqueue = 0;
05430 struct call_queue *q;
05431 struct member *mem;
05432 struct ao2_iterator queue_iter;
05433
05434 if (penalty < 0) {
05435 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05436 return RESULT_FAILURE;
05437 }
05438
05439 queue_iter = ao2_iterator_init(queues, 0);
05440 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05441 ao2_lock(q);
05442 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05443 foundqueue++;
05444 if ((mem = interface_exists(q, interface))) {
05445 foundinterface++;
05446 mem->penalty = penalty;
05447
05448 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05449 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05450 "Queue: %s\r\n"
05451 "Location: %s\r\n"
05452 "Penalty: %d\r\n",
05453 q->name, mem->interface, penalty);
05454 ao2_ref(mem, -1);
05455 }
05456 }
05457 ao2_unlock(q);
05458 queue_t_unref(q, "Done with iterator");
05459 }
05460 ao2_iterator_destroy(&queue_iter);
05461
05462 if (foundinterface) {
05463 return RESULT_SUCCESS;
05464 } else if (!foundqueue) {
05465 ast_log (LOG_ERROR, "Invalid queuename\n");
05466 } else {
05467 ast_log (LOG_ERROR, "Invalid interface\n");
05468 }
05469
05470 return RESULT_FAILURE;
05471 }
05472
05473
05474
05475
05476 static int get_member_penalty(char *queuename, char *interface)
05477 {
05478 int foundqueue = 0, penalty;
05479 struct call_queue *q, tmpq = {
05480 .name = queuename,
05481 };
05482 struct member *mem;
05483
05484 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05485 foundqueue = 1;
05486 ao2_lock(q);
05487 if ((mem = interface_exists(q, interface))) {
05488 penalty = mem->penalty;
05489 ao2_ref(mem, -1);
05490 ao2_unlock(q);
05491 queue_t_unref(q, "Search complete");
05492 return penalty;
05493 }
05494 ao2_unlock(q);
05495 queue_t_unref(q, "Search complete");
05496 }
05497
05498
05499 if (foundqueue)
05500 ast_log (LOG_ERROR, "Invalid queuename\n");
05501 else
05502 ast_log (LOG_ERROR, "Invalid interface\n");
05503
05504 return RESULT_FAILURE;
05505 }
05506
05507
05508 static void reload_queue_members(void)
05509 {
05510 char *cur_ptr;
05511 const char *queue_name;
05512 char *member;
05513 char *interface;
05514 char *membername = NULL;
05515 char *state_interface;
05516 char *penalty_tok;
05517 int penalty = 0;
05518 char *paused_tok;
05519 int paused = 0;
05520 struct ast_db_entry *db_tree;
05521 struct ast_db_entry *entry;
05522 struct call_queue *cur_queue;
05523 char queue_data[PM_MAX_LEN];
05524
05525
05526 db_tree = ast_db_gettree(pm_family, NULL);
05527 for (entry = db_tree; entry; entry = entry->next) {
05528
05529 queue_name = entry->key + strlen(pm_family) + 2;
05530
05531 {
05532 struct call_queue tmpq = {
05533 .name = queue_name,
05534 };
05535 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05536 }
05537
05538 if (!cur_queue)
05539 cur_queue = load_realtime_queue(queue_name);
05540
05541 if (!cur_queue) {
05542
05543
05544 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05545 ast_db_del(pm_family, queue_name);
05546 continue;
05547 }
05548
05549 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05550 queue_t_unref(cur_queue, "Expire reload reference");
05551 continue;
05552 }
05553
05554 cur_ptr = queue_data;
05555 while ((member = strsep(&cur_ptr, ",|"))) {
05556 if (ast_strlen_zero(member))
05557 continue;
05558
05559 interface = strsep(&member, ";");
05560 penalty_tok = strsep(&member, ";");
05561 paused_tok = strsep(&member, ";");
05562 membername = strsep(&member, ";");
05563 state_interface = strsep(&member, ";");
05564
05565 if (!penalty_tok) {
05566 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05567 break;
05568 }
05569 penalty = strtol(penalty_tok, NULL, 10);
05570 if (errno == ERANGE) {
05571 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05572 break;
05573 }
05574
05575 if (!paused_tok) {
05576 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05577 break;
05578 }
05579 paused = strtol(paused_tok, NULL, 10);
05580 if ((errno == ERANGE) || paused < 0 || paused > 1) {
05581 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05582 break;
05583 }
05584
05585 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
05586
05587 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05588 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05589 break;
05590 }
05591 }
05592 queue_t_unref(cur_queue, "Expire reload reference");
05593 }
05594
05595 if (db_tree) {
05596 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05597 ast_db_freetree(db_tree);
05598 }
05599 }
05600
05601
05602 static int pqm_exec(struct ast_channel *chan, const char *data)
05603 {
05604 char *parse;
05605 AST_DECLARE_APP_ARGS(args,
05606 AST_APP_ARG(queuename);
05607 AST_APP_ARG(interface);
05608 AST_APP_ARG(options);
05609 AST_APP_ARG(reason);
05610 );
05611
05612 if (ast_strlen_zero(data)) {
05613 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05614 return -1;
05615 }
05616
05617 parse = ast_strdupa(data);
05618
05619 AST_STANDARD_APP_ARGS(args, parse);
05620
05621 if (ast_strlen_zero(args.interface)) {
05622 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05623 return -1;
05624 }
05625
05626 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05627 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05628 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05629 return 0;
05630 }
05631
05632 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05633
05634 return 0;
05635 }
05636
05637
05638 static int upqm_exec(struct ast_channel *chan, const char *data)
05639 {
05640 char *parse;
05641 AST_DECLARE_APP_ARGS(args,
05642 AST_APP_ARG(queuename);
05643 AST_APP_ARG(interface);
05644 AST_APP_ARG(options);
05645 AST_APP_ARG(reason);
05646 );
05647
05648 if (ast_strlen_zero(data)) {
05649 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05650 return -1;
05651 }
05652
05653 parse = ast_strdupa(data);
05654
05655 AST_STANDARD_APP_ARGS(args, parse);
05656
05657 if (ast_strlen_zero(args.interface)) {
05658 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05659 return -1;
05660 }
05661
05662 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05663 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05664 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05665 return 0;
05666 }
05667
05668 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05669
05670 return 0;
05671 }
05672
05673
05674 static int rqm_exec(struct ast_channel *chan, const char *data)
05675 {
05676 int res=-1;
05677 char *parse, *temppos = NULL;
05678 AST_DECLARE_APP_ARGS(args,
05679 AST_APP_ARG(queuename);
05680 AST_APP_ARG(interface);
05681 AST_APP_ARG(options);
05682 );
05683
05684
05685 if (ast_strlen_zero(data)) {
05686 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05687 return -1;
05688 }
05689
05690 parse = ast_strdupa(data);
05691
05692 AST_STANDARD_APP_ARGS(args, parse);
05693
05694 if (ast_strlen_zero(args.interface)) {
05695 args.interface = ast_strdupa(chan->name);
05696 temppos = strrchr(args.interface, '-');
05697 if (temppos)
05698 *temppos = '\0';
05699 }
05700
05701 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05702
05703 switch (remove_from_queue(args.queuename, args.interface)) {
05704 case RES_OKAY:
05705 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05706 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05707 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05708 res = 0;
05709 break;
05710 case RES_EXISTS:
05711 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05712 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05713 res = 0;
05714 break;
05715 case RES_NOSUCHQUEUE:
05716 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05717 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05718 res = 0;
05719 break;
05720 case RES_NOT_DYNAMIC:
05721 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05722 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05723 res = 0;
05724 break;
05725 }
05726
05727 return res;
05728 }
05729
05730
05731 static int aqm_exec(struct ast_channel *chan, const char *data)
05732 {
05733 int res=-1;
05734 char *parse, *temppos = NULL;
05735 AST_DECLARE_APP_ARGS(args,
05736 AST_APP_ARG(queuename);
05737 AST_APP_ARG(interface);
05738 AST_APP_ARG(penalty);
05739 AST_APP_ARG(options);
05740 AST_APP_ARG(membername);
05741 AST_APP_ARG(state_interface);
05742 );
05743 int penalty = 0;
05744
05745 if (ast_strlen_zero(data)) {
05746 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05747 return -1;
05748 }
05749
05750 parse = ast_strdupa(data);
05751
05752 AST_STANDARD_APP_ARGS(args, parse);
05753
05754 if (ast_strlen_zero(args.interface)) {
05755 args.interface = ast_strdupa(chan->name);
05756 temppos = strrchr(args.interface, '-');
05757 if (temppos)
05758 *temppos = '\0';
05759 }
05760
05761 if (!ast_strlen_zero(args.penalty)) {
05762 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05763 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05764 penalty = 0;
05765 }
05766 }
05767
05768 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05769 case RES_OKAY:
05770 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05771 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05772 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05773 res = 0;
05774 break;
05775 case RES_EXISTS:
05776 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05777 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05778 res = 0;
05779 break;
05780 case RES_NOSUCHQUEUE:
05781 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05782 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05783 res = 0;
05784 break;
05785 case RES_OUTOFMEMORY:
05786 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
05787 break;
05788 }
05789
05790 return res;
05791 }
05792
05793
05794 static int ql_exec(struct ast_channel *chan, const char *data)
05795 {
05796 char *parse;
05797
05798 AST_DECLARE_APP_ARGS(args,
05799 AST_APP_ARG(queuename);
05800 AST_APP_ARG(uniqueid);
05801 AST_APP_ARG(membername);
05802 AST_APP_ARG(event);
05803 AST_APP_ARG(params);
05804 );
05805
05806 if (ast_strlen_zero(data)) {
05807 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05808 return -1;
05809 }
05810
05811 parse = ast_strdupa(data);
05812
05813 AST_STANDARD_APP_ARGS(args, parse);
05814
05815 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05816 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05817 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05818 return -1;
05819 }
05820
05821 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
05822 "%s", args.params ? args.params : "");
05823
05824 return 0;
05825 }
05826
05827
05828 static void copy_rules(struct queue_ent *qe, const char *rulename)
05829 {
05830 struct penalty_rule *pr_iter;
05831 struct rule_list *rl_iter;
05832 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05833 AST_LIST_LOCK(&rule_lists);
05834 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05835 if (!strcasecmp(rl_iter->name, tmp))
05836 break;
05837 }
05838 if (rl_iter) {
05839 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05840 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05841 if (!new_pr) {
05842 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05843 break;
05844 }
05845 new_pr->time = pr_iter->time;
05846 new_pr->max_value = pr_iter->max_value;
05847 new_pr->min_value = pr_iter->min_value;
05848 new_pr->max_relative = pr_iter->max_relative;
05849 new_pr->min_relative = pr_iter->min_relative;
05850 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05851 }
05852 }
05853 AST_LIST_UNLOCK(&rule_lists);
05854 }
05855
05856
05857
05858
05859
05860
05861
05862
05863
05864
05865
05866
05867
05868 static int queue_exec(struct ast_channel *chan, const char *data)
05869 {
05870 int res=-1;
05871 int ringing=0;
05872 const char *user_priority;
05873 const char *max_penalty_str;
05874 const char *min_penalty_str;
05875 int prio;
05876 int qcontinue = 0;
05877 int max_penalty, min_penalty;
05878 enum queue_result reason = QUEUE_UNKNOWN;
05879
05880 int tries = 0;
05881 int noption = 0;
05882 char *parse;
05883 int makeannouncement = 0;
05884 int position = 0;
05885 AST_DECLARE_APP_ARGS(args,
05886 AST_APP_ARG(queuename);
05887 AST_APP_ARG(options);
05888 AST_APP_ARG(url);
05889 AST_APP_ARG(announceoverride);
05890 AST_APP_ARG(queuetimeoutstr);
05891 AST_APP_ARG(agi);
05892 AST_APP_ARG(macro);
05893 AST_APP_ARG(gosub);
05894 AST_APP_ARG(rule);
05895 AST_APP_ARG(position);
05896 );
05897
05898 struct queue_ent qe = { 0 };
05899
05900 if (ast_strlen_zero(data)) {
05901 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
05902 return -1;
05903 }
05904
05905 parse = ast_strdupa(data);
05906 AST_STANDARD_APP_ARGS(args, parse);
05907
05908
05909 qe.start = time(NULL);
05910
05911
05912 if (!ast_strlen_zero(args.queuetimeoutstr))
05913 qe.expire = qe.start + atoi(args.queuetimeoutstr);
05914 else
05915 qe.expire = 0;
05916
05917
05918 ast_channel_lock(chan);
05919 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05920 if (user_priority) {
05921 if (sscanf(user_priority, "%30d", &prio) == 1) {
05922 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05923 } else {
05924 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05925 user_priority, chan->name);
05926 prio = 0;
05927 }
05928 } else {
05929 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05930 prio = 0;
05931 }
05932
05933
05934
05935 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05936 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05937 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05938 } else {
05939 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05940 max_penalty_str, chan->name);
05941 max_penalty = 0;
05942 }
05943 } else {
05944 max_penalty = 0;
05945 }
05946
05947 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05948 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05949 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05950 } else {
05951 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05952 min_penalty_str, chan->name);
05953 min_penalty = 0;
05954 }
05955 } else {
05956 min_penalty = 0;
05957 }
05958 ast_channel_unlock(chan);
05959
05960 if (args.options && (strchr(args.options, 'r')))
05961 ringing = 1;
05962
05963 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
05964 qe.ring_when_ringing = 1;
05965 }
05966
05967 if (args.options && (strchr(args.options, 'c')))
05968 qcontinue = 1;
05969
05970 if (args.position) {
05971 position = atoi(args.position);
05972 if (position < 0) {
05973 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
05974 position = 0;
05975 }
05976 }
05977
05978 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05979 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05980
05981 qe.chan = chan;
05982 qe.prio = prio;
05983 qe.max_penalty = max_penalty;
05984 qe.min_penalty = min_penalty;
05985 qe.last_pos_said = 0;
05986 qe.last_pos = 0;
05987 qe.last_periodic_announce_time = time(NULL);
05988 qe.last_periodic_announce_sound = 0;
05989 qe.valid_digits = 0;
05990 if (join_queue(args.queuename, &qe, &reason, position)) {
05991 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05992 set_queue_result(chan, reason);
05993 return 0;
05994 }
05995 ast_assert(qe.parent != NULL);
05996
05997 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
05998 S_OR(args.url, ""),
05999 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
06000 qe.opos);
06001 copy_rules(&qe, args.rule);
06002 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
06003 check_turns:
06004 if (ringing) {
06005 ast_indicate(chan, AST_CONTROL_RINGING);
06006 } else {
06007 ast_moh_start(chan, qe.moh, NULL);
06008 }
06009
06010
06011 res = wait_our_turn(&qe, ringing, &reason);
06012 if (res) {
06013 goto stop;
06014 }
06015
06016 makeannouncement = 0;
06017
06018 for (;;) {
06019
06020
06021
06022
06023
06024
06025 if (qe.expire && (time(NULL) >= qe.expire)) {
06026 record_abandoned(&qe);
06027 reason = QUEUE_TIMEOUT;
06028 res = 0;
06029 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
06030 qe.pos, qe.opos, (long) time(NULL) - qe.start);
06031 break;
06032 }
06033
06034 if (makeannouncement) {
06035
06036 if (qe.parent->announcefrequency)
06037 if ((res = say_position(&qe,ringing)))
06038 goto stop;
06039 }
06040 makeannouncement = 1;
06041
06042
06043 if (qe.parent->periodicannouncefrequency)
06044 if ((res = say_periodic_announcement(&qe,ringing)))
06045 goto stop;
06046
06047
06048 if (qe.expire && (time(NULL) >= qe.expire)) {
06049 record_abandoned(&qe);
06050 reason = QUEUE_TIMEOUT;
06051 res = 0;
06052 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06053 break;
06054 }
06055
06056
06057 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
06058 update_qe_rule(&qe);
06059 }
06060
06061
06062 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
06063 if (res) {
06064 goto stop;
06065 }
06066
06067 if (qe.parent->leavewhenempty) {
06068 int status = 0;
06069 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
06070 record_abandoned(&qe);
06071 reason = QUEUE_LEAVEEMPTY;
06072 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
06073 res = 0;
06074 break;
06075 }
06076 }
06077
06078
06079 if (noption && tries >= ao2_container_count(qe.parent->members)) {
06080 ast_verb(3, "Exiting on time-out cycle\n");
06081 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06082 record_abandoned(&qe);
06083 reason = QUEUE_TIMEOUT;
06084 res = 0;
06085 break;
06086 }
06087
06088
06089
06090 if (qe.expire && (time(NULL) >= qe.expire)) {
06091 record_abandoned(&qe);
06092 reason = QUEUE_TIMEOUT;
06093 res = 0;
06094 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06095 break;
06096 }
06097
06098
06099 update_realtime_members(qe.parent);
06100
06101 res = wait_a_bit(&qe);
06102 if (res)
06103 goto stop;
06104
06105
06106
06107
06108
06109 if (!is_our_turn(&qe)) {
06110 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06111 goto check_turns;
06112 }
06113 }
06114
06115 stop:
06116 if (res) {
06117 if (res < 0) {
06118 if (!qe.handled) {
06119 record_abandoned(&qe);
06120 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06121 "%d|%d|%ld", qe.pos, qe.opos,
06122 (long) time(NULL) - qe.start);
06123 res = -1;
06124 } else if (qcontinue) {
06125 reason = QUEUE_CONTINUE;
06126 res = 0;
06127 }
06128 } else if (qe.valid_digits) {
06129 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06130 "%s|%d", qe.digits, qe.pos);
06131 }
06132 }
06133
06134
06135 if (res >= 0) {
06136 res = 0;
06137 if (ringing) {
06138 ast_indicate(chan, -1);
06139 } else {
06140 ast_moh_stop(chan);
06141 }
06142 ast_stopstream(chan);
06143 }
06144
06145 set_queue_variables(qe.parent, qe.chan);
06146
06147 leave_queue(&qe);
06148 if (reason != QUEUE_UNKNOWN)
06149 set_queue_result(chan, reason);
06150
06151
06152
06153
06154
06155
06156
06157 qe.parent = queue_unref(qe.parent);
06158
06159 return res;
06160 }
06161
06162
06163
06164
06165
06166
06167 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06168 {
06169 int res = -1;
06170 struct call_queue *q, tmpq = {
06171 .name = data,
06172 };
06173
06174 char interfacevar[256] = "";
06175 float sl = 0;
06176
06177 if (ast_strlen_zero(data)) {
06178 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06179 return -1;
06180 }
06181
06182 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06183 ao2_lock(q);
06184 if (q->setqueuevar) {
06185 sl = 0;
06186 res = 0;
06187
06188 if (q->callscompleted > 0) {
06189 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06190 }
06191
06192 snprintf(interfacevar, sizeof(interfacevar),
06193 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06194 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
06195
06196 pbx_builtin_setvar_multiple(chan, interfacevar);
06197 }
06198
06199 ao2_unlock(q);
06200 queue_t_unref(q, "Done with QUEUE() function");
06201 } else {
06202 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06203 }
06204
06205 snprintf(buf, len, "%d", res);
06206
06207 return 0;
06208 }
06209
06210
06211
06212
06213
06214 static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06215 {
06216 struct call_queue *q;
06217
06218 buf[0] = '\0';
06219
06220 if (ast_strlen_zero(data)) {
06221 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06222 return -1;
06223 }
06224 q = load_realtime_queue(data);
06225 snprintf(buf, len, "%d", q != NULL? 1 : 0);
06226 if (q) {
06227 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06228 }
06229
06230 return 0;
06231 }
06232
06233
06234
06235
06236
06237
06238 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06239 {
06240 int count = 0;
06241 struct member *m;
06242 struct ao2_iterator mem_iter;
06243 struct call_queue *q;
06244 char *option;
06245
06246 if (ast_strlen_zero(data)) {
06247 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06248 return -1;
06249 }
06250
06251 if ((option = strchr(data, ',')))
06252 *option++ = '\0';
06253 else
06254 option = "logged";
06255 if ((q = load_realtime_queue(data))) {
06256 ao2_lock(q);
06257 if (!strcasecmp(option, "logged")) {
06258 mem_iter = ao2_iterator_init(q->members, 0);
06259 while ((m = ao2_iterator_next(&mem_iter))) {
06260
06261 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06262 count++;
06263 }
06264 ao2_ref(m, -1);
06265 }
06266 ao2_iterator_destroy(&mem_iter);
06267 } else if (!strcasecmp(option, "free")) {
06268 mem_iter = ao2_iterator_init(q->members, 0);
06269 while ((m = ao2_iterator_next(&mem_iter))) {
06270
06271 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06272 count++;
06273 }
06274 ao2_ref(m, -1);
06275 }
06276 ao2_iterator_destroy(&mem_iter);
06277 } else if (!strcasecmp(option, "ready")) {
06278 time_t now;
06279 time(&now);
06280 mem_iter = ao2_iterator_init(q->members, 0);
06281 while ((m = ao2_iterator_next(&mem_iter))) {
06282
06283 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06284 !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06285 count++;
06286 }
06287 ao2_ref(m, -1);
06288 }
06289 ao2_iterator_destroy(&mem_iter);
06290 } else
06291 count = ao2_container_count(q->members);
06292 ao2_unlock(q);
06293 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06294 } else
06295 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06296
06297 snprintf(buf, len, "%d", count);
06298
06299 return 0;
06300 }
06301
06302
06303
06304
06305
06306
06307 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06308 {
06309 int count = 0;
06310 struct member *m;
06311 struct call_queue *q;
06312 struct ao2_iterator mem_iter;
06313 static int depflag = 1;
06314
06315 if (depflag) {
06316 depflag = 0;
06317 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");
06318 }
06319
06320 if (ast_strlen_zero(data)) {
06321 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06322 return -1;
06323 }
06324
06325 if ((q = load_realtime_queue(data))) {
06326 ao2_lock(q);
06327 mem_iter = ao2_iterator_init(q->members, 0);
06328 while ((m = ao2_iterator_next(&mem_iter))) {
06329
06330 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06331 count++;
06332 }
06333 ao2_ref(m, -1);
06334 }
06335 ao2_iterator_destroy(&mem_iter);
06336 ao2_unlock(q);
06337 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06338 } else
06339 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06340
06341 snprintf(buf, len, "%d", count);
06342
06343 return 0;
06344 }
06345
06346
06347 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06348 {
06349 int count = 0;
06350 struct call_queue *q, tmpq = {
06351 .name = data,
06352 };
06353 struct ast_variable *var = NULL;
06354
06355 buf[0] = '\0';
06356
06357 if (ast_strlen_zero(data)) {
06358 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06359 return -1;
06360 }
06361
06362 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06363 ao2_lock(q);
06364 count = q->count;
06365 ao2_unlock(q);
06366 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06367 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06368
06369
06370
06371
06372 count = 0;
06373 ast_variables_destroy(var);
06374 } else
06375 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06376
06377 snprintf(buf, len, "%d", count);
06378
06379 return 0;
06380 }
06381
06382
06383 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06384 {
06385 struct call_queue *q, tmpq = {
06386 .name = data,
06387 };
06388 struct member *m;
06389
06390
06391 buf[0] = '\0';
06392
06393 if (ast_strlen_zero(data)) {
06394 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06395 return -1;
06396 }
06397
06398 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06399 int buflen = 0, count = 0;
06400 struct ao2_iterator mem_iter;
06401
06402 ao2_lock(q);
06403 mem_iter = ao2_iterator_init(q->members, 0);
06404 while ((m = ao2_iterator_next(&mem_iter))) {
06405
06406 if (count++) {
06407 strncat(buf + buflen, ",", len - buflen - 1);
06408 buflen++;
06409 }
06410 strncat(buf + buflen, m->interface, len - buflen - 1);
06411 buflen += strlen(m->interface);
06412
06413 if (buflen >= len - 2) {
06414 ao2_ref(m, -1);
06415 ast_log(LOG_WARNING, "Truncating list\n");
06416 break;
06417 }
06418 ao2_ref(m, -1);
06419 }
06420 ao2_iterator_destroy(&mem_iter);
06421 ao2_unlock(q);
06422 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06423 } else
06424 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06425
06426
06427 buf[len - 1] = '\0';
06428
06429 return 0;
06430 }
06431
06432
06433 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06434 {
06435 int penalty;
06436 AST_DECLARE_APP_ARGS(args,
06437 AST_APP_ARG(queuename);
06438 AST_APP_ARG(interface);
06439 );
06440
06441 buf[0] = '\0';
06442
06443 if (ast_strlen_zero(data)) {
06444 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06445 return -1;
06446 }
06447
06448 AST_STANDARD_APP_ARGS(args, data);
06449
06450 if (args.argc < 2) {
06451 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06452 return -1;
06453 }
06454
06455 penalty = get_member_penalty (args.queuename, args.interface);
06456
06457 if (penalty >= 0)
06458 snprintf (buf, len, "%d", penalty);
06459
06460 return 0;
06461 }
06462
06463
06464 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
06465 {
06466 int penalty;
06467 AST_DECLARE_APP_ARGS(args,
06468 AST_APP_ARG(queuename);
06469 AST_APP_ARG(interface);
06470 );
06471
06472 if (ast_strlen_zero(data)) {
06473 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06474 return -1;
06475 }
06476
06477 AST_STANDARD_APP_ARGS(args, data);
06478
06479 if (args.argc < 2) {
06480 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06481 return -1;
06482 }
06483
06484 penalty = atoi(value);
06485
06486 if (ast_strlen_zero(args.interface)) {
06487 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06488 return -1;
06489 }
06490
06491
06492 if (set_member_penalty(args.queuename, args.interface, penalty)) {
06493 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06494 return -1;
06495 }
06496
06497 return 0;
06498 }
06499
06500 static struct ast_custom_function queueexists_function = {
06501 .name = "QUEUE_EXISTS",
06502 .read = queue_function_exists,
06503 };
06504
06505 static struct ast_custom_function queuevar_function = {
06506 .name = "QUEUE_VARIABLES",
06507 .read = queue_function_var,
06508 };
06509
06510 static struct ast_custom_function queuemembercount_function = {
06511 .name = "QUEUE_MEMBER",
06512 .read = queue_function_qac,
06513 };
06514
06515 static struct ast_custom_function queuemembercount_dep = {
06516 .name = "QUEUE_MEMBER_COUNT",
06517 .read = queue_function_qac_dep,
06518 };
06519
06520 static struct ast_custom_function queuewaitingcount_function = {
06521 .name = "QUEUE_WAITING_COUNT",
06522 .read = queue_function_queuewaitingcount,
06523 };
06524
06525 static struct ast_custom_function queuememberlist_function = {
06526 .name = "QUEUE_MEMBER_LIST",
06527 .read = queue_function_queuememberlist,
06528 };
06529
06530 static struct ast_custom_function queuememberpenalty_function = {
06531 .name = "QUEUE_MEMBER_PENALTY",
06532 .read = queue_function_memberpenalty_read,
06533 .write = queue_function_memberpenalty_write,
06534 };
06535
06536
06537
06538
06539
06540
06541
06542 static int reload_queue_rules(int reload)
06543 {
06544 struct ast_config *cfg;
06545 struct rule_list *rl_iter, *new_rl;
06546 struct penalty_rule *pr_iter;
06547 char *rulecat = NULL;
06548 struct ast_variable *rulevar = NULL;
06549 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06550
06551 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06552 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06553 return AST_MODULE_LOAD_SUCCESS;
06554 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06555 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06556 return AST_MODULE_LOAD_SUCCESS;
06557 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06558 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
06559 return AST_MODULE_LOAD_SUCCESS;
06560 }
06561
06562 AST_LIST_LOCK(&rule_lists);
06563 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06564 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06565 ast_free(pr_iter);
06566 ast_free(rl_iter);
06567 }
06568 while ((rulecat = ast_category_browse(cfg, rulecat))) {
06569 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06570 AST_LIST_UNLOCK(&rule_lists);
06571 ast_config_destroy(cfg);
06572 return AST_MODULE_LOAD_FAILURE;
06573 } else {
06574 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06575 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06576 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06577 if(!strcasecmp(rulevar->name, "penaltychange"))
06578 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06579 else
06580 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06581 }
06582 }
06583 AST_LIST_UNLOCK(&rule_lists);
06584
06585 ast_config_destroy(cfg);
06586
06587 return AST_MODULE_LOAD_SUCCESS;
06588 }
06589
06590
06591 static void queue_set_global_params(struct ast_config *cfg)
06592 {
06593 const char *general_val = NULL;
06594 queue_persistent_members = 0;
06595 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06596 queue_persistent_members = ast_true(general_val);
06597 autofill_default = 0;
06598 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06599 autofill_default = ast_true(general_val);
06600 montype_default = 0;
06601 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06602 if (!strcasecmp(general_val, "mixmonitor"))
06603 montype_default = 1;
06604 }
06605 update_cdr = 0;
06606 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06607 update_cdr = ast_true(general_val);
06608 shared_lastcall = 0;
06609 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06610 shared_lastcall = ast_true(general_val);
06611 }
06612
06613
06614
06615
06616
06617
06618
06619
06620
06621 static void reload_single_member(const char *memberdata, struct call_queue *q)
06622 {
06623 char *membername, *interface, *state_interface, *tmp;
06624 char *parse;
06625 struct member *cur, *newm;
06626 struct member tmpmem;
06627 int penalty;
06628 AST_DECLARE_APP_ARGS(args,
06629 AST_APP_ARG(interface);
06630 AST_APP_ARG(penalty);
06631 AST_APP_ARG(membername);
06632 AST_APP_ARG(state_interface);
06633 );
06634
06635 if (ast_strlen_zero(memberdata)) {
06636 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06637 return;
06638 }
06639
06640
06641 parse = ast_strdupa(memberdata);
06642
06643 AST_STANDARD_APP_ARGS(args, parse);
06644
06645 interface = args.interface;
06646 if (!ast_strlen_zero(args.penalty)) {
06647 tmp = args.penalty;
06648 ast_strip(tmp);
06649 penalty = atoi(tmp);
06650 if (penalty < 0) {
06651 penalty = 0;
06652 }
06653 } else {
06654 penalty = 0;
06655 }
06656
06657 if (!ast_strlen_zero(args.membername)) {
06658 membername = args.membername;
06659 ast_strip(membername);
06660 } else {
06661 membername = interface;
06662 }
06663
06664 if (!ast_strlen_zero(args.state_interface)) {
06665 state_interface = args.state_interface;
06666 ast_strip(state_interface);
06667 } else {
06668 state_interface = interface;
06669 }
06670
06671
06672 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06673 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06674 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06675 ao2_link(q->members, newm);
06676 ao2_ref(newm, -1);
06677 }
06678 newm = NULL;
06679
06680 if (cur) {
06681 ao2_ref(cur, -1);
06682 }
06683 }
06684
06685 static int mark_member_dead(void *obj, void *arg, int flags)
06686 {
06687 struct member *member = obj;
06688 if (!member->dynamic) {
06689 member->delme = 1;
06690 }
06691 return 0;
06692 }
06693
06694 static int kill_dead_members(void *obj, void *arg, int flags)
06695 {
06696 struct member *member = obj;
06697
06698 if (!member->delme) {
06699 member->status = get_queue_member_status(member);
06700 return 0;
06701 } else {
06702 return CMP_MATCH;
06703 }
06704 }
06705
06706
06707
06708
06709
06710
06711
06712
06713
06714
06715
06716
06717 static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
06718 {
06719 int new;
06720 struct call_queue *q = NULL;
06721
06722 struct call_queue tmpq = {
06723 .name = queuename,
06724 };
06725 const char *tmpvar;
06726 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06727 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06728 int prev_weight = 0;
06729 struct ast_variable *var;
06730 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06731 if (queue_reload) {
06732
06733 if (!(q = alloc_queue(queuename))) {
06734 return;
06735 }
06736 } else {
06737
06738
06739
06740 return;
06741 }
06742 new = 1;
06743 } else {
06744 new = 0;
06745 }
06746
06747 if (!new) {
06748 ao2_lock(q);
06749 prev_weight = q->weight ? 1 : 0;
06750 }
06751
06752 if (q->found) {
06753 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
06754 if (!new) {
06755
06756 ao2_unlock(q);
06757 }
06758 queue_t_unref(q, "We exist! Expiring temporary pointer");
06759 return;
06760 }
06761
06762
06763
06764
06765
06766 if (queue_reload) {
06767 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
06768 q->strategy = strat2int(tmpvar);
06769 if (q->strategy < 0) {
06770 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
06771 tmpvar, q->name);
06772 q->strategy = QUEUE_STRATEGY_RINGALL;
06773 }
06774 } else {
06775 q->strategy = QUEUE_STRATEGY_RINGALL;
06776 }
06777 init_queue(q);
06778 }
06779 if (member_reload) {
06780 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
06781 }
06782 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
06783 if (member_reload && !strcasecmp(var->name, "member")) {
06784 reload_single_member(var->value, q);
06785 } else if (queue_reload) {
06786 queue_set_param(q, var->name, var->value, var->lineno, 1);
06787 }
06788 }
06789
06790
06791
06792 if (!q->weight && prev_weight) {
06793 ast_atomic_fetchadd_int(&use_weight, -1);
06794 }
06795 else if (q->weight && !prev_weight) {
06796 ast_atomic_fetchadd_int(&use_weight, +1);
06797 }
06798
06799
06800 if (member_reload) {
06801 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
06802 }
06803
06804 if (new) {
06805 queues_t_link(queues, q, "Add queue to container");
06806 } else {
06807 ao2_unlock(q);
06808 }
06809 queue_t_unref(q, "Expiring creation reference");
06810 }
06811
06812 static int mark_dead_and_unfound(void *obj, void *arg, int flags)
06813 {
06814 struct call_queue *q = obj;
06815 char *queuename = arg;
06816 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
06817 q->dead = 1;
06818 q->found = 0;
06819 }
06820 return 0;
06821 }
06822
06823 static int kill_dead_queues(void *obj, void *arg, int flags)
06824 {
06825 struct call_queue *q = obj;
06826 char *queuename = arg;
06827 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
06828 return CMP_MATCH;
06829 } else {
06830 return 0;
06831 }
06832 }
06833
06834
06835
06836
06837
06838
06839
06840
06841
06842
06843
06844
06845
06846 static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
06847 {
06848 struct ast_config *cfg;
06849 char *cat;
06850 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06851 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06852
06853 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
06854 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
06855 return -1;
06856 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06857 return 0;
06858 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06859 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
06860 return -1;
06861 }
06862
06863
06864 ao2_lock(queues);
06865
06866
06867
06868
06869 if (queue_reload) {
06870 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
06871 }
06872
06873
06874 cat = NULL;
06875 while ((cat = ast_category_browse(cfg, cat)) ) {
06876 if (!strcasecmp(cat, "general") && queue_reload) {
06877 queue_set_global_params(cfg);
06878 continue;
06879 }
06880 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
06881 reload_single_queue(cfg, mask, cat);
06882 }
06883
06884 ast_config_destroy(cfg);
06885
06886 if (queue_reload) {
06887 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06888 }
06889 ao2_unlock(queues);
06890 return 0;
06891 }
06892
06893
06894
06895
06896
06897
06898
06899
06900
06901
06902
06903
06904
06905
06906 static int clear_stats(const char *queuename)
06907 {
06908 struct call_queue *q;
06909 struct ao2_iterator queue_iter;
06910
06911 queue_iter = ao2_iterator_init(queues, 0);
06912 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06913 ao2_lock(q);
06914 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06915 clear_queue(q);
06916 ao2_unlock(q);
06917 queue_t_unref(q, "Done with iterator");
06918 }
06919 ao2_iterator_destroy(&queue_iter);
06920 return 0;
06921 }
06922
06923
06924
06925
06926
06927
06928
06929
06930
06931
06932
06933
06934
06935
06936 static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
06937 {
06938 int res = 0;
06939
06940 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06941 res |= reload_queue_rules(reload);
06942 }
06943 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06944 res |= clear_stats(queuename);
06945 }
06946 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06947 res |= reload_queues(reload, mask, queuename);
06948 }
06949 return res;
06950 }
06951
06952
06953 static void do_print(struct mansession *s, int fd, const char *str)
06954 {
06955 if (s)
06956 astman_append(s, "%s\r\n", str);
06957 else
06958 ast_cli(fd, "%s\n", str);
06959 }
06960
06961
06962
06963
06964
06965
06966
06967 static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
06968 {
06969 struct call_queue *q;
06970 struct ast_str *out = ast_str_alloca(240);
06971 int found = 0;
06972 time_t now = time(NULL);
06973 struct ao2_iterator queue_iter;
06974 struct ao2_iterator mem_iter;
06975
06976 if (argc != 2 && argc != 3)
06977 return CLI_SHOWUSAGE;
06978
06979 if (argc == 3) {
06980 if ((q = load_realtime_queue(argv[2]))) {
06981 queue_t_unref(q, "Done with temporary pointer");
06982 }
06983 } else if (ast_check_realtime("queues")) {
06984
06985
06986
06987 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06988 char *queuename;
06989 if (cfg) {
06990 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06991 if ((q = load_realtime_queue(queuename))) {
06992 queue_t_unref(q, "Done with temporary pointer");
06993 }
06994 }
06995 ast_config_destroy(cfg);
06996 }
06997 }
06998
06999 ao2_lock(queues);
07000 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
07001 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07002 float sl;
07003 struct call_queue *realtime_queue = NULL;
07004
07005 ao2_lock(q);
07006
07007
07008
07009
07010 if (q->realtime) {
07011 realtime_queue = load_realtime_queue(q->name);
07012 if (!realtime_queue) {
07013 ao2_unlock(q);
07014 queue_t_unref(q, "Done with iterator");
07015 continue;
07016 }
07017 queue_t_unref(realtime_queue, "Queue is already in memory");
07018 }
07019
07020 if (argc == 3 && strcasecmp(q->name, argv[2])) {
07021 ao2_unlock(q);
07022 queue_t_unref(q, "Done with iterator");
07023 continue;
07024 }
07025 found = 1;
07026
07027 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
07028 if (q->maxlen)
07029 ast_str_append(&out, 0, "%d", q->maxlen);
07030 else
07031 ast_str_append(&out, 0, "unlimited");
07032 sl = 0;
07033 if (q->callscompleted > 0)
07034 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
07035 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
07036 int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
07037 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
07038 do_print(s, fd, ast_str_buffer(out));
07039 if (!ao2_container_count(q->members))
07040 do_print(s, fd, " No Members");
07041 else {
07042 struct member *mem;
07043
07044 do_print(s, fd, " Members: ");
07045 mem_iter = ao2_iterator_init(q->members, 0);
07046 while ((mem = ao2_iterator_next(&mem_iter))) {
07047 ast_str_set(&out, 0, " %s", mem->membername);
07048 if (strcasecmp(mem->membername, mem->interface)) {
07049 ast_str_append(&out, 0, " (%s)", mem->interface);
07050 }
07051 if (mem->penalty)
07052 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
07053 ast_str_append(&out, 0, "%s%s%s (%s)",
07054 mem->dynamic ? " (dynamic)" : "",
07055 mem->realtime ? " (realtime)" : "",
07056 mem->paused ? " (paused)" : "",
07057 ast_devstate2str(mem->status));
07058 if (mem->calls)
07059 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
07060 mem->calls, (long) (time(NULL) - mem->lastcall));
07061 else
07062 ast_str_append(&out, 0, " has taken no calls yet");
07063 do_print(s, fd, ast_str_buffer(out));
07064 ao2_ref(mem, -1);
07065 }
07066 ao2_iterator_destroy(&mem_iter);
07067 }
07068 if (!q->head)
07069 do_print(s, fd, " No Callers");
07070 else {
07071 struct queue_ent *qe;
07072 int pos = 1;
07073
07074 do_print(s, fd, " Callers: ");
07075 for (qe = q->head; qe; qe = qe->next) {
07076 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
07077 pos++, qe->chan->name, (long) (now - qe->start) / 60,
07078 (long) (now - qe->start) % 60, qe->prio);
07079 do_print(s, fd, ast_str_buffer(out));
07080 }
07081 }
07082 do_print(s, fd, "");
07083 ao2_unlock(q);
07084 queue_t_unref(q, "Done with iterator");
07085 }
07086 ao2_iterator_destroy(&queue_iter);
07087 ao2_unlock(queues);
07088 if (!found) {
07089 if (argc == 3)
07090 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07091 else
07092 ast_str_set(&out, 0, "No queues.");
07093 do_print(s, fd, ast_str_buffer(out));
07094 }
07095 return CLI_SUCCESS;
07096 }
07097
07098 static char *complete_queue(const char *line, const char *word, int pos, int state)
07099 {
07100 struct call_queue *q;
07101 char *ret = NULL;
07102 int which = 0;
07103 int wordlen = strlen(word);
07104 struct ao2_iterator queue_iter;
07105
07106 queue_iter = ao2_iterator_init(queues, 0);
07107 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07108 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
07109 ret = ast_strdup(q->name);
07110 queue_t_unref(q, "Done with iterator");
07111 break;
07112 }
07113 queue_t_unref(q, "Done with iterator");
07114 }
07115 ao2_iterator_destroy(&queue_iter);
07116
07117 return ret;
07118 }
07119
07120 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
07121 {
07122 if (pos == 2)
07123 return complete_queue(line, word, pos, state);
07124 return NULL;
07125 }
07126
07127 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07128 {
07129 switch ( cmd ) {
07130 case CLI_INIT:
07131 e->command = "queue show";
07132 e->usage =
07133 "Usage: queue show\n"
07134 " Provides summary information on a specified queue.\n";
07135 return NULL;
07136 case CLI_GENERATE:
07137 return complete_queue_show(a->line, a->word, a->pos, a->n);
07138 }
07139
07140 return __queues_show(NULL, a->fd, a->argc, a->argv);
07141 }
07142
07143
07144
07145
07146 static int manager_queues_show(struct mansession *s, const struct message *m)
07147 {
07148 static const char * const a[] = { "queue", "show" };
07149
07150 __queues_show(s, -1, 2, a);
07151 astman_append(s, "\r\n\r\n");
07152
07153 return RESULT_SUCCESS;
07154 }
07155
07156 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
07157 {
07158 const char *rule = astman_get_header(m, "Rule");
07159 const char *id = astman_get_header(m, "ActionID");
07160 struct rule_list *rl_iter;
07161 struct penalty_rule *pr_iter;
07162
07163 astman_append(s, "Response: Success\r\n");
07164 if (!ast_strlen_zero(id)) {
07165 astman_append(s, "ActionID: %s\r\n", id);
07166 }
07167
07168 AST_LIST_LOCK(&rule_lists);
07169 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07170 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07171 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07172 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07173 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 );
07174 }
07175 if (!ast_strlen_zero(rule))
07176 break;
07177 }
07178 }
07179 AST_LIST_UNLOCK(&rule_lists);
07180
07181
07182
07183
07184
07185 astman_append(s, "\r\n\r\n");
07186
07187 return RESULT_SUCCESS;
07188 }
07189
07190
07191 static int manager_queues_summary(struct mansession *s, const struct message *m)
07192 {
07193 time_t now;
07194 int qmemcount = 0;
07195 int qmemavail = 0;
07196 int qchancount = 0;
07197 int qlongestholdtime = 0;
07198 const char *id = astman_get_header(m, "ActionID");
07199 const char *queuefilter = astman_get_header(m, "Queue");
07200 char idText[256] = "";
07201 struct call_queue *q;
07202 struct queue_ent *qe;
07203 struct member *mem;
07204 struct ao2_iterator queue_iter;
07205 struct ao2_iterator mem_iter;
07206
07207 astman_send_ack(s, m, "Queue summary will follow");
07208 time(&now);
07209 if (!ast_strlen_zero(id))
07210 snprintf(idText, 256, "ActionID: %s\r\n", id);
07211 queue_iter = ao2_iterator_init(queues, 0);
07212 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07213 ao2_lock(q);
07214
07215
07216 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07217
07218 qmemcount = 0;
07219 qmemavail = 0;
07220 qchancount = 0;
07221 qlongestholdtime = 0;
07222
07223
07224 mem_iter = ao2_iterator_init(q->members, 0);
07225 while ((mem = ao2_iterator_next(&mem_iter))) {
07226 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07227 ++qmemcount;
07228 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
07229 ++qmemavail;
07230 }
07231 }
07232 ao2_ref(mem, -1);
07233 }
07234 ao2_iterator_destroy(&mem_iter);
07235 for (qe = q->head; qe; qe = qe->next) {
07236 if ((now - qe->start) > qlongestholdtime) {
07237 qlongestholdtime = now - qe->start;
07238 }
07239 ++qchancount;
07240 }
07241 astman_append(s, "Event: QueueSummary\r\n"
07242 "Queue: %s\r\n"
07243 "LoggedIn: %d\r\n"
07244 "Available: %d\r\n"
07245 "Callers: %d\r\n"
07246 "HoldTime: %d\r\n"
07247 "TalkTime: %d\r\n"
07248 "LongestHoldTime: %d\r\n"
07249 "%s"
07250 "\r\n",
07251 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07252 }
07253 ao2_unlock(q);
07254 queue_t_unref(q, "Done with iterator");
07255 }
07256 ao2_iterator_destroy(&queue_iter);
07257 astman_append(s,
07258 "Event: QueueSummaryComplete\r\n"
07259 "%s"
07260 "\r\n", idText);
07261
07262 return RESULT_SUCCESS;
07263 }
07264
07265
07266 static int manager_queues_status(struct mansession *s, const struct message *m)
07267 {
07268 time_t now;
07269 int pos;
07270 const char *id = astman_get_header(m,"ActionID");
07271 const char *queuefilter = astman_get_header(m,"Queue");
07272 const char *memberfilter = astman_get_header(m,"Member");
07273 char idText[256] = "";
07274 struct call_queue *q;
07275 struct queue_ent *qe;
07276 float sl = 0;
07277 struct member *mem;
07278 struct ao2_iterator queue_iter;
07279 struct ao2_iterator mem_iter;
07280
07281 astman_send_ack(s, m, "Queue status will follow");
07282 time(&now);
07283 if (!ast_strlen_zero(id))
07284 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07285
07286 queue_iter = ao2_iterator_init(queues, 0);
07287 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07288 ao2_lock(q);
07289
07290
07291 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07292 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07293 astman_append(s, "Event: QueueParams\r\n"
07294 "Queue: %s\r\n"
07295 "Max: %d\r\n"
07296 "Strategy: %s\r\n"
07297 "Calls: %d\r\n"
07298 "Holdtime: %d\r\n"
07299 "TalkTime: %d\r\n"
07300 "Completed: %d\r\n"
07301 "Abandoned: %d\r\n"
07302 "ServiceLevel: %d\r\n"
07303 "ServicelevelPerf: %2.1f\r\n"
07304 "Weight: %d\r\n"
07305 "%s"
07306 "\r\n",
07307 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07308 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07309
07310 mem_iter = ao2_iterator_init(q->members, 0);
07311 while ((mem = ao2_iterator_next(&mem_iter))) {
07312 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07313 astman_append(s, "Event: QueueMember\r\n"
07314 "Queue: %s\r\n"
07315 "Name: %s\r\n"
07316 "Location: %s\r\n"
07317 "Membership: %s\r\n"
07318 "Penalty: %d\r\n"
07319 "CallsTaken: %d\r\n"
07320 "LastCall: %d\r\n"
07321 "Status: %d\r\n"
07322 "Paused: %d\r\n"
07323 "%s"
07324 "\r\n",
07325 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07326 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07327 }
07328 ao2_ref(mem, -1);
07329 }
07330 ao2_iterator_destroy(&mem_iter);
07331
07332 pos = 1;
07333 for (qe = q->head; qe; qe = qe->next) {
07334 astman_append(s, "Event: QueueEntry\r\n"
07335 "Queue: %s\r\n"
07336 "Position: %d\r\n"
07337 "Channel: %s\r\n"
07338 "Uniqueid: %s\r\n"
07339 "CallerIDNum: %s\r\n"
07340 "CallerIDName: %s\r\n"
07341 "ConnectedLineNum: %s\r\n"
07342 "ConnectedLineName: %s\r\n"
07343 "Wait: %ld\r\n"
07344 "%s"
07345 "\r\n",
07346 q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07347 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07348 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07349 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07350 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07351 (long) (now - qe->start), idText);
07352 }
07353 }
07354 ao2_unlock(q);
07355 queue_t_unref(q, "Done with iterator");
07356 }
07357 ao2_iterator_destroy(&queue_iter);
07358
07359 astman_append(s,
07360 "Event: QueueStatusComplete\r\n"
07361 "%s"
07362 "\r\n",idText);
07363
07364 return RESULT_SUCCESS;
07365 }
07366
07367 static int manager_add_queue_member(struct mansession *s, const struct message *m)
07368 {
07369 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07370 int paused, penalty = 0;
07371
07372 queuename = astman_get_header(m, "Queue");
07373 interface = astman_get_header(m, "Interface");
07374 penalty_s = astman_get_header(m, "Penalty");
07375 paused_s = astman_get_header(m, "Paused");
07376 membername = astman_get_header(m, "MemberName");
07377 state_interface = astman_get_header(m, "StateInterface");
07378
07379 if (ast_strlen_zero(queuename)) {
07380 astman_send_error(s, m, "'Queue' not specified.");
07381 return 0;
07382 }
07383
07384 if (ast_strlen_zero(interface)) {
07385 astman_send_error(s, m, "'Interface' not specified.");
07386 return 0;
07387 }
07388
07389 if (ast_strlen_zero(penalty_s))
07390 penalty = 0;
07391 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07392 penalty = 0;
07393
07394 if (ast_strlen_zero(paused_s))
07395 paused = 0;
07396 else
07397 paused = abs(ast_true(paused_s));
07398
07399 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07400 case RES_OKAY:
07401 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07402 astman_send_ack(s, m, "Added interface to queue");
07403 break;
07404 case RES_EXISTS:
07405 astman_send_error(s, m, "Unable to add interface: Already there");
07406 break;
07407 case RES_NOSUCHQUEUE:
07408 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07409 break;
07410 case RES_OUTOFMEMORY:
07411 astman_send_error(s, m, "Out of memory");
07412 break;
07413 }
07414
07415 return 0;
07416 }
07417
07418 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
07419 {
07420 const char *queuename, *interface;
07421
07422 queuename = astman_get_header(m, "Queue");
07423 interface = astman_get_header(m, "Interface");
07424
07425 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07426 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07427 return 0;
07428 }
07429
07430 switch (remove_from_queue(queuename, interface)) {
07431 case RES_OKAY:
07432 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07433 astman_send_ack(s, m, "Removed interface from queue");
07434 break;
07435 case RES_EXISTS:
07436 astman_send_error(s, m, "Unable to remove interface: Not there");
07437 break;
07438 case RES_NOSUCHQUEUE:
07439 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07440 break;
07441 case RES_OUTOFMEMORY:
07442 astman_send_error(s, m, "Out of memory");
07443 break;
07444 case RES_NOT_DYNAMIC:
07445 astman_send_error(s, m, "Member not dynamic");
07446 break;
07447 }
07448
07449 return 0;
07450 }
07451
07452 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
07453 {
07454 const char *queuename, *interface, *paused_s, *reason;
07455 int paused;
07456
07457 interface = astman_get_header(m, "Interface");
07458 paused_s = astman_get_header(m, "Paused");
07459 queuename = astman_get_header(m, "Queue");
07460 reason = astman_get_header(m, "Reason");
07461
07462 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07463 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07464 return 0;
07465 }
07466
07467 paused = abs(ast_true(paused_s));
07468
07469 if (set_member_paused(queuename, interface, reason, paused))
07470 astman_send_error(s, m, "Interface not found");
07471 else
07472 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07473 return 0;
07474 }
07475
07476 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
07477 {
07478 const char *queuename, *event, *message, *interface, *uniqueid;
07479
07480 queuename = astman_get_header(m, "Queue");
07481 uniqueid = astman_get_header(m, "UniqueId");
07482 interface = astman_get_header(m, "Interface");
07483 event = astman_get_header(m, "Event");
07484 message = astman_get_header(m, "Message");
07485
07486 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07487 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07488 return 0;
07489 }
07490
07491 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07492 astman_send_ack(s, m, "Event added successfully");
07493
07494 return 0;
07495 }
07496
07497 static int manager_queue_reload(struct mansession *s, const struct message *m)
07498 {
07499 struct ast_flags mask = {0,};
07500 const char *queuename = NULL;
07501 int header_found = 0;
07502
07503 queuename = astman_get_header(m, "Queue");
07504 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07505 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07506 header_found = 1;
07507 }
07508 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07509 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07510 header_found = 1;
07511 }
07512 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07513 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07514 header_found = 1;
07515 }
07516
07517 if (!header_found) {
07518 ast_set_flag(&mask, AST_FLAGS_ALL);
07519 }
07520
07521 if (!reload_handler(1, &mask, queuename)) {
07522 astman_send_ack(s, m, "Queue reloaded successfully");
07523 } else {
07524 astman_send_error(s, m, "Error encountered while reloading queue");
07525 }
07526 return 0;
07527 }
07528
07529 static int manager_queue_reset(struct mansession *s, const struct message *m)
07530 {
07531 const char *queuename = NULL;
07532 struct ast_flags mask = {QUEUE_RESET_STATS,};
07533
07534 queuename = astman_get_header(m, "Queue");
07535
07536 if (!reload_handler(1, &mask, queuename)) {
07537 astman_send_ack(s, m, "Queue stats reset successfully");
07538 } else {
07539 astman_send_error(s, m, "Error encountered while resetting queue stats");
07540 }
07541 return 0;
07542 }
07543
07544 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
07545 {
07546
07547 switch (pos) {
07548 case 3:
07549 return NULL;
07550 case 4:
07551 return state == 0 ? ast_strdup("to") : NULL;
07552 case 5:
07553 return complete_queue(line, word, pos, state);
07554 case 6:
07555 return state == 0 ? ast_strdup("penalty") : NULL;
07556 case 7:
07557 if (state < 100) {
07558 char *num;
07559 if ((num = ast_malloc(3))) {
07560 sprintf(num, "%d", state);
07561 }
07562 return num;
07563 } else {
07564 return NULL;
07565 }
07566 case 8:
07567 return state == 0 ? ast_strdup("as") : NULL;
07568 case 9:
07569 return NULL;
07570 default:
07571 return NULL;
07572 }
07573 }
07574
07575 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
07576 {
07577 const char *queuename, *interface, *penalty_s;
07578 int penalty;
07579
07580 interface = astman_get_header(m, "Interface");
07581 penalty_s = astman_get_header(m, "Penalty");
07582
07583 queuename = astman_get_header(m, "Queue");
07584
07585 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07586 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07587 return 0;
07588 }
07589
07590 penalty = atoi(penalty_s);
07591
07592 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07593 astman_send_error(s, m, "Invalid interface, queuename or penalty");
07594 else
07595 astman_send_ack(s, m, "Interface penalty set successfully");
07596
07597 return 0;
07598 }
07599
07600 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07601 {
07602 const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07603 int penalty;
07604
07605 switch ( cmd ) {
07606 case CLI_INIT:
07607 e->command = "queue add member";
07608 e->usage =
07609 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07610 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n";
07611 return NULL;
07612 case CLI_GENERATE:
07613 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07614 }
07615
07616 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07617 return CLI_SHOWUSAGE;
07618 } else if (strcmp(a->argv[4], "to")) {
07619 return CLI_SHOWUSAGE;
07620 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07621 return CLI_SHOWUSAGE;
07622 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07623 return CLI_SHOWUSAGE;
07624 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07625 return CLI_SHOWUSAGE;
07626 }
07627
07628 queuename = a->argv[5];
07629 interface = a->argv[3];
07630 if (a->argc >= 8) {
07631 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07632 if (penalty < 0) {
07633 ast_cli(a->fd, "Penalty must be >= 0\n");
07634 penalty = 0;
07635 }
07636 } else {
07637 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07638 penalty = 0;
07639 }
07640 } else {
07641 penalty = 0;
07642 }
07643
07644 if (a->argc >= 10) {
07645 membername = a->argv[9];
07646 }
07647
07648 if (a->argc >= 12) {
07649 state_interface = a->argv[11];
07650 }
07651
07652 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07653 case RES_OKAY:
07654 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07655 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07656 return CLI_SUCCESS;
07657 case RES_EXISTS:
07658 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07659 return CLI_FAILURE;
07660 case RES_NOSUCHQUEUE:
07661 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07662 return CLI_FAILURE;
07663 case RES_OUTOFMEMORY:
07664 ast_cli(a->fd, "Out of memory\n");
07665 return CLI_FAILURE;
07666 case RES_NOT_DYNAMIC:
07667 ast_cli(a->fd, "Member not dynamic\n");
07668 return CLI_FAILURE;
07669 default:
07670 return CLI_FAILURE;
07671 }
07672 }
07673
07674 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
07675 {
07676 int which = 0;
07677 struct call_queue *q;
07678 struct member *m;
07679 struct ao2_iterator queue_iter;
07680 struct ao2_iterator mem_iter;
07681 int wordlen = strlen(word);
07682
07683
07684 if (pos > 5 || pos < 3)
07685 return NULL;
07686 if (pos == 4)
07687 return (state == 0 ? ast_strdup("from") : NULL);
07688
07689 if (pos == 5)
07690 return complete_queue(line, word, pos, state);
07691
07692
07693 queue_iter = ao2_iterator_init(queues, 0);
07694 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07695 ao2_lock(q);
07696 mem_iter = ao2_iterator_init(q->members, 0);
07697 while ((m = ao2_iterator_next(&mem_iter))) {
07698 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07699 char *tmp;
07700 tmp = ast_strdup(m->interface);
07701 ao2_ref(m, -1);
07702 ao2_iterator_destroy(&mem_iter);
07703 ao2_unlock(q);
07704 queue_t_unref(q, "Done with iterator, returning interface name");
07705 ao2_iterator_destroy(&queue_iter);
07706 return tmp;
07707 }
07708 ao2_ref(m, -1);
07709 }
07710 ao2_iterator_destroy(&mem_iter);
07711 ao2_unlock(q);
07712 queue_t_unref(q, "Done with iterator");
07713 }
07714 ao2_iterator_destroy(&queue_iter);
07715
07716 return NULL;
07717 }
07718
07719 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07720 {
07721 const char *queuename, *interface;
07722
07723 switch (cmd) {
07724 case CLI_INIT:
07725 e->command = "queue remove member";
07726 e->usage =
07727 "Usage: queue remove member <channel> from <queue>\n"
07728 " Remove a specific channel from a queue.\n";
07729 return NULL;
07730 case CLI_GENERATE:
07731 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
07732 }
07733
07734 if (a->argc != 6) {
07735 return CLI_SHOWUSAGE;
07736 } else if (strcmp(a->argv[4], "from")) {
07737 return CLI_SHOWUSAGE;
07738 }
07739
07740 queuename = a->argv[5];
07741 interface = a->argv[3];
07742
07743 switch (remove_from_queue(queuename, interface)) {
07744 case RES_OKAY:
07745 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
07746 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
07747 return CLI_SUCCESS;
07748 case RES_EXISTS:
07749 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
07750 return CLI_FAILURE;
07751 case RES_NOSUCHQUEUE:
07752 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
07753 return CLI_FAILURE;
07754 case RES_OUTOFMEMORY:
07755 ast_cli(a->fd, "Out of memory\n");
07756 return CLI_FAILURE;
07757 case RES_NOT_DYNAMIC:
07758 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
07759 return CLI_FAILURE;
07760 default:
07761 return CLI_FAILURE;
07762 }
07763 }
07764
07765 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
07766 {
07767
07768 switch (pos) {
07769 case 3:
07770 return NULL;
07771 case 4:
07772 return state == 0 ? ast_strdup("queue") : NULL;
07773 case 5:
07774 return complete_queue(line, word, pos, state);
07775 case 6:
07776 return state == 0 ? ast_strdup("reason") : NULL;
07777 case 7:
07778 return NULL;
07779 default:
07780 return NULL;
07781 }
07782 }
07783
07784 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07785 {
07786 const char *queuename, *interface, *reason;
07787 int paused;
07788
07789 switch (cmd) {
07790 case CLI_INIT:
07791 e->command = "queue {pause|unpause} member";
07792 e->usage =
07793 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
07794 " Pause or unpause a queue member. Not specifying a particular queue\n"
07795 " will pause or unpause a member across all queues to which the member\n"
07796 " belongs.\n";
07797 return NULL;
07798 case CLI_GENERATE:
07799 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
07800 }
07801
07802 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
07803 return CLI_SHOWUSAGE;
07804 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
07805 return CLI_SHOWUSAGE;
07806 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
07807 return CLI_SHOWUSAGE;
07808 }
07809
07810
07811 interface = a->argv[3];
07812 queuename = a->argc >= 6 ? a->argv[5] : NULL;
07813 reason = a->argc == 8 ? a->argv[7] : NULL;
07814 paused = !strcasecmp(a->argv[1], "pause");
07815
07816 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
07817 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
07818 if (!ast_strlen_zero(queuename))
07819 ast_cli(a->fd, " in queue '%s'", queuename);
07820 if (!ast_strlen_zero(reason))
07821 ast_cli(a->fd, " for reason '%s'", reason);
07822 ast_cli(a->fd, "\n");
07823 return CLI_SUCCESS;
07824 } else {
07825 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
07826 if (!ast_strlen_zero(queuename))
07827 ast_cli(a->fd, " in queue '%s'", queuename);
07828 if (!ast_strlen_zero(reason))
07829 ast_cli(a->fd, " for reason '%s'", reason);
07830 ast_cli(a->fd, "\n");
07831 return CLI_FAILURE;
07832 }
07833 }
07834
07835 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
07836 {
07837
07838 switch (pos) {
07839 case 4:
07840 if (state == 0) {
07841 return ast_strdup("on");
07842 } else {
07843 return NULL;
07844 }
07845 case 6:
07846 if (state == 0) {
07847 return ast_strdup("in");
07848 } else {
07849 return NULL;
07850 }
07851 case 7:
07852 return complete_queue(line, word, pos, state);
07853 default:
07854 return NULL;
07855 }
07856 }
07857
07858 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07859 {
07860 const char *queuename = NULL, *interface;
07861 int penalty = 0;
07862
07863 switch (cmd) {
07864 case CLI_INIT:
07865 e->command = "queue set penalty";
07866 e->usage =
07867 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
07868 " Set a member's penalty in the queue specified. If no queue is specified\n"
07869 " then that interface's penalty is set in all queues to which that interface is a member\n";
07870 return NULL;
07871 case CLI_GENERATE:
07872 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
07873 }
07874
07875 if (a->argc != 6 && a->argc != 8) {
07876 return CLI_SHOWUSAGE;
07877 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
07878 return CLI_SHOWUSAGE;
07879 }
07880
07881 if (a->argc == 8)
07882 queuename = a->argv[7];
07883 interface = a->argv[5];
07884 penalty = atoi(a->argv[3]);
07885
07886 switch (set_member_penalty(queuename, interface, penalty)) {
07887 case RESULT_SUCCESS:
07888 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07889 return CLI_SUCCESS;
07890 case RESULT_FAILURE:
07891 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07892 return CLI_FAILURE;
07893 default:
07894 return CLI_FAILURE;
07895 }
07896 }
07897
07898 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
07899 {
07900 int which = 0;
07901 struct rule_list *rl_iter;
07902 int wordlen = strlen(word);
07903 char *ret = NULL;
07904 if (pos != 3) {
07905 return NULL;
07906 }
07907
07908 AST_LIST_LOCK(&rule_lists);
07909 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07910 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07911 ret = ast_strdup(rl_iter->name);
07912 break;
07913 }
07914 }
07915 AST_LIST_UNLOCK(&rule_lists);
07916
07917 return ret;
07918 }
07919
07920 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07921 {
07922 const char *rule;
07923 struct rule_list *rl_iter;
07924 struct penalty_rule *pr_iter;
07925 switch (cmd) {
07926 case CLI_INIT:
07927 e->command = "queue show rules";
07928 e->usage =
07929 "Usage: queue show rules [rulename]\n"
07930 " Show the list of rules associated with rulename. If no\n"
07931 " rulename is specified, list all rules defined in queuerules.conf\n";
07932 return NULL;
07933 case CLI_GENERATE:
07934 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07935 }
07936
07937 if (a->argc != 3 && a->argc != 4)
07938 return CLI_SHOWUSAGE;
07939
07940 rule = a->argc == 4 ? a->argv[3] : "";
07941 AST_LIST_LOCK(&rule_lists);
07942 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07943 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07944 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07945 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07946 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);
07947 }
07948 }
07949 }
07950 AST_LIST_UNLOCK(&rule_lists);
07951 return CLI_SUCCESS;
07952 }
07953
07954 static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07955 {
07956 struct ast_flags mask = {QUEUE_RESET_STATS,};
07957 int i;
07958
07959 switch (cmd) {
07960 case CLI_INIT:
07961 e->command = "queue reset stats";
07962 e->usage =
07963 "Usage: queue reset stats [<queuenames>]\n"
07964 "\n"
07965 "Issuing this command will reset statistics for\n"
07966 "<queuenames>, or for all queues if no queue is\n"
07967 "specified.\n";
07968 return NULL;
07969 case CLI_GENERATE:
07970 if (a->pos >= 3) {
07971 return complete_queue(a->line, a->word, a->pos, a->n);
07972 } else {
07973 return NULL;
07974 }
07975 }
07976
07977 if (a->argc < 3) {
07978 return CLI_SHOWUSAGE;
07979 }
07980
07981 if (a->argc == 3) {
07982 reload_handler(1, &mask, NULL);
07983 return CLI_SUCCESS;
07984 }
07985
07986 for (i = 3; i < a->argc; ++i) {
07987 reload_handler(1, &mask, a->argv[i]);
07988 }
07989
07990 return CLI_SUCCESS;
07991 }
07992
07993 static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07994 {
07995 struct ast_flags mask = {0,};
07996 int i;
07997
07998 switch (cmd) {
07999 case CLI_INIT:
08000 e->command = "queue reload {parameters|members|rules|all}";
08001 e->usage =
08002 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08003 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08004 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08005 "specified in order to know what information to reload. Below is an explanation\n"
08006 "of each of these qualifiers.\n"
08007 "\n"
08008 "\t'members' - reload queue members from queues.conf\n"
08009 "\t'parameters' - reload all queue options except for queue members\n"
08010 "\t'rules' - reload the queuerules.conf file\n"
08011 "\t'all' - reload queue rules, parameters, and members\n"
08012 "\n"
08013 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08014 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08015 "one queue is specified when using this command, reloading queue rules may cause\n"
08016 "other queues to be affected\n";
08017 return NULL;
08018 case CLI_GENERATE:
08019 if (a->pos >= 3) {
08020 return complete_queue(a->line, a->word, a->pos, a->n);
08021 } else {
08022 return NULL;
08023 }
08024 }
08025
08026 if (a->argc < 3)
08027 return CLI_SHOWUSAGE;
08028
08029 if (!strcasecmp(a->argv[2], "rules")) {
08030 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08031 } else if (!strcasecmp(a->argv[2], "members")) {
08032 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08033 } else if (!strcasecmp(a->argv[2], "parameters")) {
08034 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08035 } else if (!strcasecmp(a->argv[2], "all")) {
08036 ast_set_flag(&mask, AST_FLAGS_ALL);
08037 }
08038
08039 if (a->argc == 3) {
08040 reload_handler(1, &mask, NULL);
08041 return CLI_SUCCESS;
08042 }
08043
08044 for (i = 3; i < a->argc; ++i) {
08045 reload_handler(1, &mask, a->argv[i]);
08046 }
08047
08048 return CLI_SUCCESS;
08049 }
08050
08051 static const char qpm_cmd_usage[] =
08052 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
08053
08054 static const char qum_cmd_usage[] =
08055 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
08056
08057 static const char qsmp_cmd_usage[] =
08058 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
08059
08060 static struct ast_cli_entry cli_queue[] = {
08061 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
08062 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
08063 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
08064 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
08065 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
08066 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
08067 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
08068 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
08069 };
08070
08071
08072 #define DATA_EXPORT_CALL_QUEUE(MEMBER) \
08073 MEMBER(call_queue, name, AST_DATA_STRING) \
08074 MEMBER(call_queue, moh, AST_DATA_STRING) \
08075 MEMBER(call_queue, announce, AST_DATA_STRING) \
08076 MEMBER(call_queue, context, AST_DATA_STRING) \
08077 MEMBER(call_queue, membermacro, AST_DATA_STRING) \
08078 MEMBER(call_queue, membergosub, AST_DATA_STRING) \
08079 MEMBER(call_queue, defaultrule, AST_DATA_STRING) \
08080 MEMBER(call_queue, sound_next, AST_DATA_STRING) \
08081 MEMBER(call_queue, sound_thereare, AST_DATA_STRING) \
08082 MEMBER(call_queue, sound_calls, AST_DATA_STRING) \
08083 MEMBER(call_queue, queue_quantity1, AST_DATA_STRING) \
08084 MEMBER(call_queue, queue_quantity2, AST_DATA_STRING) \
08085 MEMBER(call_queue, sound_holdtime, AST_DATA_STRING) \
08086 MEMBER(call_queue, sound_minutes, AST_DATA_STRING) \
08087 MEMBER(call_queue, sound_minute, AST_DATA_STRING) \
08088 MEMBER(call_queue, sound_seconds, AST_DATA_STRING) \
08089 MEMBER(call_queue, sound_thanks, AST_DATA_STRING) \
08090 MEMBER(call_queue, sound_callerannounce, AST_DATA_STRING) \
08091 MEMBER(call_queue, sound_reporthold, AST_DATA_STRING) \
08092 MEMBER(call_queue, dead, AST_DATA_BOOLEAN) \
08093 MEMBER(call_queue, eventwhencalled, AST_DATA_BOOLEAN) \
08094 MEMBER(call_queue, ringinuse, AST_DATA_BOOLEAN) \
08095 MEMBER(call_queue, setinterfacevar, AST_DATA_BOOLEAN) \
08096 MEMBER(call_queue, setqueuevar, AST_DATA_BOOLEAN) \
08097 MEMBER(call_queue, setqueueentryvar, AST_DATA_BOOLEAN) \
08098 MEMBER(call_queue, reportholdtime, AST_DATA_BOOLEAN) \
08099 MEMBER(call_queue, wrapped, AST_DATA_BOOLEAN) \
08100 MEMBER(call_queue, timeoutrestart, AST_DATA_BOOLEAN) \
08101 MEMBER(call_queue, announceholdtime, AST_DATA_INTEGER) \
08102 MEMBER(call_queue, maskmemberstatus, AST_DATA_BOOLEAN) \
08103 MEMBER(call_queue, realtime, AST_DATA_BOOLEAN) \
08104 MEMBER(call_queue, found, AST_DATA_BOOLEAN) \
08105 MEMBER(call_queue, announcepositionlimit, AST_DATA_INTEGER) \
08106 MEMBER(call_queue, announcefrequency, AST_DATA_SECONDS) \
08107 MEMBER(call_queue, minannouncefrequency, AST_DATA_SECONDS) \
08108 MEMBER(call_queue, periodicannouncefrequency, AST_DATA_SECONDS) \
08109 MEMBER(call_queue, numperiodicannounce, AST_DATA_INTEGER) \
08110 MEMBER(call_queue, randomperiodicannounce, AST_DATA_INTEGER) \
08111 MEMBER(call_queue, roundingseconds, AST_DATA_SECONDS) \
08112 MEMBER(call_queue, holdtime, AST_DATA_SECONDS) \
08113 MEMBER(call_queue, talktime, AST_DATA_SECONDS) \
08114 MEMBER(call_queue, callscompleted, AST_DATA_INTEGER) \
08115 MEMBER(call_queue, callsabandoned, AST_DATA_INTEGER) \
08116 MEMBER(call_queue, servicelevel, AST_DATA_INTEGER) \
08117 MEMBER(call_queue, callscompletedinsl, AST_DATA_INTEGER) \
08118 MEMBER(call_queue, monfmt, AST_DATA_STRING) \
08119 MEMBER(call_queue, montype, AST_DATA_INTEGER) \
08120 MEMBER(call_queue, count, AST_DATA_INTEGER) \
08121 MEMBER(call_queue, maxlen, AST_DATA_INTEGER) \
08122 MEMBER(call_queue, wrapuptime, AST_DATA_SECONDS) \
08123 MEMBER(call_queue, retry, AST_DATA_SECONDS) \
08124 MEMBER(call_queue, timeout, AST_DATA_SECONDS) \
08125 MEMBER(call_queue, weight, AST_DATA_INTEGER) \
08126 MEMBER(call_queue, autopause, AST_DATA_INTEGER) \
08127 MEMBER(call_queue, timeoutpriority, AST_DATA_INTEGER) \
08128 MEMBER(call_queue, rrpos, AST_DATA_INTEGER) \
08129 MEMBER(call_queue, memberdelay, AST_DATA_INTEGER) \
08130 MEMBER(call_queue, autofill, AST_DATA_INTEGER) \
08131 MEMBER(call_queue, members, AST_DATA_CONTAINER)
08132
08133 AST_DATA_STRUCTURE(call_queue, DATA_EXPORT_CALL_QUEUE);
08134
08135
08136 #define DATA_EXPORT_MEMBER(MEMBER) \
08137 MEMBER(member, interface, AST_DATA_STRING) \
08138 MEMBER(member, state_interface, AST_DATA_STRING) \
08139 MEMBER(member, membername, AST_DATA_STRING) \
08140 MEMBER(member, penalty, AST_DATA_INTEGER) \
08141 MEMBER(member, calls, AST_DATA_INTEGER) \
08142 MEMBER(member, dynamic, AST_DATA_INTEGER) \
08143 MEMBER(member, realtime, AST_DATA_INTEGER) \
08144 MEMBER(member, status, AST_DATA_INTEGER) \
08145 MEMBER(member, paused, AST_DATA_BOOLEAN) \
08146 MEMBER(member, rt_uniqueid, AST_DATA_STRING)
08147
08148 AST_DATA_STRUCTURE(member, DATA_EXPORT_MEMBER);
08149
08150 #define DATA_EXPORT_QUEUE_ENT(MEMBER) \
08151 MEMBER(queue_ent, moh, AST_DATA_STRING) \
08152 MEMBER(queue_ent, announce, AST_DATA_STRING) \
08153 MEMBER(queue_ent, context, AST_DATA_STRING) \
08154 MEMBER(queue_ent, digits, AST_DATA_STRING) \
08155 MEMBER(queue_ent, valid_digits, AST_DATA_INTEGER) \
08156 MEMBER(queue_ent, pos, AST_DATA_INTEGER) \
08157 MEMBER(queue_ent, prio, AST_DATA_INTEGER) \
08158 MEMBER(queue_ent, last_pos_said, AST_DATA_INTEGER) \
08159 MEMBER(queue_ent, last_periodic_announce_time, AST_DATA_INTEGER) \
08160 MEMBER(queue_ent, last_periodic_announce_sound, AST_DATA_INTEGER) \
08161 MEMBER(queue_ent, last_pos, AST_DATA_INTEGER) \
08162 MEMBER(queue_ent, opos, AST_DATA_INTEGER) \
08163 MEMBER(queue_ent, handled, AST_DATA_INTEGER) \
08164 MEMBER(queue_ent, pending, AST_DATA_INTEGER) \
08165 MEMBER(queue_ent, max_penalty, AST_DATA_INTEGER) \
08166 MEMBER(queue_ent, min_penalty, AST_DATA_INTEGER) \
08167 MEMBER(queue_ent, linpos, AST_DATA_INTEGER) \
08168 MEMBER(queue_ent, linwrapped, AST_DATA_INTEGER) \
08169 MEMBER(queue_ent, start, AST_DATA_INTEGER) \
08170 MEMBER(queue_ent, expire, AST_DATA_INTEGER) \
08171 MEMBER(queue_ent, cancel_answered_elsewhere, AST_DATA_INTEGER)
08172
08173 AST_DATA_STRUCTURE(queue_ent, DATA_EXPORT_QUEUE_ENT);
08174
08175
08176
08177
08178
08179
08180
08181
08182 static void queues_data_provider_get_helper(const struct ast_data_search *search,
08183 struct ast_data *data_root, struct call_queue *queue)
08184 {
08185 struct ao2_iterator im;
08186 struct member *member;
08187 struct queue_ent *qe;
08188 struct ast_data *data_queue, *data_members = NULL, *enum_node;
08189 struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08190
08191 data_queue = ast_data_add_node(data_root, "queue");
08192 if (!data_queue) {
08193 return;
08194 }
08195
08196 ast_data_add_structure(call_queue, data_queue, queue);
08197
08198 ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08199 ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08200
08201
08202 enum_node = ast_data_add_node(data_queue, "announceposition");
08203 if (!enum_node) {
08204 return;
08205 }
08206 switch (queue->announceposition) {
08207 case ANNOUNCEPOSITION_LIMIT:
08208 ast_data_add_str(enum_node, "text", "limit");
08209 break;
08210 case ANNOUNCEPOSITION_MORE_THAN:
08211 ast_data_add_str(enum_node, "text", "more");
08212 break;
08213 case ANNOUNCEPOSITION_YES:
08214 ast_data_add_str(enum_node, "text", "yes");
08215 break;
08216 case ANNOUNCEPOSITION_NO:
08217 ast_data_add_str(enum_node, "text", "no");
08218 break;
08219 default:
08220 ast_data_add_str(enum_node, "text", "unknown");
08221 break;
08222 }
08223 ast_data_add_int(enum_node, "value", queue->announceposition);
08224
08225
08226 im = ao2_iterator_init(queue->members, 0);
08227 while ((member = ao2_iterator_next(&im))) {
08228 if (!data_members) {
08229 data_members = ast_data_add_node(data_queue, "members");
08230 if (!data_members) {
08231 ao2_ref(member, -1);
08232 continue;
08233 }
08234 }
08235
08236 data_member = ast_data_add_node(data_members, "member");
08237 if (!data_member) {
08238 ao2_ref(member, -1);
08239 continue;
08240 }
08241
08242 ast_data_add_structure(member, data_member, member);
08243
08244 ao2_ref(member, -1);
08245 }
08246 ao2_iterator_destroy(&im);
08247
08248
08249 if (queue->head) {
08250 for (qe = queue->head; qe; qe = qe->next) {
08251 if (!data_callers) {
08252 data_callers = ast_data_add_node(data_queue, "callers");
08253 if (!data_callers) {
08254 continue;
08255 }
08256 }
08257
08258 data_caller = ast_data_add_node(data_callers, "caller");
08259 if (!data_caller) {
08260 continue;
08261 }
08262
08263 ast_data_add_structure(queue_ent, data_caller, qe);
08264
08265
08266 data_caller_channel = ast_data_add_node(data_caller, "channel");
08267 if (!data_caller_channel) {
08268 continue;
08269 }
08270
08271 ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08272 }
08273 }
08274
08275
08276 if (!ast_data_search_match(search, data_queue)) {
08277 ast_data_remove_node(data_root, data_queue);
08278 }
08279 }
08280
08281
08282
08283
08284
08285
08286
08287
08288 static int queues_data_provider_get(const struct ast_data_search *search,
08289 struct ast_data *data_root)
08290 {
08291 struct ao2_iterator i;
08292 struct call_queue *queue, *queue_realtime = NULL;
08293 struct ast_config *cfg;
08294 char *queuename;
08295
08296
08297 cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08298 if (cfg) {
08299 for (queuename = ast_category_browse(cfg, NULL);
08300 !ast_strlen_zero(queuename);
08301 queuename = ast_category_browse(cfg, queuename)) {
08302 if ((queue = load_realtime_queue(queuename))) {
08303 queue_unref(queue);
08304 }
08305 }
08306 ast_config_destroy(cfg);
08307 }
08308
08309
08310 i = ao2_iterator_init(queues, 0);
08311 while ((queue = ao2_iterator_next(&i))) {
08312 ao2_lock(queue);
08313 if (queue->realtime) {
08314 queue_realtime = load_realtime_queue(queue->name);
08315 if (!queue_realtime) {
08316 ao2_unlock(queue);
08317 queue_unref(queue);
08318 continue;
08319 }
08320 queue_unref(queue_realtime);
08321 }
08322
08323 queues_data_provider_get_helper(search, data_root, queue);
08324 ao2_unlock(queue);
08325 queue_unref(queue);
08326 }
08327 ao2_iterator_destroy(&i);
08328
08329 return 0;
08330 }
08331
08332 static const struct ast_data_handler queues_data_provider = {
08333 .version = AST_DATA_HANDLER_VERSION,
08334 .get = queues_data_provider_get
08335 };
08336
08337 static const struct ast_data_entry queue_data_providers[] = {
08338 AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider),
08339 };
08340
08341 static int unload_module(void)
08342 {
08343 int res;
08344 struct ast_context *con;
08345 struct ao2_iterator q_iter;
08346 struct call_queue *q = NULL;
08347
08348 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08349 res = ast_manager_unregister("QueueStatus");
08350 res |= ast_manager_unregister("Queues");
08351 res |= ast_manager_unregister("QueueRule");
08352 res |= ast_manager_unregister("QueueSummary");
08353 res |= ast_manager_unregister("QueueAdd");
08354 res |= ast_manager_unregister("QueueRemove");
08355 res |= ast_manager_unregister("QueuePause");
08356 res |= ast_manager_unregister("QueueLog");
08357 res |= ast_manager_unregister("QueuePenalty");
08358 res |= ast_unregister_application(app_aqm);
08359 res |= ast_unregister_application(app_rqm);
08360 res |= ast_unregister_application(app_pqm);
08361 res |= ast_unregister_application(app_upqm);
08362 res |= ast_unregister_application(app_ql);
08363 res |= ast_unregister_application(app);
08364 res |= ast_custom_function_unregister(&queueexists_function);
08365 res |= ast_custom_function_unregister(&queuevar_function);
08366 res |= ast_custom_function_unregister(&queuemembercount_function);
08367 res |= ast_custom_function_unregister(&queuemembercount_dep);
08368 res |= ast_custom_function_unregister(&queuememberlist_function);
08369 res |= ast_custom_function_unregister(&queuewaitingcount_function);
08370 res |= ast_custom_function_unregister(&queuememberpenalty_function);
08371
08372 res |= ast_data_unregister(NULL);
08373
08374 if (device_state_sub)
08375 ast_event_unsubscribe(device_state_sub);
08376
08377 ast_extension_state_del(0, extension_state_cb);
08378
08379 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08380 ast_context_remove_extension2(con, "s", 1, NULL, 0);
08381 ast_context_destroy(con, "app_queue");
08382 }
08383
08384 q_iter = ao2_iterator_init(queues, 0);
08385 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08386 queues_t_unlink(queues, q, "Remove queue from container due to unload");
08387 queue_t_unref(q, "Done with iterator");
08388 }
08389 ao2_iterator_destroy(&q_iter);
08390 ao2_ref(queues, -1);
08391 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08392 ast_unload_realtime("queue_members");
08393 return res;
08394 }
08395
08396 static int load_module(void)
08397 {
08398 int res;
08399 struct ast_context *con;
08400 struct ast_flags mask = {AST_FLAGS_ALL, };
08401
08402 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08403
08404 use_weight = 0;
08405
08406 if (reload_handler(0, &mask, NULL))
08407 return AST_MODULE_LOAD_DECLINE;
08408
08409 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08410 if (!con)
08411 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08412 else
08413 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08414
08415 if (queue_persistent_members)
08416 reload_queue_members();
08417
08418 ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08419
08420 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08421 res = ast_register_application_xml(app, queue_exec);
08422 res |= ast_register_application_xml(app_aqm, aqm_exec);
08423 res |= ast_register_application_xml(app_rqm, rqm_exec);
08424 res |= ast_register_application_xml(app_pqm, pqm_exec);
08425 res |= ast_register_application_xml(app_upqm, upqm_exec);
08426 res |= ast_register_application_xml(app_ql, ql_exec);
08427 res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08428 res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08429 res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08430 res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08431 res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08432 res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08433 res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08434 res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08435 res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08436 res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08437 res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08438 res |= ast_custom_function_register(&queuevar_function);
08439 res |= ast_custom_function_register(&queueexists_function);
08440 res |= ast_custom_function_register(&queuemembercount_function);
08441 res |= ast_custom_function_register(&queuemembercount_dep);
08442 res |= ast_custom_function_register(&queuememberlist_function);
08443 res |= ast_custom_function_register(&queuewaitingcount_function);
08444 res |= ast_custom_function_register(&queuememberpenalty_function);
08445
08446 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08447 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08448 }
08449
08450
08451 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08452 res = -1;
08453 }
08454
08455 ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08456
08457 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08458
08459 return res ? AST_MODULE_LOAD_DECLINE : 0;
08460 }
08461
08462 static int reload(void)
08463 {
08464 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08465 ast_unload_realtime("queue_members");
08466 reload_handler(1, &mask, NULL);
08467 return 0;
08468 }
08469
08470 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "True Call Queueing",
08471 .load = load_module,
08472 .unload = unload_module,
08473 .reload = reload,
08474 .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
08475 .nonoptreq = "res_monitor",
08476 );
08477