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