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