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: 352955 $")
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 ast_channel_update_connected_line(in, &connected_caller, NULL);
03532 ast_party_connected_line_free(&connected_caller);
03533 }
03534 }
03535 if (o->aoc_s_rate_list) {
03536 size_t encoded_size;
03537 struct ast_aoc_encoded *encoded;
03538 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03539 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03540 ast_aoc_destroy_encoded(encoded);
03541 }
03542 }
03543 peer = o;
03544 }
03545 } else if (o->chan && (o->chan == winner)) {
03546
03547 ast_copy_string(on, o->member->interface, sizeof(on));
03548 ast_copy_string(membername, o->member->membername, sizeof(membername));
03549
03550 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03551 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03552 numnochan++;
03553 do_hang(o);
03554 winner = NULL;
03555 continue;
03556 } else if (!ast_strlen_zero(o->chan->call_forward)) {
03557 struct ast_channel *original = o->chan;
03558 char tmpchan[256];
03559 char *stuff;
03560 char *tech;
03561
03562 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03563 if ((stuff = strchr(tmpchan, '/'))) {
03564 *stuff++ = '\0';
03565 tech = tmpchan;
03566 } else {
03567 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03568 stuff = tmpchan;
03569 tech = "Local";
03570 }
03571
03572 ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03573
03574
03575 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03576
03577 o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03578 if (!o->chan) {
03579 ast_log(LOG_NOTICE,
03580 "Forwarding failed to create channel to dial '%s/%s'\n",
03581 tech, stuff);
03582 o->stillgoing = 0;
03583 numnochan++;
03584 } else {
03585 struct ast_party_redirecting redirecting;
03586
03587 ast_channel_lock_both(o->chan, in);
03588 ast_channel_inherit_variables(in, o->chan);
03589 ast_channel_datastore_inherit(in, o->chan);
03590
03591 ast_string_field_set(o->chan, accountcode, in->accountcode);
03592
03593 ast_channel_set_redirecting(o->chan, &original->redirecting, NULL);
03594 if (!o->chan->redirecting.from.number.valid
03595 || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03596
03597
03598
03599
03600 ast_party_number_free(&o->chan->redirecting.from.number);
03601 ast_party_number_init(&o->chan->redirecting.from.number);
03602 o->chan->redirecting.from.number.valid = 1;
03603 o->chan->redirecting.from.number.str =
03604 ast_strdup(S_OR(in->macroexten, in->exten));
03605 }
03606
03607 o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03608
03609 ast_party_caller_copy(&o->chan->caller, &in->caller);
03610 ast_party_connected_line_copy(&o->chan->connected, &original->connected);
03611
03612
03613
03614
03615
03616
03617
03618
03619 ast_party_redirecting_init(&redirecting);
03620 ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03621 ast_channel_unlock(o->chan);
03622 res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0);
03623 if (res) {
03624 ast_channel_update_redirecting(in, &redirecting, NULL);
03625 }
03626 ast_party_redirecting_free(&redirecting);
03627 ast_channel_unlock(in);
03628
03629 update_connectedline = 1;
03630
03631 if (ast_call(o->chan, stuff, 0)) {
03632 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03633 tech, stuff);
03634 do_hang(o);
03635 numnochan++;
03636 }
03637 }
03638
03639 ast_hangup(winner);
03640 continue;
03641 }
03642 f = ast_read(winner);
03643 if (f) {
03644 if (f->frametype == AST_FRAME_CONTROL) {
03645 switch (f->subclass.integer) {
03646 case AST_CONTROL_ANSWER:
03647
03648 if (!peer) {
03649 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03650 if (update_connectedline) {
03651 if (o->pending_connected_update) {
03652 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03653 ast_channel_update_connected_line(in, &o->connected, NULL);
03654 }
03655 } else if (!o->dial_callerid_absent) {
03656 ast_channel_lock(o->chan);
03657 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03658 ast_channel_unlock(o->chan);
03659 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03660 ast_channel_update_connected_line(in, &connected_caller, NULL);
03661 ast_party_connected_line_free(&connected_caller);
03662 }
03663 }
03664 if (o->aoc_s_rate_list) {
03665 size_t encoded_size;
03666 struct ast_aoc_encoded *encoded;
03667 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03668 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03669 ast_aoc_destroy_encoded(encoded);
03670 }
03671 }
03672 peer = o;
03673 }
03674 break;
03675 case AST_CONTROL_BUSY:
03676 ast_verb(3, "%s is busy\n", ochan_name);
03677 if (in->cdr)
03678 ast_cdr_busy(in->cdr);
03679 do_hang(o);
03680 endtime = (long) time(NULL);
03681 endtime -= starttime;
03682 rna(endtime * 1000, qe, on, membername, 0);
03683 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03684 if (qe->parent->timeoutrestart)
03685 *to = orig;
03686
03687 if (*to > 500) {
03688 ring_one(qe, outgoing, &numbusies);
03689 starttime = (long) time(NULL);
03690 }
03691 }
03692 numbusies++;
03693 break;
03694 case AST_CONTROL_CONGESTION:
03695 ast_verb(3, "%s is circuit-busy\n", ochan_name);
03696 if (in->cdr)
03697 ast_cdr_busy(in->cdr);
03698 endtime = (long) time(NULL);
03699 endtime -= starttime;
03700 rna(endtime * 1000, qe, on, membername, 0);
03701 do_hang(o);
03702 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03703 if (qe->parent->timeoutrestart)
03704 *to = orig;
03705 if (*to > 500) {
03706 ring_one(qe, outgoing, &numbusies);
03707 starttime = (long) time(NULL);
03708 }
03709 }
03710 numbusies++;
03711 break;
03712 case AST_CONTROL_RINGING:
03713 ast_verb(3, "%s is ringing\n", ochan_name);
03714
03715
03716 if (qe->ring_when_ringing) {
03717 ast_moh_stop(qe->chan);
03718 ast_indicate(qe->chan, AST_CONTROL_RINGING);
03719 }
03720 break;
03721 case AST_CONTROL_OFFHOOK:
03722
03723 break;
03724 case AST_CONTROL_CONNECTED_LINE:
03725 if (!update_connectedline) {
03726 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03727 } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03728 struct ast_party_connected_line connected;
03729 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03730 ast_party_connected_line_set_init(&connected, &o->connected);
03731 ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03732 ast_party_connected_line_set(&o->connected, &connected, NULL);
03733 ast_party_connected_line_free(&connected);
03734 o->pending_connected_update = 1;
03735 } else {
03736 if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03737 ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03738 }
03739 }
03740 break;
03741 case AST_CONTROL_AOC:
03742 {
03743 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03744 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03745 ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03746 o->aoc_s_rate_list = decoded;
03747 } else {
03748 ast_aoc_destroy_decoded(decoded);
03749 }
03750 }
03751 break;
03752 case AST_CONTROL_REDIRECTING:
03753 if (!update_connectedline) {
03754 ast_verb(3, "Redirecting update to %s prevented\n", inchan_name);
03755 } else if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03756 ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name);
03757 if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03758 ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03759 }
03760 }
03761 break;
03762 default:
03763 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03764 break;
03765 }
03766 }
03767 ast_frfree(f);
03768 } else {
03769 endtime = (long) time(NULL) - starttime;
03770 rna(endtime * 1000, qe, on, membername, 1);
03771 do_hang(o);
03772 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03773 if (qe->parent->timeoutrestart)
03774 *to = orig;
03775 if (*to > 500) {
03776 ring_one(qe, outgoing, &numbusies);
03777 starttime = (long) time(NULL);
03778 }
03779 }
03780 }
03781 }
03782 }
03783
03784
03785 if (winner == in) {
03786 f = ast_read(in);
03787 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03788
03789 *to = -1;
03790 if (f) {
03791 if (f->data.uint32) {
03792 in->hangupcause = f->data.uint32;
03793 }
03794 ast_frfree(f);
03795 }
03796 return NULL;
03797 }
03798 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03799 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03800 *to = 0;
03801 ast_frfree(f);
03802 return NULL;
03803 }
03804 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03805 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03806 *to = 0;
03807 *digit = f->subclass.integer;
03808 ast_frfree(f);
03809 return NULL;
03810 }
03811 ast_frfree(f);
03812 }
03813 if (!*to) {
03814 for (o = start; o; o = o->call_next)
03815 rna(orig, qe, o->interface, o->member->membername, 1);
03816 }
03817 }
03818
03819 #ifdef HAVE_EPOLL
03820 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03821 if (epollo->chan)
03822 ast_poll_channel_del(in, epollo->chan);
03823 }
03824 #endif
03825
03826 return peer;
03827 }
03828
03829
03830
03831
03832
03833
03834
03835
03836
03837
03838
03839
03840 static int is_our_turn(struct queue_ent *qe)
03841 {
03842 struct queue_ent *ch;
03843 int res;
03844 int avl;
03845 int idx = 0;
03846
03847 ao2_lock(qe->parent);
03848
03849 avl = num_available_members(qe->parent);
03850
03851 ch = qe->parent->head;
03852
03853 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03854
03855 while ((idx < avl) && (ch) && (ch != qe)) {
03856 if (!ch->pending)
03857 idx++;
03858 ch = ch->next;
03859 }
03860
03861 ao2_unlock(qe->parent);
03862
03863
03864
03865
03866 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03867 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03868 res = 1;
03869 } else {
03870 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03871 res = 0;
03872 }
03873
03874 return res;
03875 }
03876
03877
03878
03879
03880
03881
03882
03883 static void update_qe_rule(struct queue_ent *qe)
03884 {
03885 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03886 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03887 char max_penalty_str[20], min_penalty_str[20];
03888
03889 if (max_penalty < 0)
03890 max_penalty = 0;
03891 if (min_penalty < 0)
03892 min_penalty = 0;
03893 if (min_penalty > max_penalty)
03894 min_penalty = max_penalty;
03895 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03896 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03897 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03898 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03899 qe->max_penalty = max_penalty;
03900 qe->min_penalty = min_penalty;
03901 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
03902 qe->pr = AST_LIST_NEXT(qe->pr, list);
03903 }
03904
03905
03906
03907
03908
03909
03910
03911
03912
03913
03914
03915 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
03916 {
03917 int res = 0;
03918
03919
03920 for (;;) {
03921
03922 if (is_our_turn(qe))
03923 break;
03924
03925
03926 if (qe->expire && (time(NULL) >= qe->expire)) {
03927 *reason = QUEUE_TIMEOUT;
03928 break;
03929 }
03930
03931 if (qe->parent->leavewhenempty) {
03932 int status = 0;
03933
03934 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
03935 *reason = QUEUE_LEAVEEMPTY;
03936 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03937 leave_queue(qe);
03938 break;
03939 }
03940 }
03941
03942
03943 if (qe->parent->announcefrequency &&
03944 (res = say_position(qe,ringing)))
03945 break;
03946
03947
03948 if (qe->expire && (time(NULL) >= qe->expire)) {
03949 *reason = QUEUE_TIMEOUT;
03950 break;
03951 }
03952
03953
03954 if (qe->parent->periodicannouncefrequency &&
03955 (res = say_periodic_announcement(qe,ringing)))
03956 break;
03957
03958
03959 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03960 update_qe_rule(qe);
03961 }
03962
03963
03964 if (qe->expire && (time(NULL) >= qe->expire)) {
03965 *reason = QUEUE_TIMEOUT;
03966 break;
03967 }
03968
03969
03970 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03971 if (res > 0 && !valid_exit(qe, res))
03972 res = 0;
03973 else
03974 break;
03975 }
03976
03977
03978 if (qe->expire && (time(NULL) >= qe->expire)) {
03979 *reason = QUEUE_TIMEOUT;
03980 break;
03981 }
03982 }
03983
03984 return res;
03985 }
03986
03987
03988
03989
03990
03991 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
03992 {
03993 int oldtalktime;
03994
03995 struct member *mem;
03996 struct call_queue *qtmp;
03997 struct ao2_iterator queue_iter;
03998
03999 if (shared_lastcall) {
04000 queue_iter = ao2_iterator_init(queues, 0);
04001 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04002 ao2_lock(qtmp);
04003 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04004 time(&mem->lastcall);
04005 mem->calls++;
04006 mem->lastqueue = q;
04007 ao2_ref(mem, -1);
04008 }
04009 ao2_unlock(qtmp);
04010 queue_t_unref(qtmp, "Done with iterator");
04011 }
04012 ao2_iterator_destroy(&queue_iter);
04013 } else {
04014 ao2_lock(q);
04015 time(&member->lastcall);
04016 member->calls++;
04017 member->lastqueue = q;
04018 ao2_unlock(q);
04019 }
04020 ao2_lock(q);
04021 q->callscompleted++;
04022 if (callcompletedinsl)
04023 q->callscompletedinsl++;
04024
04025 oldtalktime = q->talktime;
04026 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04027 ao2_unlock(q);
04028 return 0;
04029 }
04030
04031
04032
04033
04034
04035
04036
04037
04038
04039 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
04040 {
04041
04042 int membercount = ao2_container_count(q->members);
04043 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04044
04045 if (usepenalty) {
04046 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04047 (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04048 return -1;
04049 }
04050 } else {
04051 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04052 membercount, q->penaltymemberslimit);
04053 }
04054
04055 switch (q->strategy) {
04056 case QUEUE_STRATEGY_RINGALL:
04057
04058 tmp->metric = mem->penalty * 1000000 * usepenalty;
04059 break;
04060 case QUEUE_STRATEGY_LINEAR:
04061 if (pos < qe->linpos) {
04062 tmp->metric = 1000 + pos;
04063 } else {
04064 if (pos > qe->linpos)
04065
04066 qe->linwrapped = 1;
04067 tmp->metric = pos;
04068 }
04069 tmp->metric += mem->penalty * 1000000 * usepenalty;
04070 break;
04071 case QUEUE_STRATEGY_RRORDERED:
04072 case QUEUE_STRATEGY_RRMEMORY:
04073 if (pos < q->rrpos) {
04074 tmp->metric = 1000 + pos;
04075 } else {
04076 if (pos > q->rrpos)
04077
04078 q->wrapped = 1;
04079 tmp->metric = pos;
04080 }
04081 tmp->metric += mem->penalty * 1000000 * usepenalty;
04082 break;
04083 case QUEUE_STRATEGY_RANDOM:
04084 tmp->metric = ast_random() % 1000;
04085 tmp->metric += mem->penalty * 1000000 * usepenalty;
04086 break;
04087 case QUEUE_STRATEGY_WRANDOM:
04088 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04089 break;
04090 case QUEUE_STRATEGY_FEWESTCALLS:
04091 tmp->metric = mem->calls;
04092 tmp->metric += mem->penalty * 1000000 * usepenalty;
04093 break;
04094 case QUEUE_STRATEGY_LEASTRECENT:
04095 if (!mem->lastcall)
04096 tmp->metric = 0;
04097 else
04098 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04099 tmp->metric += mem->penalty * 1000000 * usepenalty;
04100 break;
04101 default:
04102 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04103 break;
04104 }
04105 return 0;
04106 }
04107
04108 enum agent_complete_reason {
04109 CALLER,
04110 AGENT,
04111 TRANSFER
04112 };
04113
04114
04115 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
04116 const struct ast_channel *peer, const struct member *member, time_t callstart,
04117 char *vars, size_t vars_len, enum agent_complete_reason rsn)
04118 {
04119 const char *reason = NULL;
04120
04121 if (!qe->parent->eventwhencalled)
04122 return;
04123
04124 switch (rsn) {
04125 case CALLER:
04126 reason = "caller";
04127 break;
04128 case AGENT:
04129 reason = "agent";
04130 break;
04131 case TRANSFER:
04132 reason = "transfer";
04133 break;
04134 }
04135
04136 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04137 "Queue: %s\r\n"
04138 "Uniqueid: %s\r\n"
04139 "Channel: %s\r\n"
04140 "Member: %s\r\n"
04141 "MemberName: %s\r\n"
04142 "HoldTime: %ld\r\n"
04143 "TalkTime: %ld\r\n"
04144 "Reason: %s\r\n"
04145 "%s",
04146 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04147 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04148 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04149 }
04150
04151 struct queue_transfer_ds {
04152 struct queue_ent *qe;
04153 struct member *member;
04154 time_t starttime;
04155 int callcompletedinsl;
04156 };
04157
04158 static void queue_transfer_destroy(void *data)
04159 {
04160 struct queue_transfer_ds *qtds = data;
04161 ast_free(qtds);
04162 }
04163
04164
04165
04166 static const struct ast_datastore_info queue_transfer_info = {
04167 .type = "queue_transfer",
04168 .chan_fixup = queue_transfer_fixup,
04169 .destroy = queue_transfer_destroy,
04170 };
04171
04172
04173
04174
04175
04176
04177
04178
04179
04180
04181 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
04182 {
04183 struct queue_transfer_ds *qtds = data;
04184 struct queue_ent *qe = qtds->qe;
04185 struct member *member = qtds->member;
04186 time_t callstart = qtds->starttime;
04187 int callcompletedinsl = qtds->callcompletedinsl;
04188 struct ast_datastore *datastore;
04189
04190 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04191 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04192 (long) (time(NULL) - callstart), qe->opos);
04193
04194 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04195
04196
04197 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04198 ast_channel_datastore_remove(old_chan, datastore);
04199 } else {
04200 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04201 }
04202 }
04203
04204
04205
04206
04207
04208
04209
04210
04211
04212 static int attended_transfer_occurred(struct ast_channel *chan)
04213 {
04214 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04215 }
04216
04217
04218
04219 static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
04220 {
04221 struct ast_datastore *ds;
04222 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04223
04224 if (!qtds) {
04225 ast_log(LOG_WARNING, "Memory allocation error!\n");
04226 return NULL;
04227 }
04228
04229 ast_channel_lock(qe->chan);
04230 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04231 ast_channel_unlock(qe->chan);
04232 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04233 return NULL;
04234 }
04235
04236 qtds->qe = qe;
04237
04238 qtds->member = member;
04239 qtds->starttime = starttime;
04240 qtds->callcompletedinsl = callcompletedinsl;
04241 ds->data = qtds;
04242 ast_channel_datastore_add(qe->chan, ds);
04243 ast_channel_unlock(qe->chan);
04244 return ds;
04245 }
04246
04247 struct queue_end_bridge {
04248 struct call_queue *q;
04249 struct ast_channel *chan;
04250 };
04251
04252 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
04253 {
04254 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04255 ao2_ref(qeb, +1);
04256 qeb->chan = originator;
04257 }
04258
04259 static void end_bridge_callback(void *data)
04260 {
04261 struct queue_end_bridge *qeb = data;
04262 struct call_queue *q = qeb->q;
04263 struct ast_channel *chan = qeb->chan;
04264
04265 if (ao2_ref(qeb, -1) == 1) {
04266 set_queue_variables(q, chan);
04267
04268 queue_t_unref(q, "Expire bridge_config reference");
04269 }
04270 }
04271
04272
04273
04274
04275
04276
04277
04278
04279
04280
04281
04282
04283
04284
04285
04286
04287
04288
04289
04290
04291
04292
04293
04294
04295
04296
04297
04298
04299 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
04300 {
04301 struct member *cur;
04302 struct callattempt *outgoing = NULL;
04303 int to, orig;
04304 char oldexten[AST_MAX_EXTENSION]="";
04305 char oldcontext[AST_MAX_CONTEXT]="";
04306 char queuename[256]="";
04307 char interfacevar[256]="";
04308 struct ast_channel *peer;
04309 struct ast_channel *which;
04310 struct callattempt *lpeer;
04311 struct member *member;
04312 struct ast_app *application;
04313 int res = 0, bridge = 0;
04314 int numbusies = 0;
04315 int x=0;
04316 char *announce = NULL;
04317 char digit = 0;
04318 time_t callstart;
04319 time_t now = time(NULL);
04320 struct ast_bridge_config bridge_config;
04321 char nondataquality = 1;
04322 char *agiexec = NULL;
04323 char *macroexec = NULL;
04324 char *gosubexec = NULL;
04325 const char *monitorfilename;
04326 const char *monitor_exec;
04327 const char *monitor_options;
04328 char tmpid[256], tmpid2[256];
04329 char meid[1024], meid2[1024];
04330 char mixmonargs[1512];
04331 struct ast_app *mixmonapp = NULL;
04332 char *p;
04333 char vars[2048];
04334 int forwardsallowed = 1;
04335 int update_connectedline = 1;
04336 int callcompletedinsl;
04337 struct ao2_iterator memi;
04338 struct ast_datastore *datastore, *transfer_ds;
04339 struct queue_end_bridge *queue_end_bridge = NULL;
04340
04341 ast_channel_lock(qe->chan);
04342 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04343 ast_channel_unlock(qe->chan);
04344
04345 memset(&bridge_config, 0, sizeof(bridge_config));
04346 tmpid[0] = 0;
04347 meid[0] = 0;
04348 time(&now);
04349
04350
04351
04352
04353
04354 if (qe->expire && now >= qe->expire) {
04355 res = 0;
04356 goto out;
04357 }
04358
04359 for (; options && *options; options++)
04360 switch (*options) {
04361 case 't':
04362 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04363 break;
04364 case 'T':
04365 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04366 break;
04367 case 'w':
04368 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04369 break;
04370 case 'W':
04371 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04372 break;
04373 case 'c':
04374 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04375 break;
04376 case 'd':
04377 nondataquality = 0;
04378 break;
04379 case 'h':
04380 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04381 break;
04382 case 'H':
04383 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04384 break;
04385 case 'k':
04386 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04387 break;
04388 case 'K':
04389 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04390 break;
04391 case 'n':
04392 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04393 (*tries)++;
04394 else
04395 *tries = ao2_container_count(qe->parent->members);
04396 *noption = 1;
04397 break;
04398 case 'i':
04399 forwardsallowed = 0;
04400 break;
04401 case 'I':
04402 update_connectedline = 0;
04403 break;
04404 case 'x':
04405 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04406 break;
04407 case 'X':
04408 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04409 break;
04410 case 'C':
04411 qe->cancel_answered_elsewhere = 1;
04412 break;
04413 }
04414
04415
04416
04417
04418 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04419 qe->cancel_answered_elsewhere = 1;
04420 }
04421
04422 ao2_lock(qe->parent);
04423 ast_debug(1, "%s is trying to call a queue member.\n",
04424 qe->chan->name);
04425 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04426 if (!ast_strlen_zero(qe->announce))
04427 announce = qe->announce;
04428 if (!ast_strlen_zero(announceoverride))
04429 announce = announceoverride;
04430
04431 memi = ao2_iterator_init(qe->parent->members, 0);
04432 while ((cur = ao2_iterator_next(&memi))) {
04433 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04434 struct ast_dialed_interface *di;
04435 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04436 if (!tmp) {
04437 ao2_ref(cur, -1);
04438 ao2_iterator_destroy(&memi);
04439 ao2_unlock(qe->parent);
04440 goto out;
04441 }
04442 if (!datastore) {
04443 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04444 callattempt_free(tmp);
04445 ao2_ref(cur, -1);
04446 ao2_iterator_destroy(&memi);
04447 ao2_unlock(qe->parent);
04448 goto out;
04449 }
04450 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04451 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04452 callattempt_free(tmp);
04453 ao2_ref(cur, -1);
04454 ao2_iterator_destroy(&memi);
04455 ao2_unlock(&qe->parent);
04456 goto out;
04457 }
04458 datastore->data = dialed_interfaces;
04459 AST_LIST_HEAD_INIT(dialed_interfaces);
04460
04461 ast_channel_lock(qe->chan);
04462 ast_channel_datastore_add(qe->chan, datastore);
04463 ast_channel_unlock(qe->chan);
04464 } else
04465 dialed_interfaces = datastore->data;
04466
04467 AST_LIST_LOCK(dialed_interfaces);
04468 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04469 if (!strcasecmp(cur->interface, di->interface)) {
04470 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
04471 di->interface);
04472 break;
04473 }
04474 }
04475 AST_LIST_UNLOCK(dialed_interfaces);
04476
04477 if (di) {
04478 callattempt_free(tmp);
04479 ao2_ref(cur, -1);
04480 continue;
04481 }
04482
04483
04484
04485
04486
04487 if (strncasecmp(cur->interface, "Local/", 6)) {
04488 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04489 callattempt_free(tmp);
04490 ao2_ref(cur, -1);
04491 ao2_iterator_destroy(&memi);
04492 ao2_unlock(qe->parent);
04493 goto out;
04494 }
04495 strcpy(di->interface, cur->interface);
04496
04497 AST_LIST_LOCK(dialed_interfaces);
04498 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04499 AST_LIST_UNLOCK(dialed_interfaces);
04500 }
04501
04502 ast_channel_lock(qe->chan);
04503
04504
04505
04506
04507
04508
04509 ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04510 ast_channel_unlock(qe->chan);
04511
04512 tmp->stillgoing = -1;
04513 tmp->member = cur;
04514 tmp->lastcall = cur->lastcall;
04515 tmp->lastqueue = cur->lastqueue;
04516 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04517
04518
04519 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04520
04521
04522
04523 tmp->q_next = outgoing;
04524 outgoing = tmp;
04525
04526 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04527 break;
04528 } else {
04529 callattempt_free(tmp);
04530 }
04531 }
04532 ao2_iterator_destroy(&memi);
04533
04534 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04535
04536 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04537 to = (qe->expire - now) * 1000;
04538 else
04539 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04540 } else {
04541
04542 if (qe->expire && qe->expire<=now) {
04543 to = 0;
04544 } else if (qe->parent->timeout) {
04545 to = qe->parent->timeout * 1000;
04546 } else {
04547 to = -1;
04548 }
04549 }
04550 orig = to;
04551 ++qe->pending;
04552 ao2_unlock(qe->parent);
04553 ring_one(qe, outgoing, &numbusies);
04554 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline);
04555
04556
04557
04558
04559
04560
04561 ast_channel_lock(qe->chan);
04562 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04563 ast_datastore_free(datastore);
04564 }
04565 ast_channel_unlock(qe->chan);
04566 ao2_lock(qe->parent);
04567 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
04568 store_next_rr(qe, outgoing);
04569
04570 }
04571 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04572 store_next_lin(qe, outgoing);
04573 }
04574 ao2_unlock(qe->parent);
04575 peer = lpeer ? lpeer->chan : NULL;
04576 if (!peer) {
04577 qe->pending = 0;
04578 if (to) {
04579
04580 res = -1;
04581 } else {
04582
04583 res = digit;
04584 }
04585 if (res == -1)
04586 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
04587 if (ast_cdr_isset_unanswered()) {
04588
04589
04590 struct callattempt *o;
04591 for (o = outgoing; o; o = o->q_next) {
04592 if (!o->chan) {
04593 continue;
04594 }
04595 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04596 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04597 break;
04598 }
04599 }
04600 }
04601 } else {
04602
04603
04604
04605 if (!strcmp(qe->chan->tech->type, "DAHDI"))
04606 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04607 if (!strcmp(peer->tech->type, "DAHDI"))
04608 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04609
04610 time(&now);
04611 recalc_holdtime(qe, (now - qe->start));
04612 ao2_lock(qe->parent);
04613 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04614 ao2_unlock(qe->parent);
04615 member = lpeer->member;
04616
04617 ao2_ref(member, 1);
04618 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04619 outgoing = NULL;
04620 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04621 int res2;
04622
04623 res2 = ast_autoservice_start(qe->chan);
04624 if (!res2) {
04625 if (qe->parent->memberdelay) {
04626 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04627 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04628 }
04629 if (!res2 && announce) {
04630 play_file(peer, announce);
04631 }
04632 if (!res2 && qe->parent->reportholdtime) {
04633 if (!play_file(peer, qe->parent->sound_reporthold)) {
04634 int holdtime, holdtimesecs;
04635
04636 time(&now);
04637 holdtime = abs((now - qe->start) / 60);
04638 holdtimesecs = abs((now - qe->start) % 60);
04639 if (holdtime > 0) {
04640 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
04641 play_file(peer, qe->parent->sound_minutes);
04642 }
04643 if (holdtimesecs > 1) {
04644 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
04645 play_file(peer, qe->parent->sound_seconds);
04646 }
04647 }
04648 }
04649 }
04650 res2 |= ast_autoservice_stop(qe->chan);
04651 if (ast_check_hangup(peer)) {
04652
04653 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
04654 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
04655 if (qe->parent->eventwhencalled)
04656 manager_event(EVENT_FLAG_AGENT, "AgentDump",
04657 "Queue: %s\r\n"
04658 "Uniqueid: %s\r\n"
04659 "Channel: %s\r\n"
04660 "Member: %s\r\n"
04661 "MemberName: %s\r\n"
04662 "%s",
04663 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04664 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04665 ast_hangup(peer);
04666 ao2_ref(member, -1);
04667 goto out;
04668 } else if (res2) {
04669
04670 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
04671 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04672 record_abandoned(qe);
04673 ast_hangup(peer);
04674 ao2_ref(member, -1);
04675 return -1;
04676 }
04677 }
04678
04679 if (ringing)
04680 ast_indicate(qe->chan,-1);
04681 else
04682 ast_moh_stop(qe->chan);
04683
04684 if (qe->chan->cdr)
04685 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
04686
04687 res = ast_channel_make_compatible(qe->chan, peer);
04688 if (res < 0) {
04689 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
04690 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
04691 record_abandoned(qe);
04692 ast_cdr_failed(qe->chan->cdr);
04693 ast_hangup(peer);
04694 ao2_ref(member, -1);
04695 return -1;
04696 }
04697
04698
04699 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
04700 if (play_file(qe->chan, qe->parent->sound_callerannounce))
04701 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
04702 }
04703
04704 ao2_lock(qe->parent);
04705
04706
04707 if (qe->parent->setinterfacevar) {
04708 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
04709 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
04710 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04711 pbx_builtin_setvar_multiple(peer, interfacevar);
04712 }
04713
04714
04715
04716 if (qe->parent->setqueueentryvar) {
04717 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
04718 (long) time(NULL) - qe->start, qe->opos);
04719 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04720 pbx_builtin_setvar_multiple(peer, interfacevar);
04721 }
04722
04723 ao2_unlock(qe->parent);
04724
04725
04726 set_queue_variables(qe->parent, qe->chan);
04727 set_queue_variables(qe->parent, peer);
04728
04729 ast_channel_lock(qe->chan);
04730 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04731 monitorfilename = ast_strdupa(monitorfilename);
04732 }
04733 ast_channel_unlock(qe->chan);
04734
04735 if (qe->parent->monfmt && *qe->parent->monfmt) {
04736 if (!qe->parent->montype) {
04737 const char *monexec, *monargs;
04738 ast_debug(1, "Starting Monitor as requested.\n");
04739 ast_channel_lock(qe->chan);
04740 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
04741 which = qe->chan;
04742 monexec = monexec ? ast_strdupa(monexec) : NULL;
04743 }
04744 else
04745 which = peer;
04746 ast_channel_unlock(qe->chan);
04747 if (monitorfilename) {
04748 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04749 } else if (qe->chan->cdr) {
04750 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04751 } else {
04752
04753 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04754 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04755 }
04756 if (!ast_strlen_zero(monexec)) {
04757 ast_monitor_setjoinfiles(which, 1);
04758 }
04759 } else {
04760 mixmonapp = pbx_findapp("MixMonitor");
04761
04762 if (mixmonapp) {
04763 ast_debug(1, "Starting MixMonitor as requested.\n");
04764 if (!monitorfilename) {
04765 if (qe->chan->cdr)
04766 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04767 else
04768 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04769 } else {
04770 const char *m = monitorfilename;
04771 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04772 switch (*m) {
04773 case '^':
04774 if (*(m + 1) == '{')
04775 *p = '$';
04776 break;
04777 case ',':
04778 *p++ = '\\';
04779
04780 default:
04781 *p = *m;
04782 }
04783 if (*m == '\0')
04784 break;
04785 }
04786 if (p == tmpid2 + sizeof(tmpid2))
04787 tmpid2[sizeof(tmpid2) - 1] = '\0';
04788
04789 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04790 }
04791
04792 ast_channel_lock(qe->chan);
04793 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04794 monitor_exec = ast_strdupa(monitor_exec);
04795 }
04796 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04797 monitor_options = ast_strdupa(monitor_options);
04798 } else {
04799 monitor_options = "";
04800 }
04801 ast_channel_unlock(qe->chan);
04802
04803 if (monitor_exec) {
04804 const char *m = monitor_exec;
04805 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04806 switch (*m) {
04807 case '^':
04808 if (*(m + 1) == '{')
04809 *p = '$';
04810 break;
04811 case ',':
04812 *p++ = '\\';
04813
04814 default:
04815 *p = *m;
04816 }
04817 if (*m == '\0')
04818 break;
04819 }
04820 if (p == meid2 + sizeof(meid2))
04821 meid2[sizeof(meid2) - 1] = '\0';
04822
04823 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04824 }
04825
04826 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04827
04828 if (!ast_strlen_zero(monitor_exec))
04829 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04830 else
04831 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04832
04833 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04834
04835 if (qe->chan->cdr)
04836 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04837 pbx_exec(qe->chan, mixmonapp, mixmonargs);
04838 if (qe->chan->cdr)
04839 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04840
04841 } else {
04842 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04843 }
04844 }
04845 }
04846
04847 leave_queue(qe);
04848 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04849 ast_debug(1, "app_queue: sendurl=%s.\n", url);
04850 ast_channel_sendurl(peer, url);
04851 }
04852
04853
04854
04855 if (!ast_strlen_zero(macro)) {
04856 macroexec = ast_strdupa(macro);
04857 } else {
04858 if (qe->parent->membermacro)
04859 macroexec = ast_strdupa(qe->parent->membermacro);
04860 }
04861
04862 if (!ast_strlen_zero(macroexec)) {
04863 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04864
04865 res = ast_autoservice_start(qe->chan);
04866 if (res) {
04867 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04868 res = -1;
04869 }
04870
04871 application = pbx_findapp("Macro");
04872
04873 if (application) {
04874 res = pbx_exec(peer, application, macroexec);
04875 ast_debug(1, "Macro exited with status %d\n", res);
04876 res = 0;
04877 } else {
04878 ast_log(LOG_ERROR, "Could not find application Macro\n");
04879 res = -1;
04880 }
04881
04882 if (ast_autoservice_stop(qe->chan) < 0) {
04883 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04884 res = -1;
04885 }
04886 }
04887
04888
04889
04890 if (!ast_strlen_zero(gosub)) {
04891 gosubexec = ast_strdupa(gosub);
04892 } else {
04893 if (qe->parent->membergosub)
04894 gosubexec = ast_strdupa(qe->parent->membergosub);
04895 }
04896
04897 if (!ast_strlen_zero(gosubexec)) {
04898 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
04899
04900 res = ast_autoservice_start(qe->chan);
04901 if (res) {
04902 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04903 res = -1;
04904 }
04905
04906 application = pbx_findapp("Gosub");
04907
04908 if (application) {
04909 char *gosub_args, *gosub_argstart;
04910
04911
04912 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
04913 ast_copy_string(peer->exten, "s", sizeof(peer->exten));
04914 peer->priority = 0;
04915
04916 gosub_argstart = strchr(gosubexec, ',');
04917 if (gosub_argstart) {
04918 const char *what_is_s = "s";
04919 *gosub_argstart = 0;
04920 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
04921 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
04922 what_is_s = "~~s~~";
04923 }
04924 if (asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
04925 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04926 gosub_args = NULL;
04927 }
04928 *gosub_argstart = ',';
04929 } else {
04930 const char *what_is_s = "s";
04931 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
04932 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
04933 what_is_s = "~~s~~";
04934 }
04935 if (asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
04936 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04937 gosub_args = NULL;
04938 }
04939 }
04940 if (gosub_args) {
04941 res = pbx_exec(peer, application, gosub_args);
04942 if (!res) {
04943 struct ast_pbx_args args;
04944 memset(&args, 0, sizeof(args));
04945 args.no_hangup_chan = 1;
04946 ast_pbx_run_args(peer, &args);
04947 }
04948 ast_free(gosub_args);
04949 ast_debug(1, "Gosub exited with status %d\n", res);
04950 } else {
04951 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
04952 }
04953 } else {
04954 ast_log(LOG_ERROR, "Could not find application Gosub\n");
04955 res = -1;
04956 }
04957
04958 if (ast_autoservice_stop(qe->chan) < 0) {
04959 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04960 res = -1;
04961 }
04962 }
04963
04964 if (!ast_strlen_zero(agi)) {
04965 ast_debug(1, "app_queue: agi=%s.\n", agi);
04966 application = pbx_findapp("agi");
04967 if (application) {
04968 agiexec = ast_strdupa(agi);
04969 pbx_exec(qe->chan, application, agiexec);
04970 } else
04971 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
04972 }
04973 qe->handled++;
04974 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
04975 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
04976
04977 if (qe->chan->cdr) {
04978 struct ast_cdr *cdr;
04979 struct ast_cdr *newcdr;
04980
04981
04982 cdr = qe->chan->cdr;
04983 while (cdr->next) {
04984 cdr = cdr->next;
04985 }
04986
04987
04988 if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) &&
04989 (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) &&
04990 (newcdr = ast_cdr_dup(cdr))) {
04991 ast_channel_lock(qe->chan);
04992 ast_cdr_init(newcdr, qe->chan);
04993 ast_cdr_reset(newcdr, 0);
04994 cdr = ast_cdr_append(cdr, newcdr);
04995 cdr = cdr->next;
04996 ast_channel_unlock(qe->chan);
04997 }
04998
04999 if (update_cdr) {
05000 ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05001 }
05002 }
05003
05004 if (qe->parent->eventwhencalled)
05005 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05006 "Queue: %s\r\n"
05007 "Uniqueid: %s\r\n"
05008 "Channel: %s\r\n"
05009 "Member: %s\r\n"
05010 "MemberName: %s\r\n"
05011 "Holdtime: %ld\r\n"
05012 "BridgedChannel: %s\r\n"
05013 "Ringtime: %ld\r\n"
05014 "%s",
05015 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05016 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05017 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05018 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05019 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05020
05021 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05022 queue_end_bridge->q = qe->parent;
05023 queue_end_bridge->chan = qe->chan;
05024 bridge_config.end_bridge_callback = end_bridge_callback;
05025 bridge_config.end_bridge_callback_data = queue_end_bridge;
05026 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05027
05028
05029
05030
05031 queue_t_ref(qe->parent, "For bridge_config reference");
05032 }
05033
05034 time(&callstart);
05035 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05036 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
05037
05038
05039
05040
05041
05042 ast_channel_lock(qe->chan);
05043 if (!attended_transfer_occurred(qe->chan)) {
05044 struct ast_datastore *tds;
05045
05046
05047 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05048 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05049 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05050 (long) (time(NULL) - callstart), qe->opos);
05051 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05052 } else if (ast_check_hangup(qe->chan)) {
05053 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05054 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05055 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05056 } else {
05057 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%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), AGENT);
05060 }
05061 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
05062 ast_channel_datastore_remove(qe->chan, tds);
05063 }
05064 ast_channel_unlock(qe->chan);
05065 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05066 } else {
05067 ast_channel_unlock(qe->chan);
05068
05069
05070 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05071 }
05072
05073 if (transfer_ds) {
05074 ast_datastore_free(transfer_ds);
05075 }
05076 ast_hangup(peer);
05077 res = bridge ? bridge : 1;
05078 ao2_ref(member, -1);
05079 }
05080 out:
05081 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05082
05083 return res;
05084 }
05085
05086 static int wait_a_bit(struct queue_ent *qe)
05087 {
05088
05089 int retrywait = qe->parent->retry * 1000;
05090
05091 int res = ast_waitfordigit(qe->chan, retrywait);
05092 if (res > 0 && !valid_exit(qe, res))
05093 res = 0;
05094
05095 return res;
05096 }
05097
05098 static struct member *interface_exists(struct call_queue *q, const char *interface)
05099 {
05100 struct member *mem;
05101 struct ao2_iterator mem_iter;
05102
05103 if (!q)
05104 return NULL;
05105
05106 mem_iter = ao2_iterator_init(q->members, 0);
05107 while ((mem = ao2_iterator_next(&mem_iter))) {
05108 if (!strcasecmp(interface, mem->interface)) {
05109 ao2_iterator_destroy(&mem_iter);
05110 return mem;
05111 }
05112 ao2_ref(mem, -1);
05113 }
05114 ao2_iterator_destroy(&mem_iter);
05115
05116 return NULL;
05117 }
05118
05119
05120
05121
05122
05123
05124 static void dump_queue_members(struct call_queue *pm_queue)
05125 {
05126 struct member *cur_member;
05127 char value[PM_MAX_LEN];
05128 int value_len = 0;
05129 int res;
05130 struct ao2_iterator mem_iter;
05131
05132 memset(value, 0, sizeof(value));
05133
05134 if (!pm_queue)
05135 return;
05136
05137 mem_iter = ao2_iterator_init(pm_queue->members, 0);
05138 while ((cur_member = ao2_iterator_next(&mem_iter))) {
05139 if (!cur_member->dynamic) {
05140 ao2_ref(cur_member, -1);
05141 continue;
05142 }
05143
05144 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
05145 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
05146
05147 ao2_ref(cur_member, -1);
05148
05149 if (res != strlen(value + value_len)) {
05150 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
05151 break;
05152 }
05153 value_len += res;
05154 }
05155 ao2_iterator_destroy(&mem_iter);
05156
05157 if (value_len && !cur_member) {
05158 if (ast_db_put(pm_family, pm_queue->name, value))
05159 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05160 } else
05161
05162 ast_db_del(pm_family, pm_queue->name);
05163 }
05164
05165
05166
05167
05168
05169
05170
05171 static int remove_from_queue(const char *queuename, const char *interface)
05172 {
05173 struct call_queue *q, tmpq = {
05174 .name = queuename,
05175 };
05176 struct member *mem, tmpmem;
05177 int res = RES_NOSUCHQUEUE;
05178
05179 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05180 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05181 ao2_lock(q);
05182 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05183
05184 if (!mem->dynamic) {
05185 ao2_ref(mem, -1);
05186 ao2_unlock(q);
05187 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05188 return RES_NOT_DYNAMIC;
05189 }
05190 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05191 "Queue: %s\r\n"
05192 "Location: %s\r\n"
05193 "MemberName: %s\r\n",
05194 q->name, mem->interface, mem->membername);
05195 ao2_unlink(q->members, mem);
05196 ao2_ref(mem, -1);
05197
05198 if (queue_persistent_members)
05199 dump_queue_members(q);
05200
05201 res = RES_OKAY;
05202 } else {
05203 res = RES_EXISTS;
05204 }
05205 ao2_unlock(q);
05206 queue_t_unref(q, "Expiring temporary reference");
05207 }
05208
05209 return res;
05210 }
05211
05212
05213
05214
05215
05216
05217
05218
05219 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
05220 {
05221 struct call_queue *q;
05222 struct member *new_member, *old_member;
05223 int res = RES_NOSUCHQUEUE;
05224
05225
05226
05227 if (!(q = load_realtime_queue(queuename)))
05228 return res;
05229
05230 ao2_lock(q);
05231 if ((old_member = interface_exists(q, interface)) == NULL) {
05232 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05233 new_member->dynamic = 1;
05234 ao2_link(q->members, new_member);
05235 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05236 "Queue: %s\r\n"
05237 "Location: %s\r\n"
05238 "MemberName: %s\r\n"
05239 "Membership: %s\r\n"
05240 "Penalty: %d\r\n"
05241 "CallsTaken: %d\r\n"
05242 "LastCall: %d\r\n"
05243 "Status: %d\r\n"
05244 "Paused: %d\r\n",
05245 q->name, new_member->interface, new_member->membername,
05246 "dynamic",
05247 new_member->penalty, new_member->calls, (int) new_member->lastcall,
05248 new_member->status, new_member->paused);
05249
05250 ao2_ref(new_member, -1);
05251 new_member = NULL;
05252
05253 if (dump)
05254 dump_queue_members(q);
05255
05256 res = RES_OKAY;
05257 } else {
05258 res = RES_OUTOFMEMORY;
05259 }
05260 } else {
05261 ao2_ref(old_member, -1);
05262 res = RES_EXISTS;
05263 }
05264 ao2_unlock(q);
05265 queue_t_unref(q, "Expiring temporary reference");
05266
05267 return res;
05268 }
05269
05270 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
05271 {
05272 int found = 0;
05273 struct call_queue *q;
05274 struct member *mem;
05275 struct ao2_iterator queue_iter;
05276 int failed;
05277
05278
05279
05280 if (ast_strlen_zero(queuename))
05281 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05282
05283 queue_iter = ao2_iterator_init(queues, 0);
05284 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05285 ao2_lock(q);
05286 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05287 if ((mem = interface_exists(q, interface))) {
05288 if (mem->paused == paused) {
05289 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05290 }
05291
05292 failed = 0;
05293 if (mem->realtime) {
05294 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05295 }
05296
05297 if (failed) {
05298 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05299 ao2_ref(mem, -1);
05300 ao2_unlock(q);
05301 queue_t_unref(q, "Done with iterator");
05302 continue;
05303 }
05304 found++;
05305 mem->paused = paused;
05306
05307 if (queue_persistent_members)
05308 dump_queue_members(q);
05309
05310 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05311
05312 if (!ast_strlen_zero(reason)) {
05313 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05314 "Queue: %s\r\n"
05315 "Location: %s\r\n"
05316 "MemberName: %s\r\n"
05317 "Paused: %d\r\n"
05318 "Reason: %s\r\n",
05319 q->name, mem->interface, mem->membername, paused, reason);
05320 } else {
05321 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05322 "Queue: %s\r\n"
05323 "Location: %s\r\n"
05324 "MemberName: %s\r\n"
05325 "Paused: %d\r\n",
05326 q->name, mem->interface, mem->membername, paused);
05327 }
05328 ao2_ref(mem, -1);
05329 }
05330 }
05331
05332 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05333 ao2_unlock(q);
05334 queue_t_unref(q, "Done with iterator");
05335 break;
05336 }
05337
05338 ao2_unlock(q);
05339 queue_t_unref(q, "Done with iterator");
05340 }
05341 ao2_iterator_destroy(&queue_iter);
05342
05343 return found ? RESULT_SUCCESS : RESULT_FAILURE;
05344 }
05345
05346
05347 static int set_member_penalty(const char *queuename, const char *interface, int penalty)
05348 {
05349 int foundinterface = 0, foundqueue = 0;
05350 struct call_queue *q;
05351 struct member *mem;
05352 struct ao2_iterator queue_iter;
05353
05354 if (penalty < 0) {
05355 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05356 return RESULT_FAILURE;
05357 }
05358
05359 queue_iter = ao2_iterator_init(queues, 0);
05360 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05361 ao2_lock(q);
05362 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05363 foundqueue++;
05364 if ((mem = interface_exists(q, interface))) {
05365 foundinterface++;
05366 mem->penalty = penalty;
05367
05368 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05369 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05370 "Queue: %s\r\n"
05371 "Location: %s\r\n"
05372 "Penalty: %d\r\n",
05373 q->name, mem->interface, penalty);
05374 ao2_ref(mem, -1);
05375 }
05376 }
05377 ao2_unlock(q);
05378 queue_t_unref(q, "Done with iterator");
05379 }
05380 ao2_iterator_destroy(&queue_iter);
05381
05382 if (foundinterface) {
05383 return RESULT_SUCCESS;
05384 } else if (!foundqueue) {
05385 ast_log (LOG_ERROR, "Invalid queuename\n");
05386 } else {
05387 ast_log (LOG_ERROR, "Invalid interface\n");
05388 }
05389
05390 return RESULT_FAILURE;
05391 }
05392
05393
05394
05395
05396 static int get_member_penalty(char *queuename, char *interface)
05397 {
05398 int foundqueue = 0, penalty;
05399 struct call_queue *q, tmpq = {
05400 .name = queuename,
05401 };
05402 struct member *mem;
05403
05404 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05405 foundqueue = 1;
05406 ao2_lock(q);
05407 if ((mem = interface_exists(q, interface))) {
05408 penalty = mem->penalty;
05409 ao2_ref(mem, -1);
05410 ao2_unlock(q);
05411 queue_t_unref(q, "Search complete");
05412 return penalty;
05413 }
05414 ao2_unlock(q);
05415 queue_t_unref(q, "Search complete");
05416 }
05417
05418
05419 if (foundqueue)
05420 ast_log (LOG_ERROR, "Invalid queuename\n");
05421 else
05422 ast_log (LOG_ERROR, "Invalid interface\n");
05423
05424 return RESULT_FAILURE;
05425 }
05426
05427
05428 static void reload_queue_members(void)
05429 {
05430 char *cur_ptr;
05431 const char *queue_name;
05432 char *member;
05433 char *interface;
05434 char *membername = NULL;
05435 char *state_interface;
05436 char *penalty_tok;
05437 int penalty = 0;
05438 char *paused_tok;
05439 int paused = 0;
05440 struct ast_db_entry *db_tree;
05441 struct ast_db_entry *entry;
05442 struct call_queue *cur_queue;
05443 char queue_data[PM_MAX_LEN];
05444
05445
05446 db_tree = ast_db_gettree(pm_family, NULL);
05447 for (entry = db_tree; entry; entry = entry->next) {
05448
05449 queue_name = entry->key + strlen(pm_family) + 2;
05450
05451 {
05452 struct call_queue tmpq = {
05453 .name = queue_name,
05454 };
05455 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05456 }
05457
05458 if (!cur_queue)
05459 cur_queue = load_realtime_queue(queue_name);
05460
05461 if (!cur_queue) {
05462
05463
05464 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05465 ast_db_del(pm_family, queue_name);
05466 continue;
05467 }
05468
05469 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05470 queue_t_unref(cur_queue, "Expire reload reference");
05471 continue;
05472 }
05473
05474 cur_ptr = queue_data;
05475 while ((member = strsep(&cur_ptr, ",|"))) {
05476 if (ast_strlen_zero(member))
05477 continue;
05478
05479 interface = strsep(&member, ";");
05480 penalty_tok = strsep(&member, ";");
05481 paused_tok = strsep(&member, ";");
05482 membername = strsep(&member, ";");
05483 state_interface = strsep(&member, ";");
05484
05485 if (!penalty_tok) {
05486 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05487 break;
05488 }
05489 penalty = strtol(penalty_tok, NULL, 10);
05490 if (errno == ERANGE) {
05491 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05492 break;
05493 }
05494
05495 if (!paused_tok) {
05496 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05497 break;
05498 }
05499 paused = strtol(paused_tok, NULL, 10);
05500 if ((errno == ERANGE) || paused < 0 || paused > 1) {
05501 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05502 break;
05503 }
05504
05505 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
05506
05507 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05508 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05509 break;
05510 }
05511 }
05512 queue_t_unref(cur_queue, "Expire reload reference");
05513 }
05514
05515 if (db_tree) {
05516 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05517 ast_db_freetree(db_tree);
05518 }
05519 }
05520
05521
05522 static int pqm_exec(struct ast_channel *chan, const char *data)
05523 {
05524 char *parse;
05525 AST_DECLARE_APP_ARGS(args,
05526 AST_APP_ARG(queuename);
05527 AST_APP_ARG(interface);
05528 AST_APP_ARG(options);
05529 AST_APP_ARG(reason);
05530 );
05531
05532 if (ast_strlen_zero(data)) {
05533 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05534 return -1;
05535 }
05536
05537 parse = ast_strdupa(data);
05538
05539 AST_STANDARD_APP_ARGS(args, parse);
05540
05541 if (ast_strlen_zero(args.interface)) {
05542 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05543 return -1;
05544 }
05545
05546 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05547 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05548 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05549 return 0;
05550 }
05551
05552 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05553
05554 return 0;
05555 }
05556
05557
05558 static int upqm_exec(struct ast_channel *chan, const char *data)
05559 {
05560 char *parse;
05561 AST_DECLARE_APP_ARGS(args,
05562 AST_APP_ARG(queuename);
05563 AST_APP_ARG(interface);
05564 AST_APP_ARG(options);
05565 AST_APP_ARG(reason);
05566 );
05567
05568 if (ast_strlen_zero(data)) {
05569 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05570 return -1;
05571 }
05572
05573 parse = ast_strdupa(data);
05574
05575 AST_STANDARD_APP_ARGS(args, parse);
05576
05577 if (ast_strlen_zero(args.interface)) {
05578 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05579 return -1;
05580 }
05581
05582 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05583 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05584 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05585 return 0;
05586 }
05587
05588 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05589
05590 return 0;
05591 }
05592
05593
05594 static int rqm_exec(struct ast_channel *chan, const char *data)
05595 {
05596 int res=-1;
05597 char *parse, *temppos = NULL;
05598 AST_DECLARE_APP_ARGS(args,
05599 AST_APP_ARG(queuename);
05600 AST_APP_ARG(interface);
05601 AST_APP_ARG(options);
05602 );
05603
05604
05605 if (ast_strlen_zero(data)) {
05606 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05607 return -1;
05608 }
05609
05610 parse = ast_strdupa(data);
05611
05612 AST_STANDARD_APP_ARGS(args, parse);
05613
05614 if (ast_strlen_zero(args.interface)) {
05615 args.interface = ast_strdupa(chan->name);
05616 temppos = strrchr(args.interface, '-');
05617 if (temppos)
05618 *temppos = '\0';
05619 }
05620
05621 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05622
05623 switch (remove_from_queue(args.queuename, args.interface)) {
05624 case RES_OKAY:
05625 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05626 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05627 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05628 res = 0;
05629 break;
05630 case RES_EXISTS:
05631 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05632 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05633 res = 0;
05634 break;
05635 case RES_NOSUCHQUEUE:
05636 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05637 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05638 res = 0;
05639 break;
05640 case RES_NOT_DYNAMIC:
05641 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05642 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05643 res = 0;
05644 break;
05645 }
05646
05647 return res;
05648 }
05649
05650
05651 static int aqm_exec(struct ast_channel *chan, const char *data)
05652 {
05653 int res=-1;
05654 char *parse, *temppos = NULL;
05655 AST_DECLARE_APP_ARGS(args,
05656 AST_APP_ARG(queuename);
05657 AST_APP_ARG(interface);
05658 AST_APP_ARG(penalty);
05659 AST_APP_ARG(options);
05660 AST_APP_ARG(membername);
05661 AST_APP_ARG(state_interface);
05662 );
05663 int penalty = 0;
05664
05665 if (ast_strlen_zero(data)) {
05666 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05667 return -1;
05668 }
05669
05670 parse = ast_strdupa(data);
05671
05672 AST_STANDARD_APP_ARGS(args, parse);
05673
05674 if (ast_strlen_zero(args.interface)) {
05675 args.interface = ast_strdupa(chan->name);
05676 temppos = strrchr(args.interface, '-');
05677 if (temppos)
05678 *temppos = '\0';
05679 }
05680
05681 if (!ast_strlen_zero(args.penalty)) {
05682 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05683 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05684 penalty = 0;
05685 }
05686 }
05687
05688 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05689 case RES_OKAY:
05690 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05691 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05692 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05693 res = 0;
05694 break;
05695 case RES_EXISTS:
05696 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05697 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05698 res = 0;
05699 break;
05700 case RES_NOSUCHQUEUE:
05701 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05702 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05703 res = 0;
05704 break;
05705 case RES_OUTOFMEMORY:
05706 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
05707 break;
05708 }
05709
05710 return res;
05711 }
05712
05713
05714 static int ql_exec(struct ast_channel *chan, const char *data)
05715 {
05716 char *parse;
05717
05718 AST_DECLARE_APP_ARGS(args,
05719 AST_APP_ARG(queuename);
05720 AST_APP_ARG(uniqueid);
05721 AST_APP_ARG(membername);
05722 AST_APP_ARG(event);
05723 AST_APP_ARG(params);
05724 );
05725
05726 if (ast_strlen_zero(data)) {
05727 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05728 return -1;
05729 }
05730
05731 parse = ast_strdupa(data);
05732
05733 AST_STANDARD_APP_ARGS(args, parse);
05734
05735 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05736 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05737 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05738 return -1;
05739 }
05740
05741 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
05742 "%s", args.params ? args.params : "");
05743
05744 return 0;
05745 }
05746
05747
05748 static void copy_rules(struct queue_ent *qe, const char *rulename)
05749 {
05750 struct penalty_rule *pr_iter;
05751 struct rule_list *rl_iter;
05752 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05753 AST_LIST_LOCK(&rule_lists);
05754 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05755 if (!strcasecmp(rl_iter->name, tmp))
05756 break;
05757 }
05758 if (rl_iter) {
05759 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05760 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05761 if (!new_pr) {
05762 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05763 break;
05764 }
05765 new_pr->time = pr_iter->time;
05766 new_pr->max_value = pr_iter->max_value;
05767 new_pr->min_value = pr_iter->min_value;
05768 new_pr->max_relative = pr_iter->max_relative;
05769 new_pr->min_relative = pr_iter->min_relative;
05770 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05771 }
05772 }
05773 AST_LIST_UNLOCK(&rule_lists);
05774 }
05775
05776
05777
05778
05779
05780
05781
05782
05783
05784
05785
05786
05787
05788 static int queue_exec(struct ast_channel *chan, const char *data)
05789 {
05790 int res=-1;
05791 int ringing=0;
05792 const char *user_priority;
05793 const char *max_penalty_str;
05794 const char *min_penalty_str;
05795 int prio;
05796 int qcontinue = 0;
05797 int max_penalty, min_penalty;
05798 enum queue_result reason = QUEUE_UNKNOWN;
05799
05800 int tries = 0;
05801 int noption = 0;
05802 char *parse;
05803 int makeannouncement = 0;
05804 int position = 0;
05805 AST_DECLARE_APP_ARGS(args,
05806 AST_APP_ARG(queuename);
05807 AST_APP_ARG(options);
05808 AST_APP_ARG(url);
05809 AST_APP_ARG(announceoverride);
05810 AST_APP_ARG(queuetimeoutstr);
05811 AST_APP_ARG(agi);
05812 AST_APP_ARG(macro);
05813 AST_APP_ARG(gosub);
05814 AST_APP_ARG(rule);
05815 AST_APP_ARG(position);
05816 );
05817
05818 struct queue_ent qe = { 0 };
05819
05820 if (ast_strlen_zero(data)) {
05821 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
05822 return -1;
05823 }
05824
05825 parse = ast_strdupa(data);
05826 AST_STANDARD_APP_ARGS(args, parse);
05827
05828
05829 qe.start = time(NULL);
05830
05831
05832 if (!ast_strlen_zero(args.queuetimeoutstr))
05833 qe.expire = qe.start + atoi(args.queuetimeoutstr);
05834 else
05835 qe.expire = 0;
05836
05837
05838 ast_channel_lock(chan);
05839 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05840 if (user_priority) {
05841 if (sscanf(user_priority, "%30d", &prio) == 1) {
05842 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05843 } else {
05844 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05845 user_priority, chan->name);
05846 prio = 0;
05847 }
05848 } else {
05849 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05850 prio = 0;
05851 }
05852
05853
05854
05855 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05856 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05857 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05858 } else {
05859 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05860 max_penalty_str, chan->name);
05861 max_penalty = 0;
05862 }
05863 } else {
05864 max_penalty = 0;
05865 }
05866
05867 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05868 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05869 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05870 } else {
05871 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05872 min_penalty_str, chan->name);
05873 min_penalty = 0;
05874 }
05875 } else {
05876 min_penalty = 0;
05877 }
05878 ast_channel_unlock(chan);
05879
05880 if (args.options && (strchr(args.options, 'r')))
05881 ringing = 1;
05882
05883 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
05884 qe.ring_when_ringing = 1;
05885 }
05886
05887 if (args.options && (strchr(args.options, 'c')))
05888 qcontinue = 1;
05889
05890 if (args.position) {
05891 position = atoi(args.position);
05892 if (position < 0) {
05893 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
05894 position = 0;
05895 }
05896 }
05897
05898 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05899 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05900
05901 qe.chan = chan;
05902 qe.prio = prio;
05903 qe.max_penalty = max_penalty;
05904 qe.min_penalty = min_penalty;
05905 qe.last_pos_said = 0;
05906 qe.last_pos = 0;
05907 qe.last_periodic_announce_time = time(NULL);
05908 qe.last_periodic_announce_sound = 0;
05909 qe.valid_digits = 0;
05910 if (join_queue(args.queuename, &qe, &reason, position)) {
05911 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05912 set_queue_result(chan, reason);
05913 return 0;
05914 }
05915 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
05916 S_OR(args.url, ""),
05917 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
05918 qe.opos);
05919 copy_rules(&qe, args.rule);
05920 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
05921 check_turns:
05922 if (ringing) {
05923 ast_indicate(chan, AST_CONTROL_RINGING);
05924 } else {
05925 ast_moh_start(chan, qe.moh, NULL);
05926 }
05927
05928
05929 res = wait_our_turn(&qe, ringing, &reason);
05930 if (res) {
05931 goto stop;
05932 }
05933
05934 makeannouncement = 0;
05935
05936 for (;;) {
05937
05938
05939
05940
05941
05942
05943 if (qe.expire && (time(NULL) >= qe.expire)) {
05944 record_abandoned(&qe);
05945 reason = QUEUE_TIMEOUT;
05946 res = 0;
05947 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
05948 qe.pos, qe.opos, (long) time(NULL) - qe.start);
05949 break;
05950 }
05951
05952 if (makeannouncement) {
05953
05954 if (qe.parent->announcefrequency)
05955 if ((res = say_position(&qe,ringing)))
05956 goto stop;
05957 }
05958 makeannouncement = 1;
05959
05960
05961 if (qe.parent->periodicannouncefrequency)
05962 if ((res = say_periodic_announcement(&qe,ringing)))
05963 goto stop;
05964
05965
05966 if (qe.expire && (time(NULL) >= qe.expire)) {
05967 record_abandoned(&qe);
05968 reason = QUEUE_TIMEOUT;
05969 res = 0;
05970 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05971 break;
05972 }
05973
05974
05975 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05976 update_qe_rule(&qe);
05977 }
05978
05979
05980 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05981 if (res) {
05982 goto stop;
05983 }
05984
05985 if (qe.parent->leavewhenempty) {
05986 int status = 0;
05987 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
05988 record_abandoned(&qe);
05989 reason = QUEUE_LEAVEEMPTY;
05990 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05991 res = 0;
05992 break;
05993 }
05994 }
05995
05996
05997 if (noption && tries >= ao2_container_count(qe.parent->members)) {
05998 ast_verb(3, "Exiting on time-out cycle\n");
05999 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06000 record_abandoned(&qe);
06001 reason = QUEUE_TIMEOUT;
06002 res = 0;
06003 break;
06004 }
06005
06006
06007
06008 if (qe.expire && (time(NULL) >= qe.expire)) {
06009 record_abandoned(&qe);
06010 reason = QUEUE_TIMEOUT;
06011 res = 0;
06012 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06013 break;
06014 }
06015
06016
06017 update_realtime_members(qe.parent);
06018
06019 res = wait_a_bit(&qe);
06020 if (res)
06021 goto stop;
06022
06023
06024
06025
06026
06027 if (!is_our_turn(&qe)) {
06028 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06029 goto check_turns;
06030 }
06031 }
06032
06033 stop:
06034 if (res) {
06035 if (res < 0) {
06036 if (!qe.handled) {
06037 record_abandoned(&qe);
06038 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06039 "%d|%d|%ld", qe.pos, qe.opos,
06040 (long) time(NULL) - qe.start);
06041 res = -1;
06042 } else if (qcontinue) {
06043 reason = QUEUE_CONTINUE;
06044 res = 0;
06045 }
06046 } else if (qe.valid_digits) {
06047 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06048 "%s|%d", qe.digits, qe.pos);
06049 }
06050 }
06051
06052
06053 if (res >= 0) {
06054 res = 0;
06055 if (ringing) {
06056 ast_indicate(chan, -1);
06057 } else {
06058 ast_moh_stop(chan);
06059 }
06060 ast_stopstream(chan);
06061 }
06062
06063 set_queue_variables(qe.parent, qe.chan);
06064
06065 leave_queue(&qe);
06066 if (reason != QUEUE_UNKNOWN)
06067 set_queue_result(chan, reason);
06068
06069 if (qe.parent) {
06070
06071
06072
06073 qe.parent = queue_unref(qe.parent);
06074 }
06075
06076 return res;
06077 }
06078
06079
06080
06081
06082
06083
06084 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06085 {
06086 int res = -1;
06087 struct call_queue *q, tmpq = {
06088 .name = data,
06089 };
06090
06091 char interfacevar[256] = "";
06092 float sl = 0;
06093
06094 if (ast_strlen_zero(data)) {
06095 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06096 return -1;
06097 }
06098
06099 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06100 ao2_lock(q);
06101 if (q->setqueuevar) {
06102 sl = 0;
06103 res = 0;
06104
06105 if (q->callscompleted > 0) {
06106 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06107 }
06108
06109 snprintf(interfacevar, sizeof(interfacevar),
06110 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06111 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
06112
06113 pbx_builtin_setvar_multiple(chan, interfacevar);
06114 }
06115
06116 ao2_unlock(q);
06117 queue_t_unref(q, "Done with QUEUE() function");
06118 } else {
06119 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06120 }
06121
06122 snprintf(buf, len, "%d", res);
06123
06124 return 0;
06125 }
06126
06127
06128
06129
06130
06131 static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06132 {
06133 struct call_queue *q;
06134
06135 buf[0] = '\0';
06136
06137 if (ast_strlen_zero(data)) {
06138 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06139 return -1;
06140 }
06141 q = load_realtime_queue(data);
06142 snprintf(buf, len, "%d", q != NULL? 1 : 0);
06143 if (q) {
06144 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06145 }
06146
06147 return 0;
06148 }
06149
06150
06151
06152
06153
06154
06155 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06156 {
06157 int count = 0;
06158 struct member *m;
06159 struct ao2_iterator mem_iter;
06160 struct call_queue *q;
06161 char *option;
06162
06163 if (ast_strlen_zero(data)) {
06164 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06165 return -1;
06166 }
06167
06168 if ((option = strchr(data, ',')))
06169 *option++ = '\0';
06170 else
06171 option = "logged";
06172 if ((q = load_realtime_queue(data))) {
06173 ao2_lock(q);
06174 if (!strcasecmp(option, "logged")) {
06175 mem_iter = ao2_iterator_init(q->members, 0);
06176 while ((m = ao2_iterator_next(&mem_iter))) {
06177
06178 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06179 count++;
06180 }
06181 ao2_ref(m, -1);
06182 }
06183 ao2_iterator_destroy(&mem_iter);
06184 } else if (!strcasecmp(option, "free")) {
06185 mem_iter = ao2_iterator_init(q->members, 0);
06186 while ((m = ao2_iterator_next(&mem_iter))) {
06187
06188 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06189 count++;
06190 }
06191 ao2_ref(m, -1);
06192 }
06193 ao2_iterator_destroy(&mem_iter);
06194 } else if (!strcasecmp(option, "ready")) {
06195 time_t now;
06196 time(&now);
06197 mem_iter = ao2_iterator_init(q->members, 0);
06198 while ((m = ao2_iterator_next(&mem_iter))) {
06199
06200 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06201 !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06202 count++;
06203 }
06204 ao2_ref(m, -1);
06205 }
06206 ao2_iterator_destroy(&mem_iter);
06207 } else
06208 count = ao2_container_count(q->members);
06209 ao2_unlock(q);
06210 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06211 } else
06212 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06213
06214 snprintf(buf, len, "%d", count);
06215
06216 return 0;
06217 }
06218
06219
06220
06221
06222
06223
06224 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06225 {
06226 int count = 0;
06227 struct member *m;
06228 struct call_queue *q;
06229 struct ao2_iterator mem_iter;
06230 static int depflag = 1;
06231
06232 if (depflag) {
06233 depflag = 0;
06234 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");
06235 }
06236
06237 if (ast_strlen_zero(data)) {
06238 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06239 return -1;
06240 }
06241
06242 if ((q = load_realtime_queue(data))) {
06243 ao2_lock(q);
06244 mem_iter = ao2_iterator_init(q->members, 0);
06245 while ((m = ao2_iterator_next(&mem_iter))) {
06246
06247 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06248 count++;
06249 }
06250 ao2_ref(m, -1);
06251 }
06252 ao2_iterator_destroy(&mem_iter);
06253 ao2_unlock(q);
06254 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06255 } else
06256 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06257
06258 snprintf(buf, len, "%d", count);
06259
06260 return 0;
06261 }
06262
06263
06264 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06265 {
06266 int count = 0;
06267 struct call_queue *q, tmpq = {
06268 .name = data,
06269 };
06270 struct ast_variable *var = NULL;
06271
06272 buf[0] = '\0';
06273
06274 if (ast_strlen_zero(data)) {
06275 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06276 return -1;
06277 }
06278
06279 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06280 ao2_lock(q);
06281 count = q->count;
06282 ao2_unlock(q);
06283 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06284 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06285
06286
06287
06288
06289 count = 0;
06290 ast_variables_destroy(var);
06291 } else
06292 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06293
06294 snprintf(buf, len, "%d", count);
06295
06296 return 0;
06297 }
06298
06299
06300 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06301 {
06302 struct call_queue *q, tmpq = {
06303 .name = data,
06304 };
06305 struct member *m;
06306
06307
06308 buf[0] = '\0';
06309
06310 if (ast_strlen_zero(data)) {
06311 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06312 return -1;
06313 }
06314
06315 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06316 int buflen = 0, count = 0;
06317 struct ao2_iterator mem_iter;
06318
06319 ao2_lock(q);
06320 mem_iter = ao2_iterator_init(q->members, 0);
06321 while ((m = ao2_iterator_next(&mem_iter))) {
06322
06323 if (count++) {
06324 strncat(buf + buflen, ",", len - buflen - 1);
06325 buflen++;
06326 }
06327 strncat(buf + buflen, m->interface, len - buflen - 1);
06328 buflen += strlen(m->interface);
06329
06330 if (buflen >= len - 2) {
06331 ao2_ref(m, -1);
06332 ast_log(LOG_WARNING, "Truncating list\n");
06333 break;
06334 }
06335 ao2_ref(m, -1);
06336 }
06337 ao2_iterator_destroy(&mem_iter);
06338 ao2_unlock(q);
06339 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06340 } else
06341 ast_log(LOG_WARNING, "queue %s was not found\n", data);
06342
06343
06344 buf[len - 1] = '\0';
06345
06346 return 0;
06347 }
06348
06349
06350 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06351 {
06352 int penalty;
06353 AST_DECLARE_APP_ARGS(args,
06354 AST_APP_ARG(queuename);
06355 AST_APP_ARG(interface);
06356 );
06357
06358 buf[0] = '\0';
06359
06360 if (ast_strlen_zero(data)) {
06361 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06362 return -1;
06363 }
06364
06365 AST_STANDARD_APP_ARGS(args, data);
06366
06367 if (args.argc < 2) {
06368 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06369 return -1;
06370 }
06371
06372 penalty = get_member_penalty (args.queuename, args.interface);
06373
06374 if (penalty >= 0)
06375 snprintf (buf, len, "%d", penalty);
06376
06377 return 0;
06378 }
06379
06380
06381 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
06382 {
06383 int penalty;
06384 AST_DECLARE_APP_ARGS(args,
06385 AST_APP_ARG(queuename);
06386 AST_APP_ARG(interface);
06387 );
06388
06389 if (ast_strlen_zero(data)) {
06390 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06391 return -1;
06392 }
06393
06394 AST_STANDARD_APP_ARGS(args, data);
06395
06396 if (args.argc < 2) {
06397 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06398 return -1;
06399 }
06400
06401 penalty = atoi(value);
06402
06403 if (ast_strlen_zero(args.interface)) {
06404 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06405 return -1;
06406 }
06407
06408
06409 if (set_member_penalty(args.queuename, args.interface, penalty)) {
06410 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06411 return -1;
06412 }
06413
06414 return 0;
06415 }
06416
06417 static struct ast_custom_function queueexists_function = {
06418 .name = "QUEUE_EXISTS",
06419 .read = queue_function_exists,
06420 };
06421
06422 static struct ast_custom_function queuevar_function = {
06423 .name = "QUEUE_VARIABLES",
06424 .read = queue_function_var,
06425 };
06426
06427 static struct ast_custom_function queuemembercount_function = {
06428 .name = "QUEUE_MEMBER",
06429 .read = queue_function_qac,
06430 };
06431
06432 static struct ast_custom_function queuemembercount_dep = {
06433 .name = "QUEUE_MEMBER_COUNT",
06434 .read = queue_function_qac_dep,
06435 };
06436
06437 static struct ast_custom_function queuewaitingcount_function = {
06438 .name = "QUEUE_WAITING_COUNT",
06439 .read = queue_function_queuewaitingcount,
06440 };
06441
06442 static struct ast_custom_function queuememberlist_function = {
06443 .name = "QUEUE_MEMBER_LIST",
06444 .read = queue_function_queuememberlist,
06445 };
06446
06447 static struct ast_custom_function queuememberpenalty_function = {
06448 .name = "QUEUE_MEMBER_PENALTY",
06449 .read = queue_function_memberpenalty_read,
06450 .write = queue_function_memberpenalty_write,
06451 };
06452
06453
06454
06455
06456
06457
06458
06459 static int reload_queue_rules(int reload)
06460 {
06461 struct ast_config *cfg;
06462 struct rule_list *rl_iter, *new_rl;
06463 struct penalty_rule *pr_iter;
06464 char *rulecat = NULL;
06465 struct ast_variable *rulevar = NULL;
06466 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06467
06468 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06469 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06470 return AST_MODULE_LOAD_SUCCESS;
06471 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06472 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06473 return AST_MODULE_LOAD_SUCCESS;
06474 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06475 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
06476 return AST_MODULE_LOAD_SUCCESS;
06477 }
06478
06479 AST_LIST_LOCK(&rule_lists);
06480 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06481 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06482 ast_free(pr_iter);
06483 ast_free(rl_iter);
06484 }
06485 while ((rulecat = ast_category_browse(cfg, rulecat))) {
06486 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06487 AST_LIST_UNLOCK(&rule_lists);
06488 ast_config_destroy(cfg);
06489 return AST_MODULE_LOAD_FAILURE;
06490 } else {
06491 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06492 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06493 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06494 if(!strcasecmp(rulevar->name, "penaltychange"))
06495 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06496 else
06497 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06498 }
06499 }
06500 AST_LIST_UNLOCK(&rule_lists);
06501
06502 ast_config_destroy(cfg);
06503
06504 return AST_MODULE_LOAD_SUCCESS;
06505 }
06506
06507
06508 static void queue_set_global_params(struct ast_config *cfg)
06509 {
06510 const char *general_val = NULL;
06511 queue_persistent_members = 0;
06512 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06513 queue_persistent_members = ast_true(general_val);
06514 autofill_default = 0;
06515 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06516 autofill_default = ast_true(general_val);
06517 montype_default = 0;
06518 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06519 if (!strcasecmp(general_val, "mixmonitor"))
06520 montype_default = 1;
06521 }
06522 update_cdr = 0;
06523 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06524 update_cdr = ast_true(general_val);
06525 shared_lastcall = 0;
06526 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06527 shared_lastcall = ast_true(general_val);
06528 }
06529
06530
06531
06532
06533
06534
06535
06536
06537
06538 static void reload_single_member(const char *memberdata, struct call_queue *q)
06539 {
06540 char *membername, *interface, *state_interface, *tmp;
06541 char *parse;
06542 struct member *cur, *newm;
06543 struct member tmpmem;
06544 int penalty;
06545 AST_DECLARE_APP_ARGS(args,
06546 AST_APP_ARG(interface);
06547 AST_APP_ARG(penalty);
06548 AST_APP_ARG(membername);
06549 AST_APP_ARG(state_interface);
06550 );
06551
06552 if (ast_strlen_zero(memberdata)) {
06553 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06554 return;
06555 }
06556
06557
06558 parse = ast_strdupa(memberdata);
06559
06560 AST_STANDARD_APP_ARGS(args, parse);
06561
06562 interface = args.interface;
06563 if (!ast_strlen_zero(args.penalty)) {
06564 tmp = args.penalty;
06565 ast_strip(tmp);
06566 penalty = atoi(tmp);
06567 if (penalty < 0) {
06568 penalty = 0;
06569 }
06570 } else {
06571 penalty = 0;
06572 }
06573
06574 if (!ast_strlen_zero(args.membername)) {
06575 membername = args.membername;
06576 ast_strip(membername);
06577 } else {
06578 membername = interface;
06579 }
06580
06581 if (!ast_strlen_zero(args.state_interface)) {
06582 state_interface = args.state_interface;
06583 ast_strip(state_interface);
06584 } else {
06585 state_interface = interface;
06586 }
06587
06588
06589 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06590 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06591 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06592 ao2_link(q->members, newm);
06593 ao2_ref(newm, -1);
06594 }
06595 newm = NULL;
06596
06597 if (cur) {
06598 ao2_ref(cur, -1);
06599 }
06600 }
06601
06602 static int mark_member_dead(void *obj, void *arg, int flags)
06603 {
06604 struct member *member = obj;
06605 if (!member->dynamic) {
06606 member->delme = 1;
06607 }
06608 return 0;
06609 }
06610
06611 static int kill_dead_members(void *obj, void *arg, int flags)
06612 {
06613 struct member *member = obj;
06614
06615 if (!member->delme) {
06616 member->status = get_queue_member_status(member);
06617 return 0;
06618 } else {
06619 return CMP_MATCH;
06620 }
06621 }
06622
06623
06624
06625
06626
06627
06628
06629
06630
06631
06632
06633
06634 static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
06635 {
06636 int new;
06637 struct call_queue *q = NULL;
06638
06639 struct call_queue tmpq = {
06640 .name = queuename,
06641 };
06642 const char *tmpvar;
06643 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06644 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06645 int prev_weight = 0;
06646 struct ast_variable *var;
06647 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06648 if (queue_reload) {
06649
06650 if (!(q = alloc_queue(queuename))) {
06651 return;
06652 }
06653 } else {
06654
06655
06656
06657 return;
06658 }
06659 new = 1;
06660 } else {
06661 new = 0;
06662 }
06663
06664 if (!new) {
06665 ao2_lock(q);
06666 prev_weight = q->weight ? 1 : 0;
06667 }
06668
06669 if (q->found) {
06670 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
06671 if (!new) {
06672
06673 ao2_unlock(q);
06674 }
06675 queue_t_unref(q, "We exist! Expiring temporary pointer");
06676 return;
06677 }
06678
06679
06680
06681
06682
06683 if (queue_reload) {
06684 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
06685 q->strategy = strat2int(tmpvar);
06686 if (q->strategy < 0) {
06687 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
06688 tmpvar, q->name);
06689 q->strategy = QUEUE_STRATEGY_RINGALL;
06690 }
06691 } else {
06692 q->strategy = QUEUE_STRATEGY_RINGALL;
06693 }
06694 init_queue(q);
06695 }
06696 if (member_reload) {
06697 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
06698 }
06699 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
06700 if (member_reload && !strcasecmp(var->name, "member")) {
06701 reload_single_member(var->value, q);
06702 } else if (queue_reload) {
06703 queue_set_param(q, var->name, var->value, var->lineno, 1);
06704 }
06705 }
06706
06707
06708
06709 if (!q->weight && prev_weight) {
06710 ast_atomic_fetchadd_int(&use_weight, -1);
06711 }
06712 else if (q->weight && !prev_weight) {
06713 ast_atomic_fetchadd_int(&use_weight, +1);
06714 }
06715
06716
06717 if (member_reload) {
06718 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
06719 }
06720
06721 if (new) {
06722 queues_t_link(queues, q, "Add queue to container");
06723 } else {
06724 ao2_unlock(q);
06725 }
06726 queue_t_unref(q, "Expiring creation reference");
06727 }
06728
06729 static int mark_dead_and_unfound(void *obj, void *arg, int flags)
06730 {
06731 struct call_queue *q = obj;
06732 char *queuename = arg;
06733 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
06734 q->dead = 1;
06735 q->found = 0;
06736 }
06737 return 0;
06738 }
06739
06740 static int kill_dead_queues(void *obj, void *arg, int flags)
06741 {
06742 struct call_queue *q = obj;
06743 char *queuename = arg;
06744 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
06745 return CMP_MATCH;
06746 } else {
06747 return 0;
06748 }
06749 }
06750
06751
06752
06753
06754
06755
06756
06757
06758
06759
06760
06761
06762
06763 static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
06764 {
06765 struct ast_config *cfg;
06766 char *cat;
06767 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06768 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06769
06770 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
06771 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
06772 return -1;
06773 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06774 return 0;
06775 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06776 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
06777 return -1;
06778 }
06779
06780
06781 ao2_lock(queues);
06782
06783
06784
06785
06786 if (queue_reload) {
06787 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
06788 }
06789
06790
06791 cat = NULL;
06792 while ((cat = ast_category_browse(cfg, cat)) ) {
06793 if (!strcasecmp(cat, "general") && queue_reload) {
06794 queue_set_global_params(cfg);
06795 continue;
06796 }
06797 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
06798 reload_single_queue(cfg, mask, cat);
06799 }
06800
06801 ast_config_destroy(cfg);
06802
06803 if (queue_reload) {
06804 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06805 }
06806 ao2_unlock(queues);
06807 return 0;
06808 }
06809
06810
06811
06812
06813
06814
06815
06816
06817
06818
06819
06820
06821
06822
06823 static int clear_stats(const char *queuename)
06824 {
06825 struct call_queue *q;
06826 struct ao2_iterator queue_iter;
06827
06828 queue_iter = ao2_iterator_init(queues, 0);
06829 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06830 ao2_lock(q);
06831 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06832 clear_queue(q);
06833 ao2_unlock(q);
06834 queue_t_unref(q, "Done with iterator");
06835 }
06836 ao2_iterator_destroy(&queue_iter);
06837 return 0;
06838 }
06839
06840
06841
06842
06843
06844
06845
06846
06847
06848
06849
06850
06851
06852
06853 static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
06854 {
06855 int res = 0;
06856
06857 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06858 res |= reload_queue_rules(reload);
06859 }
06860 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06861 res |= clear_stats(queuename);
06862 }
06863 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06864 res |= reload_queues(reload, mask, queuename);
06865 }
06866 return res;
06867 }
06868
06869
06870 static void do_print(struct mansession *s, int fd, const char *str)
06871 {
06872 if (s)
06873 astman_append(s, "%s\r\n", str);
06874 else
06875 ast_cli(fd, "%s\n", str);
06876 }
06877
06878
06879
06880
06881
06882
06883
06884 static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
06885 {
06886 struct call_queue *q;
06887 struct ast_str *out = ast_str_alloca(240);
06888 int found = 0;
06889 time_t now = time(NULL);
06890 struct ao2_iterator queue_iter;
06891 struct ao2_iterator mem_iter;
06892
06893 if (argc != 2 && argc != 3)
06894 return CLI_SHOWUSAGE;
06895
06896 if (argc == 3) {
06897 if ((q = load_realtime_queue(argv[2]))) {
06898 queue_t_unref(q, "Done with temporary pointer");
06899 }
06900 } else if (ast_check_realtime("queues")) {
06901
06902
06903
06904 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06905 char *queuename;
06906 if (cfg) {
06907 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06908 if ((q = load_realtime_queue(queuename))) {
06909 queue_t_unref(q, "Done with temporary pointer");
06910 }
06911 }
06912 ast_config_destroy(cfg);
06913 }
06914 }
06915
06916 ao2_lock(queues);
06917 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
06918 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06919 float sl;
06920 struct call_queue *realtime_queue = NULL;
06921
06922 ao2_lock(q);
06923
06924
06925
06926
06927 if (q->realtime) {
06928 realtime_queue = load_realtime_queue(q->name);
06929 if (!realtime_queue) {
06930 ao2_unlock(q);
06931 queue_t_unref(q, "Done with iterator");
06932 continue;
06933 }
06934 queue_t_unref(realtime_queue, "Queue is already in memory");
06935 }
06936
06937 if (argc == 3 && strcasecmp(q->name, argv[2])) {
06938 ao2_unlock(q);
06939 queue_t_unref(q, "Done with iterator");
06940 continue;
06941 }
06942 found = 1;
06943
06944 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
06945 if (q->maxlen)
06946 ast_str_append(&out, 0, "%d", q->maxlen);
06947 else
06948 ast_str_append(&out, 0, "unlimited");
06949 sl = 0;
06950 if (q->callscompleted > 0)
06951 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06952 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
06953 int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
06954 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
06955 do_print(s, fd, ast_str_buffer(out));
06956 if (!ao2_container_count(q->members))
06957 do_print(s, fd, " No Members");
06958 else {
06959 struct member *mem;
06960
06961 do_print(s, fd, " Members: ");
06962 mem_iter = ao2_iterator_init(q->members, 0);
06963 while ((mem = ao2_iterator_next(&mem_iter))) {
06964 ast_str_set(&out, 0, " %s", mem->membername);
06965 if (strcasecmp(mem->membername, mem->interface)) {
06966 ast_str_append(&out, 0, " (%s)", mem->interface);
06967 }
06968 if (mem->penalty)
06969 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
06970 ast_str_append(&out, 0, "%s%s%s (%s)",
06971 mem->dynamic ? " (dynamic)" : "",
06972 mem->realtime ? " (realtime)" : "",
06973 mem->paused ? " (paused)" : "",
06974 ast_devstate2str(mem->status));
06975 if (mem->calls)
06976 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
06977 mem->calls, (long) (time(NULL) - mem->lastcall));
06978 else
06979 ast_str_append(&out, 0, " has taken no calls yet");
06980 do_print(s, fd, ast_str_buffer(out));
06981 ao2_ref(mem, -1);
06982 }
06983 ao2_iterator_destroy(&mem_iter);
06984 }
06985 if (!q->head)
06986 do_print(s, fd, " No Callers");
06987 else {
06988 struct queue_ent *qe;
06989 int pos = 1;
06990
06991 do_print(s, fd, " Callers: ");
06992 for (qe = q->head; qe; qe = qe->next) {
06993 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
06994 pos++, qe->chan->name, (long) (now - qe->start) / 60,
06995 (long) (now - qe->start) % 60, qe->prio);
06996 do_print(s, fd, ast_str_buffer(out));
06997 }
06998 }
06999 do_print(s, fd, "");
07000 ao2_unlock(q);
07001 queue_t_unref(q, "Done with iterator");
07002 }
07003 ao2_iterator_destroy(&queue_iter);
07004 ao2_unlock(queues);
07005 if (!found) {
07006 if (argc == 3)
07007 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07008 else
07009 ast_str_set(&out, 0, "No queues.");
07010 do_print(s, fd, ast_str_buffer(out));
07011 }
07012 return CLI_SUCCESS;
07013 }
07014
07015 static char *complete_queue(const char *line, const char *word, int pos, int state)
07016 {
07017 struct call_queue *q;
07018 char *ret = NULL;
07019 int which = 0;
07020 int wordlen = strlen(word);
07021 struct ao2_iterator queue_iter;
07022
07023 queue_iter = ao2_iterator_init(queues, 0);
07024 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07025 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
07026 ret = ast_strdup(q->name);
07027 queue_t_unref(q, "Done with iterator");
07028 break;
07029 }
07030 queue_t_unref(q, "Done with iterator");
07031 }
07032 ao2_iterator_destroy(&queue_iter);
07033
07034 return ret;
07035 }
07036
07037 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
07038 {
07039 if (pos == 2)
07040 return complete_queue(line, word, pos, state);
07041 return NULL;
07042 }
07043
07044 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07045 {
07046 switch ( cmd ) {
07047 case CLI_INIT:
07048 e->command = "queue show";
07049 e->usage =
07050 "Usage: queue show\n"
07051 " Provides summary information on a specified queue.\n";
07052 return NULL;
07053 case CLI_GENERATE:
07054 return complete_queue_show(a->line, a->word, a->pos, a->n);
07055 }
07056
07057 return __queues_show(NULL, a->fd, a->argc, a->argv);
07058 }
07059
07060
07061
07062
07063 static int manager_queues_show(struct mansession *s, const struct message *m)
07064 {
07065 static const char * const a[] = { "queue", "show" };
07066
07067 __queues_show(s, -1, 2, a);
07068 astman_append(s, "\r\n\r\n");
07069
07070 return RESULT_SUCCESS;
07071 }
07072
07073 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
07074 {
07075 const char *rule = astman_get_header(m, "Rule");
07076 const char *id = astman_get_header(m, "ActionID");
07077 struct rule_list *rl_iter;
07078 struct penalty_rule *pr_iter;
07079
07080 astman_append(s, "Response: Success\r\n");
07081 if (!ast_strlen_zero(id)) {
07082 astman_append(s, "ActionID: %s\r\n", id);
07083 }
07084
07085 AST_LIST_LOCK(&rule_lists);
07086 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07087 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07088 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07089 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07090 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 );
07091 }
07092 if (!ast_strlen_zero(rule))
07093 break;
07094 }
07095 }
07096 AST_LIST_UNLOCK(&rule_lists);
07097
07098
07099
07100
07101
07102 astman_append(s, "\r\n\r\n");
07103
07104 return RESULT_SUCCESS;
07105 }
07106
07107
07108 static int manager_queues_summary(struct mansession *s, const struct message *m)
07109 {
07110 time_t now;
07111 int qmemcount = 0;
07112 int qmemavail = 0;
07113 int qchancount = 0;
07114 int qlongestholdtime = 0;
07115 const char *id = astman_get_header(m, "ActionID");
07116 const char *queuefilter = astman_get_header(m, "Queue");
07117 char idText[256] = "";
07118 struct call_queue *q;
07119 struct queue_ent *qe;
07120 struct member *mem;
07121 struct ao2_iterator queue_iter;
07122 struct ao2_iterator mem_iter;
07123
07124 astman_send_ack(s, m, "Queue summary will follow");
07125 time(&now);
07126 if (!ast_strlen_zero(id))
07127 snprintf(idText, 256, "ActionID: %s\r\n", id);
07128 queue_iter = ao2_iterator_init(queues, 0);
07129 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07130 ao2_lock(q);
07131
07132
07133 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07134
07135 qmemcount = 0;
07136 qmemavail = 0;
07137 qchancount = 0;
07138 qlongestholdtime = 0;
07139
07140
07141 mem_iter = ao2_iterator_init(q->members, 0);
07142 while ((mem = ao2_iterator_next(&mem_iter))) {
07143 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07144 ++qmemcount;
07145 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
07146 ++qmemavail;
07147 }
07148 }
07149 ao2_ref(mem, -1);
07150 }
07151 ao2_iterator_destroy(&mem_iter);
07152 for (qe = q->head; qe; qe = qe->next) {
07153 if ((now - qe->start) > qlongestholdtime) {
07154 qlongestholdtime = now - qe->start;
07155 }
07156 ++qchancount;
07157 }
07158 astman_append(s, "Event: QueueSummary\r\n"
07159 "Queue: %s\r\n"
07160 "LoggedIn: %d\r\n"
07161 "Available: %d\r\n"
07162 "Callers: %d\r\n"
07163 "HoldTime: %d\r\n"
07164 "TalkTime: %d\r\n"
07165 "LongestHoldTime: %d\r\n"
07166 "%s"
07167 "\r\n",
07168 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07169 }
07170 ao2_unlock(q);
07171 queue_t_unref(q, "Done with iterator");
07172 }
07173 ao2_iterator_destroy(&queue_iter);
07174 astman_append(s,
07175 "Event: QueueSummaryComplete\r\n"
07176 "%s"
07177 "\r\n", idText);
07178
07179 return RESULT_SUCCESS;
07180 }
07181
07182
07183 static int manager_queues_status(struct mansession *s, const struct message *m)
07184 {
07185 time_t now;
07186 int pos;
07187 const char *id = astman_get_header(m,"ActionID");
07188 const char *queuefilter = astman_get_header(m,"Queue");
07189 const char *memberfilter = astman_get_header(m,"Member");
07190 char idText[256] = "";
07191 struct call_queue *q;
07192 struct queue_ent *qe;
07193 float sl = 0;
07194 struct member *mem;
07195 struct ao2_iterator queue_iter;
07196 struct ao2_iterator mem_iter;
07197
07198 astman_send_ack(s, m, "Queue status will follow");
07199 time(&now);
07200 if (!ast_strlen_zero(id))
07201 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07202
07203 queue_iter = ao2_iterator_init(queues, 0);
07204 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07205 ao2_lock(q);
07206
07207
07208 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07209 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07210 astman_append(s, "Event: QueueParams\r\n"
07211 "Queue: %s\r\n"
07212 "Max: %d\r\n"
07213 "Strategy: %s\r\n"
07214 "Calls: %d\r\n"
07215 "Holdtime: %d\r\n"
07216 "TalkTime: %d\r\n"
07217 "Completed: %d\r\n"
07218 "Abandoned: %d\r\n"
07219 "ServiceLevel: %d\r\n"
07220 "ServicelevelPerf: %2.1f\r\n"
07221 "Weight: %d\r\n"
07222 "%s"
07223 "\r\n",
07224 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07225 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07226
07227 mem_iter = ao2_iterator_init(q->members, 0);
07228 while ((mem = ao2_iterator_next(&mem_iter))) {
07229 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07230 astman_append(s, "Event: QueueMember\r\n"
07231 "Queue: %s\r\n"
07232 "Name: %s\r\n"
07233 "Location: %s\r\n"
07234 "Membership: %s\r\n"
07235 "Penalty: %d\r\n"
07236 "CallsTaken: %d\r\n"
07237 "LastCall: %d\r\n"
07238 "Status: %d\r\n"
07239 "Paused: %d\r\n"
07240 "%s"
07241 "\r\n",
07242 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07243 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07244 }
07245 ao2_ref(mem, -1);
07246 }
07247 ao2_iterator_destroy(&mem_iter);
07248
07249 pos = 1;
07250 for (qe = q->head; qe; qe = qe->next) {
07251 astman_append(s, "Event: QueueEntry\r\n"
07252 "Queue: %s\r\n"
07253 "Position: %d\r\n"
07254 "Channel: %s\r\n"
07255 "Uniqueid: %s\r\n"
07256 "CallerIDNum: %s\r\n"
07257 "CallerIDName: %s\r\n"
07258 "ConnectedLineNum: %s\r\n"
07259 "ConnectedLineName: %s\r\n"
07260 "Wait: %ld\r\n"
07261 "%s"
07262 "\r\n",
07263 q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07264 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07265 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07266 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07267 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07268 (long) (now - qe->start), idText);
07269 }
07270 }
07271 ao2_unlock(q);
07272 queue_t_unref(q, "Done with iterator");
07273 }
07274 ao2_iterator_destroy(&queue_iter);
07275
07276 astman_append(s,
07277 "Event: QueueStatusComplete\r\n"
07278 "%s"
07279 "\r\n",idText);
07280
07281 return RESULT_SUCCESS;
07282 }
07283
07284 static int manager_add_queue_member(struct mansession *s, const struct message *m)
07285 {
07286 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07287 int paused, penalty = 0;
07288
07289 queuename = astman_get_header(m, "Queue");
07290 interface = astman_get_header(m, "Interface");
07291 penalty_s = astman_get_header(m, "Penalty");
07292 paused_s = astman_get_header(m, "Paused");
07293 membername = astman_get_header(m, "MemberName");
07294 state_interface = astman_get_header(m, "StateInterface");
07295
07296 if (ast_strlen_zero(queuename)) {
07297 astman_send_error(s, m, "'Queue' not specified.");
07298 return 0;
07299 }
07300
07301 if (ast_strlen_zero(interface)) {
07302 astman_send_error(s, m, "'Interface' not specified.");
07303 return 0;
07304 }
07305
07306 if (ast_strlen_zero(penalty_s))
07307 penalty = 0;
07308 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07309 penalty = 0;
07310
07311 if (ast_strlen_zero(paused_s))
07312 paused = 0;
07313 else
07314 paused = abs(ast_true(paused_s));
07315
07316 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07317 case RES_OKAY:
07318 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07319 astman_send_ack(s, m, "Added interface to queue");
07320 break;
07321 case RES_EXISTS:
07322 astman_send_error(s, m, "Unable to add interface: Already there");
07323 break;
07324 case RES_NOSUCHQUEUE:
07325 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07326 break;
07327 case RES_OUTOFMEMORY:
07328 astman_send_error(s, m, "Out of memory");
07329 break;
07330 }
07331
07332 return 0;
07333 }
07334
07335 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
07336 {
07337 const char *queuename, *interface;
07338
07339 queuename = astman_get_header(m, "Queue");
07340 interface = astman_get_header(m, "Interface");
07341
07342 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07343 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07344 return 0;
07345 }
07346
07347 switch (remove_from_queue(queuename, interface)) {
07348 case RES_OKAY:
07349 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07350 astman_send_ack(s, m, "Removed interface from queue");
07351 break;
07352 case RES_EXISTS:
07353 astman_send_error(s, m, "Unable to remove interface: Not there");
07354 break;
07355 case RES_NOSUCHQUEUE:
07356 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07357 break;
07358 case RES_OUTOFMEMORY:
07359 astman_send_error(s, m, "Out of memory");
07360 break;
07361 case RES_NOT_DYNAMIC:
07362 astman_send_error(s, m, "Member not dynamic");
07363 break;
07364 }
07365
07366 return 0;
07367 }
07368
07369 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
07370 {
07371 const char *queuename, *interface, *paused_s, *reason;
07372 int paused;
07373
07374 interface = astman_get_header(m, "Interface");
07375 paused_s = astman_get_header(m, "Paused");
07376 queuename = astman_get_header(m, "Queue");
07377 reason = astman_get_header(m, "Reason");
07378
07379 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07380 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07381 return 0;
07382 }
07383
07384 paused = abs(ast_true(paused_s));
07385
07386 if (set_member_paused(queuename, interface, reason, paused))
07387 astman_send_error(s, m, "Interface not found");
07388 else
07389 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07390 return 0;
07391 }
07392
07393 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
07394 {
07395 const char *queuename, *event, *message, *interface, *uniqueid;
07396
07397 queuename = astman_get_header(m, "Queue");
07398 uniqueid = astman_get_header(m, "UniqueId");
07399 interface = astman_get_header(m, "Interface");
07400 event = astman_get_header(m, "Event");
07401 message = astman_get_header(m, "Message");
07402
07403 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07404 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07405 return 0;
07406 }
07407
07408 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07409 astman_send_ack(s, m, "Event added successfully");
07410
07411 return 0;
07412 }
07413
07414 static int manager_queue_reload(struct mansession *s, const struct message *m)
07415 {
07416 struct ast_flags mask = {0,};
07417 const char *queuename = NULL;
07418 int header_found = 0;
07419
07420 queuename = astman_get_header(m, "Queue");
07421 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07422 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07423 header_found = 1;
07424 }
07425 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07426 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07427 header_found = 1;
07428 }
07429 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07430 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07431 header_found = 1;
07432 }
07433
07434 if (!header_found) {
07435 ast_set_flag(&mask, AST_FLAGS_ALL);
07436 }
07437
07438 if (!reload_handler(1, &mask, queuename)) {
07439 astman_send_ack(s, m, "Queue reloaded successfully");
07440 } else {
07441 astman_send_error(s, m, "Error encountered while reloading queue");
07442 }
07443 return 0;
07444 }
07445
07446 static int manager_queue_reset(struct mansession *s, const struct message *m)
07447 {
07448 const char *queuename = NULL;
07449 struct ast_flags mask = {QUEUE_RESET_STATS,};
07450
07451 queuename = astman_get_header(m, "Queue");
07452
07453 if (!reload_handler(1, &mask, queuename)) {
07454 astman_send_ack(s, m, "Queue stats reset successfully");
07455 } else {
07456 astman_send_error(s, m, "Error encountered while resetting queue stats");
07457 }
07458 return 0;
07459 }
07460
07461 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
07462 {
07463
07464 switch (pos) {
07465 case 3:
07466 return NULL;
07467 case 4:
07468 return state == 0 ? ast_strdup("to") : NULL;
07469 case 5:
07470 return complete_queue(line, word, pos, state);
07471 case 6:
07472 return state == 0 ? ast_strdup("penalty") : NULL;
07473 case 7:
07474 if (state < 100) {
07475 char *num;
07476 if ((num = ast_malloc(3))) {
07477 sprintf(num, "%d", state);
07478 }
07479 return num;
07480 } else {
07481 return NULL;
07482 }
07483 case 8:
07484 return state == 0 ? ast_strdup("as") : NULL;
07485 case 9:
07486 return NULL;
07487 default:
07488 return NULL;
07489 }
07490 }
07491
07492 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
07493 {
07494 const char *queuename, *interface, *penalty_s;
07495 int penalty;
07496
07497 interface = astman_get_header(m, "Interface");
07498 penalty_s = astman_get_header(m, "Penalty");
07499
07500 queuename = astman_get_header(m, "Queue");
07501
07502 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07503 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07504 return 0;
07505 }
07506
07507 penalty = atoi(penalty_s);
07508
07509 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07510 astman_send_error(s, m, "Invalid interface, queuename or penalty");
07511 else
07512 astman_send_ack(s, m, "Interface penalty set successfully");
07513
07514 return 0;
07515 }
07516
07517 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07518 {
07519 const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07520 int penalty;
07521
07522 switch ( cmd ) {
07523 case CLI_INIT:
07524 e->command = "queue add member";
07525 e->usage =
07526 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07527 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n";
07528 return NULL;
07529 case CLI_GENERATE:
07530 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07531 }
07532
07533 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07534 return CLI_SHOWUSAGE;
07535 } else if (strcmp(a->argv[4], "to")) {
07536 return CLI_SHOWUSAGE;
07537 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07538 return CLI_SHOWUSAGE;
07539 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07540 return CLI_SHOWUSAGE;
07541 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07542 return CLI_SHOWUSAGE;
07543 }
07544
07545 queuename = a->argv[5];
07546 interface = a->argv[3];
07547 if (a->argc >= 8) {
07548 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07549 if (penalty < 0) {
07550 ast_cli(a->fd, "Penalty must be >= 0\n");
07551 penalty = 0;
07552 }
07553 } else {
07554 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07555 penalty = 0;
07556 }
07557 } else {
07558 penalty = 0;
07559 }
07560
07561 if (a->argc >= 10) {
07562 membername = a->argv[9];
07563 }
07564
07565 if (a->argc >= 12) {
07566 state_interface = a->argv[11];
07567 }
07568
07569 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07570 case RES_OKAY:
07571 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07572 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07573 return CLI_SUCCESS;
07574 case RES_EXISTS:
07575 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07576 return CLI_FAILURE;
07577 case RES_NOSUCHQUEUE:
07578 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07579 return CLI_FAILURE;
07580 case RES_OUTOFMEMORY:
07581 ast_cli(a->fd, "Out of memory\n");
07582 return CLI_FAILURE;
07583 case RES_NOT_DYNAMIC:
07584 ast_cli(a->fd, "Member not dynamic\n");
07585 return CLI_FAILURE;
07586 default:
07587 return CLI_FAILURE;
07588 }
07589 }
07590
07591 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
07592 {
07593 int which = 0;
07594 struct call_queue *q;
07595 struct member *m;
07596 struct ao2_iterator queue_iter;
07597 struct ao2_iterator mem_iter;
07598 int wordlen = strlen(word);
07599
07600
07601 if (pos > 5 || pos < 3)
07602 return NULL;
07603 if (pos == 4)
07604 return (state == 0 ? ast_strdup("from") : NULL);
07605
07606 if (pos == 5)
07607 return complete_queue(line, word, pos, state);
07608
07609
07610 queue_iter = ao2_iterator_init(queues, 0);
07611 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07612 ao2_lock(q);
07613 mem_iter = ao2_iterator_init(q->members, 0);
07614 while ((m = ao2_iterator_next(&mem_iter))) {
07615 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07616 char *tmp;
07617 tmp = ast_strdup(m->interface);
07618 ao2_ref(m, -1);
07619 ao2_iterator_destroy(&mem_iter);
07620 ao2_unlock(q);
07621 queue_t_unref(q, "Done with iterator, returning interface name");
07622 ao2_iterator_destroy(&queue_iter);
07623 return tmp;
07624 }
07625 ao2_ref(m, -1);
07626 }
07627 ao2_iterator_destroy(&mem_iter);
07628 ao2_unlock(q);
07629 queue_t_unref(q, "Done with iterator");
07630 }
07631 ao2_iterator_destroy(&queue_iter);
07632
07633 return NULL;
07634 }
07635
07636 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07637 {
07638 const char *queuename, *interface;
07639
07640 switch (cmd) {
07641 case CLI_INIT:
07642 e->command = "queue remove member";
07643 e->usage =
07644 "Usage: queue remove member <channel> from <queue>\n"
07645 " Remove a specific channel from a queue.\n";
07646 return NULL;
07647 case CLI_GENERATE:
07648 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
07649 }
07650
07651 if (a->argc != 6) {
07652 return CLI_SHOWUSAGE;
07653 } else if (strcmp(a->argv[4], "from")) {
07654 return CLI_SHOWUSAGE;
07655 }
07656
07657 queuename = a->argv[5];
07658 interface = a->argv[3];
07659
07660 switch (remove_from_queue(queuename, interface)) {
07661 case RES_OKAY:
07662 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
07663 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
07664 return CLI_SUCCESS;
07665 case RES_EXISTS:
07666 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
07667 return CLI_FAILURE;
07668 case RES_NOSUCHQUEUE:
07669 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
07670 return CLI_FAILURE;
07671 case RES_OUTOFMEMORY:
07672 ast_cli(a->fd, "Out of memory\n");
07673 return CLI_FAILURE;
07674 case RES_NOT_DYNAMIC:
07675 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
07676 return CLI_FAILURE;
07677 default:
07678 return CLI_FAILURE;
07679 }
07680 }
07681
07682 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
07683 {
07684
07685 switch (pos) {
07686 case 3:
07687 return NULL;
07688 case 4:
07689 return state == 0 ? ast_strdup("queue") : NULL;
07690 case 5:
07691 return complete_queue(line, word, pos, state);
07692 case 6:
07693 return state == 0 ? ast_strdup("reason") : NULL;
07694 case 7:
07695 return NULL;
07696 default:
07697 return NULL;
07698 }
07699 }
07700
07701 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07702 {
07703 const char *queuename, *interface, *reason;
07704 int paused;
07705
07706 switch (cmd) {
07707 case CLI_INIT:
07708 e->command = "queue {pause|unpause} member";
07709 e->usage =
07710 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
07711 " Pause or unpause a queue member. Not specifying a particular queue\n"
07712 " will pause or unpause a member across all queues to which the member\n"
07713 " belongs.\n";
07714 return NULL;
07715 case CLI_GENERATE:
07716 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
07717 }
07718
07719 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
07720 return CLI_SHOWUSAGE;
07721 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
07722 return CLI_SHOWUSAGE;
07723 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
07724 return CLI_SHOWUSAGE;
07725 }
07726
07727
07728 interface = a->argv[3];
07729 queuename = a->argc >= 6 ? a->argv[5] : NULL;
07730 reason = a->argc == 8 ? a->argv[7] : NULL;
07731 paused = !strcasecmp(a->argv[1], "pause");
07732
07733 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
07734 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
07735 if (!ast_strlen_zero(queuename))
07736 ast_cli(a->fd, " in queue '%s'", queuename);
07737 if (!ast_strlen_zero(reason))
07738 ast_cli(a->fd, " for reason '%s'", reason);
07739 ast_cli(a->fd, "\n");
07740 return CLI_SUCCESS;
07741 } else {
07742 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
07743 if (!ast_strlen_zero(queuename))
07744 ast_cli(a->fd, " in queue '%s'", queuename);
07745 if (!ast_strlen_zero(reason))
07746 ast_cli(a->fd, " for reason '%s'", reason);
07747 ast_cli(a->fd, "\n");
07748 return CLI_FAILURE;
07749 }
07750 }
07751
07752 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
07753 {
07754
07755 switch (pos) {
07756 case 4:
07757 if (state == 0) {
07758 return ast_strdup("on");
07759 } else {
07760 return NULL;
07761 }
07762 case 6:
07763 if (state == 0) {
07764 return ast_strdup("in");
07765 } else {
07766 return NULL;
07767 }
07768 case 7:
07769 return complete_queue(line, word, pos, state);
07770 default:
07771 return NULL;
07772 }
07773 }
07774
07775 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07776 {
07777 const char *queuename = NULL, *interface;
07778 int penalty = 0;
07779
07780 switch (cmd) {
07781 case CLI_INIT:
07782 e->command = "queue set penalty";
07783 e->usage =
07784 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
07785 " Set a member's penalty in the queue specified. If no queue is specified\n"
07786 " then that interface's penalty is set in all queues to which that interface is a member\n";
07787 return NULL;
07788 case CLI_GENERATE:
07789 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
07790 }
07791
07792 if (a->argc != 6 && a->argc != 8) {
07793 return CLI_SHOWUSAGE;
07794 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
07795 return CLI_SHOWUSAGE;
07796 }
07797
07798 if (a->argc == 8)
07799 queuename = a->argv[7];
07800 interface = a->argv[5];
07801 penalty = atoi(a->argv[3]);
07802
07803 switch (set_member_penalty(queuename, interface, penalty)) {
07804 case RESULT_SUCCESS:
07805 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07806 return CLI_SUCCESS;
07807 case RESULT_FAILURE:
07808 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07809 return CLI_FAILURE;
07810 default:
07811 return CLI_FAILURE;
07812 }
07813 }
07814
07815 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
07816 {
07817 int which = 0;
07818 struct rule_list *rl_iter;
07819 int wordlen = strlen(word);
07820 char *ret = NULL;
07821 if (pos != 3) {
07822 return NULL;
07823 }
07824
07825 AST_LIST_LOCK(&rule_lists);
07826 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07827 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07828 ret = ast_strdup(rl_iter->name);
07829 break;
07830 }
07831 }
07832 AST_LIST_UNLOCK(&rule_lists);
07833
07834 return ret;
07835 }
07836
07837 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07838 {
07839 const char *rule;
07840 struct rule_list *rl_iter;
07841 struct penalty_rule *pr_iter;
07842 switch (cmd) {
07843 case CLI_INIT:
07844 e->command = "queue show rules";
07845 e->usage =
07846 "Usage: queue show rules [rulename]\n"
07847 " Show the list of rules associated with rulename. If no\n"
07848 " rulename is specified, list all rules defined in queuerules.conf\n";
07849 return NULL;
07850 case CLI_GENERATE:
07851 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07852 }
07853
07854 if (a->argc != 3 && a->argc != 4)
07855 return CLI_SHOWUSAGE;
07856
07857 rule = a->argc == 4 ? a->argv[3] : "";
07858 AST_LIST_LOCK(&rule_lists);
07859 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07860 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07861 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07862 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07863 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);
07864 }
07865 }
07866 }
07867 AST_LIST_UNLOCK(&rule_lists);
07868 return CLI_SUCCESS;
07869 }
07870
07871 static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07872 {
07873 struct ast_flags mask = {QUEUE_RESET_STATS,};
07874 int i;
07875
07876 switch (cmd) {
07877 case CLI_INIT:
07878 e->command = "queue reset stats";
07879 e->usage =
07880 "Usage: queue reset stats [<queuenames>]\n"
07881 "\n"
07882 "Issuing this command will reset statistics for\n"
07883 "<queuenames>, or for all queues if no queue is\n"
07884 "specified.\n";
07885 return NULL;
07886 case CLI_GENERATE:
07887 if (a->pos >= 3) {
07888 return complete_queue(a->line, a->word, a->pos, a->n);
07889 } else {
07890 return NULL;
07891 }
07892 }
07893
07894 if (a->argc < 3) {
07895 return CLI_SHOWUSAGE;
07896 }
07897
07898 if (a->argc == 3) {
07899 reload_handler(1, &mask, NULL);
07900 return CLI_SUCCESS;
07901 }
07902
07903 for (i = 3; i < a->argc; ++i) {
07904 reload_handler(1, &mask, a->argv[i]);
07905 }
07906
07907 return CLI_SUCCESS;
07908 }
07909
07910 static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
07911 {
07912 struct ast_flags mask = {0,};
07913 int i;
07914
07915 switch (cmd) {
07916 case CLI_INIT:
07917 e->command = "queue reload {parameters|members|rules|all}";
07918 e->usage =
07919 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
07920 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
07921 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
07922 "specified in order to know what information to reload. Below is an explanation\n"
07923 "of each of these qualifiers.\n"
07924 "\n"
07925 "\t'members' - reload queue members from queues.conf\n"
07926 "\t'parameters' - reload all queue options except for queue members\n"
07927 "\t'rules' - reload the queuerules.conf file\n"
07928 "\t'all' - reload queue rules, parameters, and members\n"
07929 "\n"
07930 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
07931 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
07932 "one queue is specified when using this command, reloading queue rules may cause\n"
07933 "other queues to be affected\n";
07934 return NULL;
07935 case CLI_GENERATE:
07936 if (a->pos >= 3) {
07937 return complete_queue(a->line, a->word, a->pos, a->n);
07938 } else {
07939 return NULL;
07940 }
07941 }
07942
07943 if (a->argc < 3)
07944 return CLI_SHOWUSAGE;
07945
07946 if (!strcasecmp(a->argv[2], "rules")) {
07947 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07948 } else if (!strcasecmp(a->argv[2], "members")) {
07949 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07950 } else if (!strcasecmp(a->argv[2], "parameters")) {
07951 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07952 } else if (!strcasecmp(a->argv[2], "all")) {
07953 ast_set_flag(&mask, AST_FLAGS_ALL);
07954 }
07955
07956 if (a->argc == 3) {
07957 reload_handler(1, &mask, NULL);
07958 return CLI_SUCCESS;
07959 }
07960
07961 for (i = 3; i < a->argc; ++i) {
07962 reload_handler(1, &mask, a->argv[i]);
07963 }
07964
07965 return CLI_SUCCESS;
07966 }
07967
07968 static const char qpm_cmd_usage[] =
07969 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
07970
07971 static const char qum_cmd_usage[] =
07972 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
07973
07974 static const char qsmp_cmd_usage[] =
07975 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
07976
07977 static struct ast_cli_entry cli_queue[] = {
07978 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
07979 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
07980 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
07981 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
07982 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
07983 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
07984 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
07985 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
07986 };
07987
07988
07989 #define DATA_EXPORT_CALL_QUEUE(MEMBER) \
07990 MEMBER(call_queue, name, AST_DATA_STRING) \
07991 MEMBER(call_queue, moh, AST_DATA_STRING) \
07992 MEMBER(call_queue, announce, AST_DATA_STRING) \
07993 MEMBER(call_queue, context, AST_DATA_STRING) \
07994 MEMBER(call_queue, membermacro, AST_DATA_STRING) \
07995 MEMBER(call_queue, membergosub, AST_DATA_STRING) \
07996 MEMBER(call_queue, defaultrule, AST_DATA_STRING) \
07997 MEMBER(call_queue, sound_next, AST_DATA_STRING) \
07998 MEMBER(call_queue, sound_thereare, AST_DATA_STRING) \
07999 MEMBER(call_queue, sound_calls, AST_DATA_STRING) \
08000 MEMBER(call_queue, queue_quantity1, AST_DATA_STRING) \
08001 MEMBER(call_queue, queue_quantity2, AST_DATA_STRING) \
08002 MEMBER(call_queue, sound_holdtime, AST_DATA_STRING) \
08003 MEMBER(call_queue, sound_minutes, AST_DATA_STRING) \
08004 MEMBER(call_queue, sound_minute, AST_DATA_STRING) \
08005 MEMBER(call_queue, sound_seconds, AST_DATA_STRING) \
08006 MEMBER(call_queue, sound_thanks, AST_DATA_STRING) \
08007 MEMBER(call_queue, sound_callerannounce, AST_DATA_STRING) \
08008 MEMBER(call_queue, sound_reporthold, AST_DATA_STRING) \
08009 MEMBER(call_queue, dead, AST_DATA_BOOLEAN) \
08010 MEMBER(call_queue, eventwhencalled, AST_DATA_BOOLEAN) \
08011 MEMBER(call_queue, ringinuse, AST_DATA_BOOLEAN) \
08012 MEMBER(call_queue, setinterfacevar, AST_DATA_BOOLEAN) \
08013 MEMBER(call_queue, setqueuevar, AST_DATA_BOOLEAN) \
08014 MEMBER(call_queue, setqueueentryvar, AST_DATA_BOOLEAN) \
08015 MEMBER(call_queue, reportholdtime, AST_DATA_BOOLEAN) \
08016 MEMBER(call_queue, wrapped, AST_DATA_BOOLEAN) \
08017 MEMBER(call_queue, timeoutrestart, AST_DATA_BOOLEAN) \
08018 MEMBER(call_queue, announceholdtime, AST_DATA_INTEGER) \
08019 MEMBER(call_queue, maskmemberstatus, AST_DATA_BOOLEAN) \
08020 MEMBER(call_queue, realtime, AST_DATA_BOOLEAN) \
08021 MEMBER(call_queue, found, AST_DATA_BOOLEAN) \
08022 MEMBER(call_queue, announcepositionlimit, AST_DATA_INTEGER) \
08023 MEMBER(call_queue, announcefrequency, AST_DATA_SECONDS) \
08024 MEMBER(call_queue, minannouncefrequency, AST_DATA_SECONDS) \
08025 MEMBER(call_queue, periodicannouncefrequency, AST_DATA_SECONDS) \
08026 MEMBER(call_queue, numperiodicannounce, AST_DATA_INTEGER) \
08027 MEMBER(call_queue, randomperiodicannounce, AST_DATA_INTEGER) \
08028 MEMBER(call_queue, roundingseconds, AST_DATA_SECONDS) \
08029 MEMBER(call_queue, holdtime, AST_DATA_SECONDS) \
08030 MEMBER(call_queue, talktime, AST_DATA_SECONDS) \
08031 MEMBER(call_queue, callscompleted, AST_DATA_INTEGER) \
08032 MEMBER(call_queue, callsabandoned, AST_DATA_INTEGER) \
08033 MEMBER(call_queue, servicelevel, AST_DATA_INTEGER) \
08034 MEMBER(call_queue, callscompletedinsl, AST_DATA_INTEGER) \
08035 MEMBER(call_queue, monfmt, AST_DATA_STRING) \
08036 MEMBER(call_queue, montype, AST_DATA_INTEGER) \
08037 MEMBER(call_queue, count, AST_DATA_INTEGER) \
08038 MEMBER(call_queue, maxlen, AST_DATA_INTEGER) \
08039 MEMBER(call_queue, wrapuptime, AST_DATA_SECONDS) \
08040 MEMBER(call_queue, retry, AST_DATA_SECONDS) \
08041 MEMBER(call_queue, timeout, AST_DATA_SECONDS) \
08042 MEMBER(call_queue, weight, AST_DATA_INTEGER) \
08043 MEMBER(call_queue, autopause, AST_DATA_INTEGER) \
08044 MEMBER(call_queue, timeoutpriority, AST_DATA_INTEGER) \
08045 MEMBER(call_queue, rrpos, AST_DATA_INTEGER) \
08046 MEMBER(call_queue, memberdelay, AST_DATA_INTEGER) \
08047 MEMBER(call_queue, autofill, AST_DATA_INTEGER) \
08048 MEMBER(call_queue, members, AST_DATA_CONTAINER)
08049
08050 AST_DATA_STRUCTURE(call_queue, DATA_EXPORT_CALL_QUEUE);
08051
08052
08053 #define DATA_EXPORT_MEMBER(MEMBER) \
08054 MEMBER(member, interface, AST_DATA_STRING) \
08055 MEMBER(member, state_interface, AST_DATA_STRING) \
08056 MEMBER(member, membername, AST_DATA_STRING) \
08057 MEMBER(member, penalty, AST_DATA_INTEGER) \
08058 MEMBER(member, calls, AST_DATA_INTEGER) \
08059 MEMBER(member, dynamic, AST_DATA_INTEGER) \
08060 MEMBER(member, realtime, AST_DATA_INTEGER) \
08061 MEMBER(member, status, AST_DATA_INTEGER) \
08062 MEMBER(member, paused, AST_DATA_BOOLEAN) \
08063 MEMBER(member, rt_uniqueid, AST_DATA_STRING)
08064
08065 AST_DATA_STRUCTURE(member, DATA_EXPORT_MEMBER);
08066
08067 #define DATA_EXPORT_QUEUE_ENT(MEMBER) \
08068 MEMBER(queue_ent, moh, AST_DATA_STRING) \
08069 MEMBER(queue_ent, announce, AST_DATA_STRING) \
08070 MEMBER(queue_ent, context, AST_DATA_STRING) \
08071 MEMBER(queue_ent, digits, AST_DATA_STRING) \
08072 MEMBER(queue_ent, valid_digits, AST_DATA_INTEGER) \
08073 MEMBER(queue_ent, pos, AST_DATA_INTEGER) \
08074 MEMBER(queue_ent, prio, AST_DATA_INTEGER) \
08075 MEMBER(queue_ent, last_pos_said, AST_DATA_INTEGER) \
08076 MEMBER(queue_ent, last_periodic_announce_time, AST_DATA_INTEGER) \
08077 MEMBER(queue_ent, last_periodic_announce_sound, AST_DATA_INTEGER) \
08078 MEMBER(queue_ent, last_pos, AST_DATA_INTEGER) \
08079 MEMBER(queue_ent, opos, AST_DATA_INTEGER) \
08080 MEMBER(queue_ent, handled, AST_DATA_INTEGER) \
08081 MEMBER(queue_ent, pending, AST_DATA_INTEGER) \
08082 MEMBER(queue_ent, max_penalty, AST_DATA_INTEGER) \
08083 MEMBER(queue_ent, min_penalty, AST_DATA_INTEGER) \
08084 MEMBER(queue_ent, linpos, AST_DATA_INTEGER) \
08085 MEMBER(queue_ent, linwrapped, AST_DATA_INTEGER) \
08086 MEMBER(queue_ent, start, AST_DATA_INTEGER) \
08087 MEMBER(queue_ent, expire, AST_DATA_INTEGER) \
08088 MEMBER(queue_ent, cancel_answered_elsewhere, AST_DATA_INTEGER)
08089
08090 AST_DATA_STRUCTURE(queue_ent, DATA_EXPORT_QUEUE_ENT);
08091
08092
08093
08094
08095
08096
08097
08098
08099 static void queues_data_provider_get_helper(const struct ast_data_search *search,
08100 struct ast_data *data_root, struct call_queue *queue)
08101 {
08102 struct ao2_iterator im;
08103 struct member *member;
08104 struct queue_ent *qe;
08105 struct ast_data *data_queue, *data_members = NULL, *enum_node;
08106 struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08107
08108 data_queue = ast_data_add_node(data_root, "queue");
08109 if (!data_queue) {
08110 return;
08111 }
08112
08113 ast_data_add_structure(call_queue, data_queue, queue);
08114
08115 ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08116 ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08117
08118
08119 enum_node = ast_data_add_node(data_queue, "announceposition");
08120 if (!enum_node) {
08121 return;
08122 }
08123 switch (queue->announceposition) {
08124 case ANNOUNCEPOSITION_LIMIT:
08125 ast_data_add_str(enum_node, "text", "limit");
08126 break;
08127 case ANNOUNCEPOSITION_MORE_THAN:
08128 ast_data_add_str(enum_node, "text", "more");
08129 break;
08130 case ANNOUNCEPOSITION_YES:
08131 ast_data_add_str(enum_node, "text", "yes");
08132 break;
08133 case ANNOUNCEPOSITION_NO:
08134 ast_data_add_str(enum_node, "text", "no");
08135 break;
08136 default:
08137 ast_data_add_str(enum_node, "text", "unknown");
08138 break;
08139 }
08140 ast_data_add_int(enum_node, "value", queue->announceposition);
08141
08142
08143 im = ao2_iterator_init(queue->members, 0);
08144 while ((member = ao2_iterator_next(&im))) {
08145 if (!data_members) {
08146 data_members = ast_data_add_node(data_queue, "members");
08147 if (!data_members) {
08148 ao2_ref(member, -1);
08149 continue;
08150 }
08151 }
08152
08153 data_member = ast_data_add_node(data_members, "member");
08154 if (!data_member) {
08155 ao2_ref(member, -1);
08156 continue;
08157 }
08158
08159 ast_data_add_structure(member, data_member, member);
08160
08161 ao2_ref(member, -1);
08162 }
08163 ao2_iterator_destroy(&im);
08164
08165
08166 if (queue->head) {
08167 for (qe = queue->head; qe; qe = qe->next) {
08168 if (!data_callers) {
08169 data_callers = ast_data_add_node(data_queue, "callers");
08170 if (!data_callers) {
08171 continue;
08172 }
08173 }
08174
08175 data_caller = ast_data_add_node(data_callers, "caller");
08176 if (!data_caller) {
08177 continue;
08178 }
08179
08180 ast_data_add_structure(queue_ent, data_caller, qe);
08181
08182
08183 data_caller_channel = ast_data_add_node(data_caller, "channel");
08184 if (!data_caller_channel) {
08185 continue;
08186 }
08187
08188 ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08189 }
08190 }
08191
08192
08193 if (!ast_data_search_match(search, data_queue)) {
08194 ast_data_remove_node(data_root, data_queue);
08195 }
08196 }
08197
08198
08199
08200
08201
08202
08203
08204
08205 static int queues_data_provider_get(const struct ast_data_search *search,
08206 struct ast_data *data_root)
08207 {
08208 struct ao2_iterator i;
08209 struct call_queue *queue, *queue_realtime = NULL;
08210 struct ast_config *cfg;
08211 char *queuename;
08212
08213
08214 cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08215 if (cfg) {
08216 for (queuename = ast_category_browse(cfg, NULL);
08217 !ast_strlen_zero(queuename);
08218 queuename = ast_category_browse(cfg, queuename)) {
08219 if ((queue = load_realtime_queue(queuename))) {
08220 queue_unref(queue);
08221 }
08222 }
08223 ast_config_destroy(cfg);
08224 }
08225
08226
08227 i = ao2_iterator_init(queues, 0);
08228 while ((queue = ao2_iterator_next(&i))) {
08229 ao2_lock(queue);
08230 if (queue->realtime) {
08231 queue_realtime = load_realtime_queue(queue->name);
08232 if (!queue_realtime) {
08233 ao2_unlock(queue);
08234 queue_unref(queue);
08235 continue;
08236 }
08237 queue_unref(queue_realtime);
08238 }
08239
08240 queues_data_provider_get_helper(search, data_root, queue);
08241 ao2_unlock(queue);
08242 queue_unref(queue);
08243 }
08244 ao2_iterator_destroy(&i);
08245
08246 return 0;
08247 }
08248
08249 static const struct ast_data_handler queues_data_provider = {
08250 .version = AST_DATA_HANDLER_VERSION,
08251 .get = queues_data_provider_get
08252 };
08253
08254 static const struct ast_data_entry queue_data_providers[] = {
08255 AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider),
08256 };
08257
08258 static int unload_module(void)
08259 {
08260 int res;
08261 struct ast_context *con;
08262 struct ao2_iterator q_iter;
08263 struct call_queue *q = NULL;
08264
08265 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08266 res = ast_manager_unregister("QueueStatus");
08267 res |= ast_manager_unregister("Queues");
08268 res |= ast_manager_unregister("QueueRule");
08269 res |= ast_manager_unregister("QueueSummary");
08270 res |= ast_manager_unregister("QueueAdd");
08271 res |= ast_manager_unregister("QueueRemove");
08272 res |= ast_manager_unregister("QueuePause");
08273 res |= ast_manager_unregister("QueueLog");
08274 res |= ast_manager_unregister("QueuePenalty");
08275 res |= ast_unregister_application(app_aqm);
08276 res |= ast_unregister_application(app_rqm);
08277 res |= ast_unregister_application(app_pqm);
08278 res |= ast_unregister_application(app_upqm);
08279 res |= ast_unregister_application(app_ql);
08280 res |= ast_unregister_application(app);
08281 res |= ast_custom_function_unregister(&queueexists_function);
08282 res |= ast_custom_function_unregister(&queuevar_function);
08283 res |= ast_custom_function_unregister(&queuemembercount_function);
08284 res |= ast_custom_function_unregister(&queuemembercount_dep);
08285 res |= ast_custom_function_unregister(&queuememberlist_function);
08286 res |= ast_custom_function_unregister(&queuewaitingcount_function);
08287 res |= ast_custom_function_unregister(&queuememberpenalty_function);
08288
08289 res |= ast_data_unregister(NULL);
08290
08291 if (device_state_sub)
08292 ast_event_unsubscribe(device_state_sub);
08293
08294 ast_extension_state_del(0, extension_state_cb);
08295
08296 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08297 ast_context_remove_extension2(con, "s", 1, NULL, 0);
08298 ast_context_destroy(con, "app_queue");
08299 }
08300
08301 q_iter = ao2_iterator_init(queues, 0);
08302 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08303 queues_t_unlink(queues, q, "Remove queue from container due to unload");
08304 queue_t_unref(q, "Done with iterator");
08305 }
08306 ao2_iterator_destroy(&q_iter);
08307 ao2_ref(queues, -1);
08308 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08309 ast_unload_realtime("queue_members");
08310 return res;
08311 }
08312
08313 static int load_module(void)
08314 {
08315 int res;
08316 struct ast_context *con;
08317 struct ast_flags mask = {AST_FLAGS_ALL, };
08318
08319 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08320
08321 use_weight = 0;
08322
08323 if (reload_handler(0, &mask, NULL))
08324 return AST_MODULE_LOAD_DECLINE;
08325
08326 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08327 if (!con)
08328 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08329 else
08330 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08331
08332 if (queue_persistent_members)
08333 reload_queue_members();
08334
08335 ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08336
08337 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08338 res = ast_register_application_xml(app, queue_exec);
08339 res |= ast_register_application_xml(app_aqm, aqm_exec);
08340 res |= ast_register_application_xml(app_rqm, rqm_exec);
08341 res |= ast_register_application_xml(app_pqm, pqm_exec);
08342 res |= ast_register_application_xml(app_upqm, upqm_exec);
08343 res |= ast_register_application_xml(app_ql, ql_exec);
08344 res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08345 res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08346 res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08347 res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08348 res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08349 res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08350 res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08351 res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08352 res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08353 res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08354 res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08355 res |= ast_custom_function_register(&queuevar_function);
08356 res |= ast_custom_function_register(&queueexists_function);
08357 res |= ast_custom_function_register(&queuemembercount_function);
08358 res |= ast_custom_function_register(&queuemembercount_dep);
08359 res |= ast_custom_function_register(&queuememberlist_function);
08360 res |= ast_custom_function_register(&queuewaitingcount_function);
08361 res |= ast_custom_function_register(&queuememberpenalty_function);
08362
08363 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08364 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08365 }
08366
08367
08368 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08369 res = -1;
08370 }
08371
08372 ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08373
08374 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08375
08376 return res ? AST_MODULE_LOAD_DECLINE : 0;
08377 }
08378
08379 static int reload(void)
08380 {
08381 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08382 ast_unload_realtime("queue_members");
08383 reload_handler(1, &mask, NULL);
08384 return 0;
08385 }
08386
08387 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "True Call Queueing",
08388 .load = load_module,
08389 .unload = unload_module,
08390 .reload = reload,
08391 .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
08392 .nonoptreq = "res_monitor",
08393 );
08394