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