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