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