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 #include "asterisk.h"
00040
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414402 $")
00042
00043 #include <dahdi/user.h>
00044
00045 #include "asterisk/lock.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/app.h"
00052 #include "asterisk/dsp.h"
00053 #include "asterisk/musiconhold.h"
00054 #include "asterisk/manager.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/say.h"
00057 #include "asterisk/utils.h"
00058 #include "asterisk/translate.h"
00059 #include "asterisk/ulaw.h"
00060 #include "asterisk/astobj2.h"
00061 #include "asterisk/devicestate.h"
00062 #include "asterisk/dial.h"
00063 #include "asterisk/causes.h"
00064 #include "asterisk/paths.h"
00065 #include "asterisk/data.h"
00066 #include "asterisk/test.h"
00067
00068 #include "enter.h"
00069 #include "leave.h"
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
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 #define CONFIG_FILE_NAME "meetme.conf"
00550 #define SLA_CONFIG_FILE "sla.conf"
00551 #define STR_CONCISE "concise"
00552
00553
00554 #define DEFAULT_AUDIO_BUFFERS 32
00555
00556
00557 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00558
00559 enum {
00560 ADMINFLAG_MUTED = (1 << 1),
00561 ADMINFLAG_SELFMUTED = (1 << 2),
00562 ADMINFLAG_KICKME = (1 << 3),
00563
00564 ADMINFLAG_T_REQUEST = (1 << 4),
00565 ADMINFLAG_HANGUP = (1 << 5),
00566 };
00567
00568 #define MEETME_DELAYDETECTTALK 300
00569 #define MEETME_DELAYDETECTENDTALK 1000
00570
00571 #define AST_FRAME_BITS 32
00572
00573 enum volume_action {
00574 VOL_UP,
00575 VOL_DOWN
00576 };
00577
00578 enum entrance_sound {
00579 ENTER,
00580 LEAVE
00581 };
00582
00583 enum recording_state {
00584 MEETME_RECORD_OFF,
00585 MEETME_RECORD_STARTED,
00586 MEETME_RECORD_ACTIVE,
00587 MEETME_RECORD_TERMINATE
00588 };
00589
00590 #define CONF_SIZE 320
00591
00592 enum {
00593
00594 CONFFLAG_ADMIN = (1 << 0),
00595
00596 CONFFLAG_MONITOR = (1 << 1),
00597
00598 CONFFLAG_KEYEXIT = (1 << 2),
00599
00600 CONFFLAG_STARMENU = (1 << 3),
00601
00602 CONFFLAG_TALKER = (1 << 4),
00603
00604 CONFFLAG_QUIET = (1 << 5),
00605
00606
00607 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00608
00609 CONFFLAG_AGI = (1 << 7),
00610
00611 CONFFLAG_MOH = (1 << 8),
00612
00613 CONFFLAG_MARKEDEXIT = (1 << 9),
00614
00615 CONFFLAG_WAITMARKED = (1 << 10),
00616
00617 CONFFLAG_EXIT_CONTEXT = (1 << 11),
00618
00619 CONFFLAG_MARKEDUSER = (1 << 12),
00620
00621 CONFFLAG_INTROUSER = (1 << 13),
00622
00623 CONFFLAG_RECORDCONF = (1<< 14),
00624
00625 CONFFLAG_MONITORTALKER = (1 << 15),
00626 CONFFLAG_DYNAMIC = (1 << 16),
00627 CONFFLAG_DYNAMICPIN = (1 << 17),
00628 CONFFLAG_EMPTY = (1 << 18),
00629 CONFFLAG_EMPTYNOPIN = (1 << 19),
00630 CONFFLAG_ALWAYSPROMPT = (1 << 20),
00631
00632 CONFFLAG_OPTIMIZETALKER = (1 << 21),
00633
00634
00635 CONFFLAG_NOONLYPERSON = (1 << 22),
00636
00637
00638 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00639
00640 CONFFLAG_STARTMUTED = (1 << 24),
00641
00642 CONFFLAG_PASS_DTMF = (1 << 25),
00643 CONFFLAG_SLA_STATION = (1 << 26),
00644 CONFFLAG_SLA_TRUNK = (1 << 27),
00645
00646 CONFFLAG_KICK_CONTINUE = (1 << 28),
00647 CONFFLAG_DURATION_STOP = (1 << 29),
00648 CONFFLAG_DURATION_LIMIT = (1 << 30),
00649 };
00650
00651
00652
00653
00654 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
00655 #define CONFFLAG_INTROMSG (1ULL << 32)
00656 #define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
00657
00658 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
00659
00660 #define CONFFLAG_DONT_DENOISE (1ULL << 35)
00661
00662 enum {
00663 OPT_ARG_WAITMARKED = 0,
00664 OPT_ARG_EXITKEYS = 1,
00665 OPT_ARG_DURATION_STOP = 2,
00666 OPT_ARG_DURATION_LIMIT = 3,
00667 OPT_ARG_MOH_CLASS = 4,
00668 OPT_ARG_INTROMSG = 5,
00669 OPT_ARG_INTROUSER_VMREC = 6,
00670 OPT_ARG_ARRAY_SIZE = 7,
00671 };
00672
00673 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00674 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00675 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00676 AST_APP_OPTION('b', CONFFLAG_AGI ),
00677 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00678 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00679 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00680 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00681 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00682 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00683 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00684 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00685 AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
00686 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00687 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00688 AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
00689 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00690 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00691 AST_APP_OPTION('n', CONFFLAG_DONT_DENOISE ),
00692 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00693 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00694 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00695 AST_APP_OPTION('q', CONFFLAG_QUIET ),
00696 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00697 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00698 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00699 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00700 AST_APP_OPTION('t', CONFFLAG_TALKER ),
00701 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00702 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00703 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00704 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00705 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00706 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00707 END_OPTIONS );
00708
00709 static const char * const app = "MeetMe";
00710 static const char * const app2 = "MeetMeCount";
00711 static const char * const app3 = "MeetMeAdmin";
00712 static const char * const app4 = "MeetMeChannelAdmin";
00713 static const char * const slastation_app = "SLAStation";
00714 static const char * const slatrunk_app = "SLATrunk";
00715
00716
00717 static int rt_schedule;
00718 static int fuzzystart;
00719 static int earlyalert;
00720 static int endalert;
00721 static int extendby;
00722
00723
00724 static int rt_log_members;
00725
00726 #define MAX_CONFNUM 80
00727 #define MAX_PIN 80
00728 #define OPTIONS_LEN 100
00729
00730
00731 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00732
00733 enum announcetypes {
00734 CONF_HASJOIN,
00735 CONF_HASLEFT
00736 };
00737
00738 struct announce_listitem {
00739 AST_LIST_ENTRY(announce_listitem) entry;
00740 char namerecloc[PATH_MAX];
00741 char language[MAX_LANGUAGE];
00742 struct ast_channel *confchan;
00743 int confusers;
00744 int vmrec;
00745 enum announcetypes announcetype;
00746 };
00747
00748
00749 struct ast_conference {
00750 ast_mutex_t playlock;
00751 ast_mutex_t listenlock;
00752 char confno[MAX_CONFNUM];
00753 struct ast_channel *chan;
00754 struct ast_channel *lchan;
00755 int fd;
00756 int dahdiconf;
00757 int users;
00758 int markedusers;
00759 int maxusers;
00760 int endalert;
00761 time_t start;
00762 int refcount;
00763 enum recording_state recording:2;
00764 unsigned int isdynamic:1;
00765 unsigned int locked:1;
00766 unsigned int gmuted:1;
00767 pthread_t recordthread;
00768 ast_mutex_t recordthreadlock;
00769 pthread_attr_t attr;
00770 char *recordingfilename;
00771 char *recordingformat;
00772 char pin[MAX_PIN];
00773 char pinadmin[MAX_PIN];
00774 char uniqueid[32];
00775 long endtime;
00776 const char *useropts;
00777 const char *adminopts;
00778 const char *bookid;
00779 struct ast_frame *transframe[32];
00780 struct ast_frame *origframe;
00781 struct ast_trans_pvt *transpath[32];
00782 struct ao2_container *usercontainer;
00783 AST_LIST_ENTRY(ast_conference) list;
00784
00785 pthread_t announcethread;
00786 ast_mutex_t announcethreadlock;
00787 unsigned int announcethread_stop:1;
00788 ast_cond_t announcelist_addition;
00789 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00790 ast_mutex_t announcelistlock;
00791 };
00792
00793 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00794
00795 static unsigned int conf_map[1024] = {0, };
00796
00797 struct volume {
00798 int desired;
00799 int actual;
00800 };
00801
00802
00803 struct ast_conf_user {
00804 int user_no;
00805 struct ast_flags64 userflags;
00806 int adminflags;
00807 struct ast_channel *chan;
00808 int talking;
00809 int dahdichannel;
00810 char usrvalue[50];
00811 char namerecloc[PATH_MAX];
00812 time_t jointime;
00813 time_t kicktime;
00814 struct timeval start_time;
00815 long timelimit;
00816 long play_warning;
00817 long warning_freq;
00818 const char *warning_sound;
00819 const char *end_sound;
00820 struct volume talk;
00821 struct volume listen;
00822 AST_LIST_ENTRY(ast_conf_user) list;
00823 };
00824
00825 enum sla_which_trunk_refs {
00826 ALL_TRUNK_REFS,
00827 INACTIVE_TRUNK_REFS,
00828 };
00829
00830 enum sla_trunk_state {
00831 SLA_TRUNK_STATE_IDLE,
00832 SLA_TRUNK_STATE_RINGING,
00833 SLA_TRUNK_STATE_UP,
00834 SLA_TRUNK_STATE_ONHOLD,
00835 SLA_TRUNK_STATE_ONHOLD_BYME,
00836 };
00837
00838 enum sla_hold_access {
00839
00840
00841 SLA_HOLD_OPEN,
00842
00843
00844 SLA_HOLD_PRIVATE,
00845 };
00846
00847 struct sla_trunk_ref;
00848
00849 struct sla_station {
00850 AST_RWLIST_ENTRY(sla_station) entry;
00851 AST_DECLARE_STRING_FIELDS(
00852 AST_STRING_FIELD(name);
00853 AST_STRING_FIELD(device);
00854 AST_STRING_FIELD(autocontext);
00855 );
00856 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00857 struct ast_dial *dial;
00858
00859
00860
00861 unsigned int ring_timeout;
00862
00863
00864
00865 unsigned int ring_delay;
00866
00867
00868 unsigned int hold_access:1;
00869
00870 unsigned int mark:1;
00871 };
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881 struct sla_station_ref {
00882 AST_LIST_ENTRY(sla_station_ref) entry;
00883 struct sla_station *station;
00884
00885 unsigned int mark:1;
00886 };
00887
00888 struct sla_trunk {
00889 AST_DECLARE_STRING_FIELDS(
00890 AST_STRING_FIELD(name);
00891 AST_STRING_FIELD(device);
00892 AST_STRING_FIELD(autocontext);
00893 );
00894 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00895
00896 unsigned int num_stations;
00897
00898 unsigned int active_stations;
00899
00900 unsigned int hold_stations;
00901 struct ast_channel *chan;
00902 unsigned int ring_timeout;
00903
00904
00905 unsigned int barge_disabled:1;
00906
00907
00908 unsigned int hold_access:1;
00909
00910
00911 unsigned int on_hold:1;
00912
00913 unsigned int mark:1;
00914 };
00915
00916
00917
00918
00919
00920
00921
00922 struct sla_trunk_ref {
00923 AST_LIST_ENTRY(sla_trunk_ref) entry;
00924 struct sla_trunk *trunk;
00925 enum sla_trunk_state state;
00926 struct ast_channel *chan;
00927
00928
00929
00930 unsigned int ring_timeout;
00931
00932
00933
00934 unsigned int ring_delay;
00935
00936 unsigned int mark:1;
00937 };
00938
00939 static struct ao2_container *sla_stations;
00940 static struct ao2_container *sla_trunks;
00941
00942 static const char sla_registrar[] = "SLA";
00943
00944
00945 enum sla_event_type {
00946
00947 SLA_EVENT_HOLD,
00948
00949 SLA_EVENT_DIAL_STATE,
00950
00951 SLA_EVENT_RINGING_TRUNK,
00952 };
00953
00954 struct sla_event {
00955 enum sla_event_type type;
00956 struct sla_station *station;
00957 struct sla_trunk_ref *trunk_ref;
00958 AST_LIST_ENTRY(sla_event) entry;
00959 };
00960
00961
00962
00963 struct sla_failed_station {
00964 struct sla_station *station;
00965 struct timeval last_try;
00966 AST_LIST_ENTRY(sla_failed_station) entry;
00967 };
00968
00969
00970 struct sla_ringing_trunk {
00971 struct sla_trunk *trunk;
00972
00973 struct timeval ring_begin;
00974 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00975 AST_LIST_ENTRY(sla_ringing_trunk) entry;
00976 };
00977
00978 enum sla_station_hangup {
00979 SLA_STATION_HANGUP_NORMAL,
00980 SLA_STATION_HANGUP_TIMEOUT,
00981 };
00982
00983
00984 struct sla_ringing_station {
00985 struct sla_station *station;
00986
00987 struct timeval ring_begin;
00988 AST_LIST_ENTRY(sla_ringing_station) entry;
00989 };
00990
00991
00992
00993
00994 static struct {
00995
00996 pthread_t thread;
00997 ast_cond_t cond;
00998 ast_mutex_t lock;
00999 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
01000 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
01001 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
01002 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
01003 unsigned int stop:1;
01004
01005
01006 unsigned int attempt_callerid:1;
01007 } sla = {
01008 .thread = AST_PTHREADT_NULL,
01009 };
01010
01011
01012
01013 static int audio_buffers;
01014
01015
01016
01017
01018
01019
01020
01021
01022 static const char gain_map[] = {
01023 -15,
01024 -13,
01025 -10,
01026 -6,
01027 0,
01028 0,
01029 0,
01030 6,
01031 10,
01032 13,
01033 15,
01034 };
01035
01036
01037 static int admin_exec(struct ast_channel *chan, const char *data);
01038 static void *recordthread(void *args);
01039
01040 static const char *istalking(int x)
01041 {
01042 if (x > 0)
01043 return "(talking)";
01044 else if (x < 0)
01045 return "(unmonitored)";
01046 else
01047 return "(not talking)";
01048 }
01049
01050 static int careful_write(int fd, unsigned char *data, int len, int block)
01051 {
01052 int res;
01053 int x;
01054
01055 while (len) {
01056 if (block) {
01057 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01058 res = ioctl(fd, DAHDI_IOMUX, &x);
01059 } else
01060 res = 0;
01061 if (res >= 0)
01062 res = write(fd, data, len);
01063 if (res < 1) {
01064 if (errno != EAGAIN) {
01065 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01066 return -1;
01067 } else
01068 return 0;
01069 }
01070 len -= res;
01071 data += res;
01072 }
01073
01074 return 0;
01075 }
01076
01077 static int set_talk_volume(struct ast_conf_user *user, int volume)
01078 {
01079 char gain_adjust;
01080
01081
01082
01083
01084 gain_adjust = gain_map[volume + 5];
01085
01086 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01087 }
01088
01089 static int set_listen_volume(struct ast_conf_user *user, int volume)
01090 {
01091 char gain_adjust;
01092
01093
01094
01095
01096 gain_adjust = gain_map[volume + 5];
01097
01098 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01099 }
01100
01101 static void tweak_volume(struct volume *vol, enum volume_action action)
01102 {
01103 switch (action) {
01104 case VOL_UP:
01105 switch (vol->desired) {
01106 case 5:
01107 break;
01108 case 0:
01109 vol->desired = 2;
01110 break;
01111 case -2:
01112 vol->desired = 0;
01113 break;
01114 default:
01115 vol->desired++;
01116 break;
01117 }
01118 break;
01119 case VOL_DOWN:
01120 switch (vol->desired) {
01121 case -5:
01122 break;
01123 case 2:
01124 vol->desired = 0;
01125 break;
01126 case 0:
01127 vol->desired = -2;
01128 break;
01129 default:
01130 vol->desired--;
01131 break;
01132 }
01133 }
01134 }
01135
01136 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
01137 {
01138 tweak_volume(&user->talk, action);
01139
01140
01141
01142 if (!set_talk_volume(user, user->talk.desired))
01143 user->talk.actual = 0;
01144 else
01145 user->talk.actual = user->talk.desired;
01146 }
01147
01148 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
01149 {
01150 tweak_volume(&user->listen, action);
01151
01152
01153
01154 if (!set_listen_volume(user, user->listen.desired))
01155 user->listen.actual = 0;
01156 else
01157 user->listen.actual = user->listen.desired;
01158 }
01159
01160 static void reset_volumes(struct ast_conf_user *user)
01161 {
01162 signed char zero_volume = 0;
01163
01164 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01165 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
01166 }
01167
01168 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
01169 {
01170 unsigned char *data;
01171 int len;
01172 int res = -1;
01173
01174 ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
01175 "Conference: %s\r\n"
01176 "Marked: %d",
01177 ast_channel_name(chan),
01178 conf->confno,
01179 conf->markedusers);
01180
01181 if (!ast_check_hangup(chan))
01182 res = ast_autoservice_start(chan);
01183
01184 AST_LIST_LOCK(&confs);
01185
01186 switch(sound) {
01187 case ENTER:
01188 data = enter;
01189 len = sizeof(enter);
01190 break;
01191 case LEAVE:
01192 data = leave;
01193 len = sizeof(leave);
01194 break;
01195 default:
01196 data = NULL;
01197 len = 0;
01198 }
01199 if (data) {
01200 careful_write(conf->fd, data, len, 1);
01201 }
01202
01203 AST_LIST_UNLOCK(&confs);
01204
01205 if (!res)
01206 ast_autoservice_stop(chan);
01207 }
01208
01209 static int user_no_cmp(void *obj, void *arg, int flags)
01210 {
01211 struct ast_conf_user *user = obj;
01212 int *user_no = arg;
01213
01214 if (user->user_no == *user_no) {
01215 return (CMP_MATCH | CMP_STOP);
01216 }
01217
01218 return 0;
01219 }
01220
01221 static int user_max_cmp(void *obj, void *arg, int flags)
01222 {
01223 struct ast_conf_user *user = obj;
01224 int *max_no = arg;
01225
01226 if (user->user_no > *max_no) {
01227 *max_no = user->user_no;
01228 }
01229
01230 return 0;
01231 }
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247 static struct ast_conference *build_conf(const char *confno, const char *pin,
01248 const char *pinadmin, int make, int dynamic, int refcount,
01249 const struct ast_channel *chan, struct ast_test *test)
01250 {
01251 struct ast_conference *cnf;
01252 struct dahdi_confinfo dahdic = { 0, };
01253 int confno_int = 0;
01254 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
01255 struct ast_format tmp_fmt;
01256
01257 AST_LIST_LOCK(&confs);
01258
01259 AST_LIST_TRAVERSE(&confs, cnf, list) {
01260 if (!strcmp(confno, cnf->confno))
01261 break;
01262 }
01263
01264 if (cnf || (!make && !dynamic) || !cap_slin)
01265 goto cnfout;
01266
01267 ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
01268
01269 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01270 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01271 goto cnfout;
01272 }
01273
01274 ast_mutex_init(&cnf->playlock);
01275 ast_mutex_init(&cnf->listenlock);
01276 cnf->recordthread = AST_PTHREADT_NULL;
01277 ast_mutex_init(&cnf->recordthreadlock);
01278 cnf->announcethread = AST_PTHREADT_NULL;
01279 ast_mutex_init(&cnf->announcethreadlock);
01280 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01281 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01282 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01283 ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
01284
01285
01286 dahdic.confno = -1;
01287 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01288 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01289 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01290 if (test) {
01291
01292
01293
01294 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01295 } else {
01296 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01297 if (cnf->fd >= 0)
01298 close(cnf->fd);
01299 ao2_ref(cnf->usercontainer, -1);
01300 ast_mutex_destroy(&cnf->playlock);
01301 ast_mutex_destroy(&cnf->listenlock);
01302 ast_mutex_destroy(&cnf->recordthreadlock);
01303 ast_mutex_destroy(&cnf->announcethreadlock);
01304 ast_free(cnf);
01305 cnf = NULL;
01306 goto cnfout;
01307 }
01308 }
01309
01310 cnf->dahdiconf = dahdic.confno;
01311
01312
01313 cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
01314 if (cnf->chan) {
01315 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
01316 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
01317 dahdic.chan = 0;
01318 dahdic.confno = cnf->dahdiconf;
01319 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01320 if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
01321 if (test) {
01322 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01323 }
01324 ast_log(LOG_WARNING, "Error setting conference\n");
01325 if (cnf->chan)
01326 ast_hangup(cnf->chan);
01327 else
01328 close(cnf->fd);
01329 ao2_ref(cnf->usercontainer, -1);
01330 ast_mutex_destroy(&cnf->playlock);
01331 ast_mutex_destroy(&cnf->listenlock);
01332 ast_mutex_destroy(&cnf->recordthreadlock);
01333 ast_mutex_destroy(&cnf->announcethreadlock);
01334 ast_free(cnf);
01335 cnf = NULL;
01336 goto cnfout;
01337 }
01338 }
01339
01340
01341 cnf->start = time(NULL);
01342 cnf->maxusers = 0x7fffffff;
01343 cnf->isdynamic = dynamic ? 1 : 0;
01344 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01345 AST_LIST_INSERT_HEAD(&confs, cnf, list);
01346
01347
01348 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01349 conf_map[confno_int] = 1;
01350
01351 cnfout:
01352 cap_slin = ast_format_cap_destroy(cap_slin);
01353 if (cnf)
01354 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01355
01356 AST_LIST_UNLOCK(&confs);
01357
01358 return cnf;
01359 }
01360
01361 static char *complete_confno(const char *word, int state)
01362 {
01363 struct ast_conference *cnf;
01364 char *ret = NULL;
01365 int which = 0;
01366 int len = strlen(word);
01367
01368 AST_LIST_LOCK(&confs);
01369 AST_LIST_TRAVERSE(&confs, cnf, list) {
01370 if (!strncmp(word, cnf->confno, len) && ++which > state) {
01371
01372 ret = ast_strdup(cnf->confno);
01373 break;
01374 }
01375 }
01376 AST_LIST_UNLOCK(&confs);
01377 return ret;
01378 }
01379
01380 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
01381 {
01382 char usrno[50];
01383 struct ao2_iterator iter;
01384 struct ast_conf_user *usr;
01385 char *ret = NULL;
01386 int which = 0;
01387 int len = strlen(word);
01388
01389 iter = ao2_iterator_init(cnf->usercontainer, 0);
01390 for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
01391 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01392 if (!strncmp(word, usrno, len) && ++which > state) {
01393 ao2_ref(usr, -1);
01394 ret = ast_strdup(usrno);
01395 break;
01396 }
01397 }
01398 ao2_iterator_destroy(&iter);
01399 return ret;
01400 }
01401
01402 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
01403 {
01404 if (pos == 2) {
01405 return complete_confno(word, state);
01406 }
01407 if (pos == 3) {
01408 int len = strlen(word);
01409 char *ret = NULL;
01410 char *saved = NULL;
01411 char *myline;
01412 char *confno;
01413 struct ast_conference *cnf;
01414
01415 if (!strncasecmp(word, "all", len)) {
01416 if (state == 0) {
01417 return ast_strdup("all");
01418 }
01419 --state;
01420 }
01421
01422
01423 myline = ast_strdupa(line);
01424 strtok_r(myline, " ", &saved);
01425 strtok_r(NULL, " ", &saved);
01426 confno = strtok_r(NULL, " ", &saved);
01427
01428 AST_LIST_LOCK(&confs);
01429 AST_LIST_TRAVERSE(&confs, cnf, list) {
01430 if (!strcmp(confno, cnf->confno)) {
01431 ret = complete_userno(cnf, word, state);
01432 break;
01433 }
01434 }
01435 AST_LIST_UNLOCK(&confs);
01436
01437 return ret;
01438 }
01439 return NULL;
01440 }
01441
01442 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
01443 {
01444 if (pos == 2) {
01445 return complete_confno(word, state);
01446 }
01447 return NULL;
01448 }
01449
01450 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
01451 {
01452 int len;
01453
01454 if (pos == 2) {
01455 len = strlen(word);
01456 if (!strncasecmp(word, STR_CONCISE, len)) {
01457 if (state == 0) {
01458 return ast_strdup(STR_CONCISE);
01459 }
01460 --state;
01461 }
01462
01463 return complete_confno(word, state);
01464 }
01465 if (pos == 3 && state == 0) {
01466 char *saved = NULL;
01467 char *myline;
01468 char *confno;
01469
01470
01471 myline = ast_strdupa(line);
01472 strtok_r(myline, " ", &saved);
01473 strtok_r(NULL, " ", &saved);
01474 confno = strtok_r(NULL, " ", &saved);
01475
01476 if (!strcasecmp(confno, STR_CONCISE)) {
01477
01478 return NULL;
01479 }
01480
01481 len = strlen(word);
01482 if (!strncasecmp(word, STR_CONCISE, len)) {
01483 return ast_strdup(STR_CONCISE);
01484 }
01485 }
01486 return NULL;
01487 }
01488
01489 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01490 {
01491
01492 struct ast_conf_user *user;
01493 struct ast_conference *cnf;
01494 int hr, min, sec;
01495 int total = 0;
01496 time_t now;
01497 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
01498 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
01499
01500 switch (cmd) {
01501 case CLI_INIT:
01502 e->command = "meetme list";
01503 e->usage =
01504 "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
01505 " List all conferences or a specific conference.\n";
01506 return NULL;
01507 case CLI_GENERATE:
01508 return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
01509 }
01510
01511 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
01512
01513 int concise = (a->argc == 3);
01514 struct ast_str *marked_users;
01515
01516 if (!(marked_users = ast_str_create(30))) {
01517 return CLI_FAILURE;
01518 }
01519
01520 now = time(NULL);
01521 AST_LIST_LOCK(&confs);
01522 if (AST_LIST_EMPTY(&confs)) {
01523 if (!concise) {
01524 ast_cli(a->fd, "No active MeetMe conferences.\n");
01525 }
01526 AST_LIST_UNLOCK(&confs);
01527 ast_free(marked_users);
01528 return CLI_SUCCESS;
01529 }
01530 if (!concise) {
01531 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01532 }
01533 AST_LIST_TRAVERSE(&confs, cnf, list) {
01534 hr = (now - cnf->start) / 3600;
01535 min = ((now - cnf->start) % 3600) / 60;
01536 sec = (now - cnf->start) % 60;
01537 if (!concise) {
01538 if (cnf->markedusers == 0) {
01539 ast_str_set(&marked_users, 0, "N/A ");
01540 } else {
01541 ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
01542 }
01543 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
01544 ast_str_buffer(marked_users), hr, min, sec,
01545 cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01546 } else {
01547 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01548 cnf->confno,
01549 cnf->users,
01550 cnf->markedusers,
01551 hr, min, sec,
01552 cnf->isdynamic,
01553 cnf->locked);
01554 }
01555
01556 total += cnf->users;
01557 }
01558 AST_LIST_UNLOCK(&confs);
01559 if (!concise) {
01560 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01561 }
01562 ast_free(marked_users);
01563 return CLI_SUCCESS;
01564 }
01565 if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
01566 struct ao2_iterator user_iter;
01567 int concise = (a->argc == 4);
01568
01569
01570 if (AST_LIST_EMPTY(&confs)) {
01571 if (!concise) {
01572 ast_cli(a->fd, "No active MeetMe conferences.\n");
01573 }
01574 return CLI_SUCCESS;
01575 }
01576
01577 AST_LIST_LOCK(&confs);
01578 AST_LIST_TRAVERSE(&confs, cnf, list) {
01579 if (strcmp(cnf->confno, a->argv[2]) == 0) {
01580 break;
01581 }
01582 }
01583 if (!cnf) {
01584 if (!concise)
01585 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01586 AST_LIST_UNLOCK(&confs);
01587 return CLI_SUCCESS;
01588 }
01589
01590 time(&now);
01591 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01592 while((user = ao2_iterator_next(&user_iter))) {
01593 hr = (now - user->jointime) / 3600;
01594 min = ((now - user->jointime) % 3600) / 60;
01595 sec = (now - user->jointime) % 60;
01596 if (!concise) {
01597 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01598 user->user_no,
01599 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
01600 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
01601 ast_channel_name(user->chan),
01602 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01603 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01604 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01605 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01606 istalking(user->talking), hr, min, sec);
01607 } else {
01608 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01609 user->user_no,
01610 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
01611 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
01612 ast_channel_name(user->chan),
01613 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01614 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01615 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01616 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01617 user->talking, hr, min, sec);
01618 }
01619 ao2_ref(user, -1);
01620 }
01621 ao2_iterator_destroy(&user_iter);
01622 if (!concise) {
01623 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01624 }
01625 AST_LIST_UNLOCK(&confs);
01626 return CLI_SUCCESS;
01627 }
01628 return CLI_SHOWUSAGE;
01629 }
01630
01631
01632 static char *meetme_cmd_helper(struct ast_cli_args *a)
01633 {
01634
01635 struct ast_str *cmdline;
01636
01637
01638 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01639 return CLI_FAILURE;
01640 }
01641
01642 ast_str_set(&cmdline, 0, "%s", a->argv[2]);
01643 if (strcasestr(a->argv[1], "lock")) {
01644 if (strcasecmp(a->argv[1], "lock") == 0) {
01645
01646 ast_str_append(&cmdline, 0, ",L");
01647 } else {
01648
01649 ast_str_append(&cmdline, 0, ",l");
01650 }
01651 } else if (strcasestr(a->argv[1], "mute")) {
01652 if (strcasecmp(a->argv[1], "mute") == 0) {
01653
01654 if (strcasecmp(a->argv[3], "all") == 0) {
01655 ast_str_append(&cmdline, 0, ",N");
01656 } else {
01657 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
01658 }
01659 } else {
01660
01661 if (strcasecmp(a->argv[3], "all") == 0) {
01662 ast_str_append(&cmdline, 0, ",n");
01663 } else {
01664 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
01665 }
01666 }
01667 } else if (strcasecmp(a->argv[1], "kick") == 0) {
01668 if (strcasecmp(a->argv[3], "all") == 0) {
01669
01670 ast_str_append(&cmdline, 0, ",K");
01671 } else {
01672
01673 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
01674 }
01675 } else {
01676
01677
01678
01679
01680 ast_free(cmdline);
01681 return CLI_SHOWUSAGE;
01682 }
01683
01684 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01685
01686 admin_exec(NULL, ast_str_buffer(cmdline));
01687 ast_free(cmdline);
01688
01689 return CLI_SUCCESS;
01690 }
01691
01692 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01693 {
01694 switch (cmd) {
01695 case CLI_INIT:
01696 e->command = "meetme {lock|unlock}";
01697 e->usage =
01698 "Usage: meetme lock|unlock <confno>\n"
01699 " Lock or unlock a conference to new users.\n";
01700 return NULL;
01701 case CLI_GENERATE:
01702 return complete_meetmecmd_lock(a->word, a->pos, a->n);
01703 }
01704
01705 if (a->argc != 3) {
01706 return CLI_SHOWUSAGE;
01707 }
01708
01709 return meetme_cmd_helper(a);
01710 }
01711
01712 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01713 {
01714 switch (cmd) {
01715 case CLI_INIT:
01716 e->command = "meetme kick";
01717 e->usage =
01718 "Usage: meetme kick <confno> all|<userno>\n"
01719 " Kick a conference or a user in a conference.\n";
01720 return NULL;
01721 case CLI_GENERATE:
01722 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
01723 }
01724
01725 if (a->argc != 4) {
01726 return CLI_SHOWUSAGE;
01727 }
01728
01729 return meetme_cmd_helper(a);
01730 }
01731
01732 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01733 {
01734 switch (cmd) {
01735 case CLI_INIT:
01736 e->command = "meetme {mute|unmute}";
01737 e->usage =
01738 "Usage: meetme mute|unmute <confno> all|<userno>\n"
01739 " Mute or unmute a conference or a user in a conference.\n";
01740 return NULL;
01741 case CLI_GENERATE:
01742 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
01743 }
01744
01745 if (a->argc != 4) {
01746 return CLI_SHOWUSAGE;
01747 }
01748
01749 return meetme_cmd_helper(a);
01750 }
01751
01752 static const char *sla_hold_str(unsigned int hold_access)
01753 {
01754 const char *hold = "Unknown";
01755
01756 switch (hold_access) {
01757 case SLA_HOLD_OPEN:
01758 hold = "Open";
01759 break;
01760 case SLA_HOLD_PRIVATE:
01761 hold = "Private";
01762 default:
01763 break;
01764 }
01765
01766 return hold;
01767 }
01768
01769 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01770 {
01771 struct ao2_iterator i;
01772 struct sla_trunk *trunk;
01773
01774 switch (cmd) {
01775 case CLI_INIT:
01776 e->command = "sla show trunks";
01777 e->usage =
01778 "Usage: sla show trunks\n"
01779 " This will list all trunks defined in sla.conf\n";
01780 return NULL;
01781 case CLI_GENERATE:
01782 return NULL;
01783 }
01784
01785 ast_cli(a->fd, "\n"
01786 "=============================================================\n"
01787 "=== Configured SLA Trunks ===================================\n"
01788 "=============================================================\n"
01789 "===\n");
01790 i = ao2_iterator_init(sla_trunks, 0);
01791 for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
01792 struct sla_station_ref *station_ref;
01793 char ring_timeout[16] = "(none)";
01794
01795 ao2_lock(trunk);
01796
01797 if (trunk->ring_timeout) {
01798 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01799 }
01800
01801 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01802 "=== Trunk Name: %s\n"
01803 "=== ==> Device: %s\n"
01804 "=== ==> AutoContext: %s\n"
01805 "=== ==> RingTimeout: %s\n"
01806 "=== ==> BargeAllowed: %s\n"
01807 "=== ==> HoldAccess: %s\n"
01808 "=== ==> Stations ...\n",
01809 trunk->name, trunk->device,
01810 S_OR(trunk->autocontext, "(none)"),
01811 ring_timeout,
01812 trunk->barge_disabled ? "No" : "Yes",
01813 sla_hold_str(trunk->hold_access));
01814
01815 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
01816 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
01817 }
01818
01819 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
01820
01821 ao2_unlock(trunk);
01822 }
01823 ao2_iterator_destroy(&i);
01824 ast_cli(a->fd, "=============================================================\n\n");
01825
01826 return CLI_SUCCESS;
01827 }
01828
01829 static const char *trunkstate2str(enum sla_trunk_state state)
01830 {
01831 #define S(e) case e: return # e;
01832 switch (state) {
01833 S(SLA_TRUNK_STATE_IDLE)
01834 S(SLA_TRUNK_STATE_RINGING)
01835 S(SLA_TRUNK_STATE_UP)
01836 S(SLA_TRUNK_STATE_ONHOLD)
01837 S(SLA_TRUNK_STATE_ONHOLD_BYME)
01838 }
01839 return "Uknown State";
01840 #undef S
01841 }
01842
01843 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01844 {
01845 struct ao2_iterator i;
01846 struct sla_station *station;
01847
01848 switch (cmd) {
01849 case CLI_INIT:
01850 e->command = "sla show stations";
01851 e->usage =
01852 "Usage: sla show stations\n"
01853 " This will list all stations defined in sla.conf\n";
01854 return NULL;
01855 case CLI_GENERATE:
01856 return NULL;
01857 }
01858
01859 ast_cli(a->fd, "\n"
01860 "=============================================================\n"
01861 "=== Configured SLA Stations =================================\n"
01862 "=============================================================\n"
01863 "===\n");
01864 i = ao2_iterator_init(sla_stations, 0);
01865 for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
01866 struct sla_trunk_ref *trunk_ref;
01867 char ring_timeout[16] = "(none)";
01868 char ring_delay[16] = "(none)";
01869
01870 ao2_lock(station);
01871
01872 if (station->ring_timeout) {
01873 snprintf(ring_timeout, sizeof(ring_timeout),
01874 "%u", station->ring_timeout);
01875 }
01876 if (station->ring_delay) {
01877 snprintf(ring_delay, sizeof(ring_delay),
01878 "%u", station->ring_delay);
01879 }
01880 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01881 "=== Station Name: %s\n"
01882 "=== ==> Device: %s\n"
01883 "=== ==> AutoContext: %s\n"
01884 "=== ==> RingTimeout: %s\n"
01885 "=== ==> RingDelay: %s\n"
01886 "=== ==> HoldAccess: %s\n"
01887 "=== ==> Trunks ...\n",
01888 station->name, station->device,
01889 S_OR(station->autocontext, "(none)"),
01890 ring_timeout, ring_delay,
01891 sla_hold_str(station->hold_access));
01892 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01893 if (trunk_ref->ring_timeout) {
01894 snprintf(ring_timeout, sizeof(ring_timeout),
01895 "%u", trunk_ref->ring_timeout);
01896 } else
01897 strcpy(ring_timeout, "(none)");
01898 if (trunk_ref->ring_delay) {
01899 snprintf(ring_delay, sizeof(ring_delay),
01900 "%u", trunk_ref->ring_delay);
01901 } else
01902 strcpy(ring_delay, "(none)");
01903 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
01904 "=== ==> State: %s\n"
01905 "=== ==> RingTimeout: %s\n"
01906 "=== ==> RingDelay: %s\n",
01907 trunk_ref->trunk->name,
01908 trunkstate2str(trunk_ref->state),
01909 ring_timeout, ring_delay);
01910 }
01911 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01912 "===\n");
01913
01914 ao2_unlock(station);
01915 }
01916 ao2_iterator_destroy(&i);
01917 ast_cli(a->fd, "============================================================\n"
01918 "\n");
01919
01920 return CLI_SUCCESS;
01921 }
01922
01923 static struct ast_cli_entry cli_meetme[] = {
01924 AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
01925 AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
01926 AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
01927 AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
01928 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
01929 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
01930 };
01931
01932 static void conf_flush(int fd, struct ast_channel *chan)
01933 {
01934 int x;
01935
01936
01937
01938
01939 if (chan) {
01940 struct ast_frame *f;
01941
01942
01943
01944
01945 while (ast_waitfor(chan, 1) > 0) {
01946 f = ast_read(chan);
01947 if (f)
01948 ast_frfree(f);
01949 else
01950 break;
01951 }
01952 }
01953
01954
01955 x = DAHDI_FLUSH_ALL;
01956 if (ioctl(fd, DAHDI_FLUSH, &x))
01957 ast_log(LOG_WARNING, "Error flushing channel\n");
01958
01959 }
01960
01961
01962
01963
01964 static int conf_free(struct ast_conference *conf)
01965 {
01966 int x;
01967 struct announce_listitem *item;
01968
01969 AST_LIST_REMOVE(&confs, conf, list);
01970
01971
01972
01973
01974
01975
01976
01977
01978
01979
01980
01981 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01982
01983 if (conf->recording == MEETME_RECORD_ACTIVE) {
01984 conf->recording = MEETME_RECORD_TERMINATE;
01985 AST_LIST_UNLOCK(&confs);
01986 while (1) {
01987 usleep(1);
01988 AST_LIST_LOCK(&confs);
01989 if (conf->recording == MEETME_RECORD_OFF)
01990 break;
01991 AST_LIST_UNLOCK(&confs);
01992 }
01993 }
01994
01995 for (x = 0; x < AST_FRAME_BITS; x++) {
01996 if (conf->transframe[x])
01997 ast_frfree(conf->transframe[x]);
01998 if (conf->transpath[x])
01999 ast_translator_free_path(conf->transpath[x]);
02000 }
02001 if (conf->announcethread != AST_PTHREADT_NULL) {
02002 ast_mutex_lock(&conf->announcelistlock);
02003 conf->announcethread_stop = 1;
02004 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
02005 ast_cond_signal(&conf->announcelist_addition);
02006 ast_mutex_unlock(&conf->announcelistlock);
02007 pthread_join(conf->announcethread, NULL);
02008
02009 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
02010
02011 if (!item->vmrec){
02012 ast_filedelete(item->namerecloc, NULL);
02013 }
02014 ao2_ref(item, -1);
02015 }
02016 ast_mutex_destroy(&conf->announcelistlock);
02017 }
02018
02019 if (conf->origframe)
02020 ast_frfree(conf->origframe);
02021 if (conf->lchan)
02022 ast_hangup(conf->lchan);
02023 if (conf->chan)
02024 ast_hangup(conf->chan);
02025 if (conf->fd >= 0)
02026 close(conf->fd);
02027 if (conf->recordingfilename) {
02028 ast_free(conf->recordingfilename);
02029 }
02030 if (conf->usercontainer) {
02031 ao2_ref(conf->usercontainer, -1);
02032 }
02033 if (conf->recordingformat) {
02034 ast_free(conf->recordingformat);
02035 }
02036 ast_mutex_destroy(&conf->playlock);
02037 ast_mutex_destroy(&conf->listenlock);
02038 ast_mutex_destroy(&conf->recordthreadlock);
02039 ast_mutex_destroy(&conf->announcethreadlock);
02040 ast_free(conf);
02041
02042 return 0;
02043 }
02044
02045 static void conf_queue_dtmf(const struct ast_conference *conf,
02046 const struct ast_conf_user *sender, struct ast_frame *f)
02047 {
02048 struct ast_conf_user *user;
02049 struct ao2_iterator user_iter;
02050
02051 user_iter = ao2_iterator_init(conf->usercontainer, 0);
02052 while ((user = ao2_iterator_next(&user_iter))) {
02053 if (user == sender) {
02054 ao2_ref(user, -1);
02055 continue;
02056 }
02057 if (ast_write(user->chan, f) < 0)
02058 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
02059 ao2_ref(user, -1);
02060 }
02061 ao2_iterator_destroy(&user_iter);
02062 }
02063
02064 static void sla_queue_event_full(enum sla_event_type type,
02065 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
02066 {
02067 struct sla_event *event;
02068
02069 if (sla.thread == AST_PTHREADT_NULL) {
02070 ao2_ref(station, -1);
02071 ao2_ref(trunk_ref, -1);
02072 return;
02073 }
02074
02075 if (!(event = ast_calloc(1, sizeof(*event)))) {
02076 ao2_ref(station, -1);
02077 ao2_ref(trunk_ref, -1);
02078 return;
02079 }
02080
02081 event->type = type;
02082 event->trunk_ref = trunk_ref;
02083 event->station = station;
02084
02085 if (!lock) {
02086 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02087 return;
02088 }
02089
02090 ast_mutex_lock(&sla.lock);
02091 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02092 ast_cond_signal(&sla.cond);
02093 ast_mutex_unlock(&sla.lock);
02094 }
02095
02096 static void sla_queue_event_nolock(enum sla_event_type type)
02097 {
02098 sla_queue_event_full(type, NULL, NULL, 0);
02099 }
02100
02101 static void sla_queue_event(enum sla_event_type type)
02102 {
02103 sla_queue_event_full(type, NULL, NULL, 1);
02104 }
02105
02106
02107 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
02108 struct ast_conference *conf)
02109 {
02110 struct sla_station *station;
02111 struct sla_trunk_ref *trunk_ref = NULL;
02112 char *trunk_name;
02113 struct ao2_iterator i;
02114
02115 trunk_name = ast_strdupa(conf->confno);
02116 strsep(&trunk_name, "_");
02117 if (ast_strlen_zero(trunk_name)) {
02118 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
02119 return;
02120 }
02121
02122 i = ao2_iterator_init(sla_stations, 0);
02123 while ((station = ao2_iterator_next(&i))) {
02124 ao2_lock(station);
02125 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
02126 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
02127 ao2_ref(trunk_ref, 1);
02128 break;
02129 }
02130 }
02131 ao2_unlock(station);
02132 if (trunk_ref) {
02133
02134 break;
02135 }
02136 ao2_ref(station, -1);
02137 }
02138 ao2_iterator_destroy(&i);
02139
02140 if (!trunk_ref) {
02141 ast_debug(1, "Trunk not found for event!\n");
02142 return;
02143 }
02144
02145 sla_queue_event_full(type, trunk_ref, station, 1);
02146 }
02147
02148
02149 static int dispose_conf(struct ast_conference *conf)
02150 {
02151 int res = 0;
02152 int confno_int = 0;
02153
02154 AST_LIST_LOCK(&confs);
02155 if (ast_atomic_dec_and_test(&conf->refcount)) {
02156
02157 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
02158 conf_map[confno_int] = 0;
02159 }
02160 conf_free(conf);
02161 res = 1;
02162 }
02163 AST_LIST_UNLOCK(&confs);
02164
02165 return res;
02166 }
02167
02168 static int rt_extend_conf(const char *confno)
02169 {
02170 char currenttime[32];
02171 char endtime[32];
02172 struct timeval now;
02173 struct ast_tm tm;
02174 struct ast_variable *var, *orig_var;
02175 char bookid[51];
02176
02177 if (!extendby) {
02178 return 0;
02179 }
02180
02181 now = ast_tvnow();
02182
02183 ast_localtime(&now, &tm, NULL);
02184 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02185
02186 var = ast_load_realtime("meetme", "confno",
02187 confno, "startTime<= ", currenttime,
02188 "endtime>= ", currenttime, NULL);
02189
02190 orig_var = var;
02191
02192
02193 while (var) {
02194 if (!strcasecmp(var->name, "bookid")) {
02195 ast_copy_string(bookid, var->value, sizeof(bookid));
02196 }
02197 if (!strcasecmp(var->name, "endtime")) {
02198 ast_copy_string(endtime, var->value, sizeof(endtime));
02199 }
02200
02201 var = var->next;
02202 }
02203 ast_variables_destroy(orig_var);
02204
02205 ast_strptime(endtime, DATE_FORMAT, &tm);
02206 now = ast_mktime(&tm, NULL);
02207
02208 now.tv_sec += extendby;
02209
02210 ast_localtime(&now, &tm, NULL);
02211 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02212 strcat(currenttime, "0");
02213
02214 var = ast_load_realtime("meetme", "confno",
02215 confno, "startTime<= ", currenttime,
02216 "endtime>= ", currenttime, NULL);
02217
02218
02219 if (!var) {
02220 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
02221 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
02222 return 0;
02223
02224 }
02225
02226 ast_variables_destroy(var);
02227 return -1;
02228 }
02229
02230 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
02231 {
02232 char *original_moh;
02233
02234 ast_channel_lock(chan);
02235 original_moh = ast_strdupa(ast_channel_musicclass(chan));
02236 ast_channel_musicclass_set(chan, musicclass);
02237 ast_channel_unlock(chan);
02238
02239 ast_moh_start(chan, original_moh, NULL);
02240
02241 ast_channel_lock(chan);
02242 ast_channel_musicclass_set(chan, original_moh);
02243 ast_channel_unlock(chan);
02244 }
02245
02246 static const char *get_announce_filename(enum announcetypes type)
02247 {
02248 switch (type) {
02249 case CONF_HASLEFT:
02250 return "conf-hasleft";
02251 break;
02252 case CONF_HASJOIN:
02253 return "conf-hasjoin";
02254 break;
02255 default:
02256 return "";
02257 }
02258 }
02259
02260 static void *announce_thread(void *data)
02261 {
02262 struct announce_listitem *current;
02263 struct ast_conference *conf = data;
02264 int res;
02265 char filename[PATH_MAX] = "";
02266 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02267 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02268
02269 while (!conf->announcethread_stop) {
02270 ast_mutex_lock(&conf->announcelistlock);
02271 if (conf->announcethread_stop) {
02272 ast_mutex_unlock(&conf->announcelistlock);
02273 break;
02274 }
02275 if (AST_LIST_EMPTY(&conf->announcelist))
02276 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02277
02278 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02279 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02280
02281 ast_mutex_unlock(&conf->announcelistlock);
02282 if (conf->announcethread_stop) {
02283 break;
02284 }
02285
02286 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02287 ast_debug(1, "About to play %s\n", current->namerecloc);
02288 if (!ast_fileexists(current->namerecloc, NULL, NULL))
02289 continue;
02290 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02291 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02292 res = ast_waitstream(current->confchan, "");
02293 if (!res) {
02294 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02295 if (!ast_streamfile(current->confchan, filename, current->language))
02296 ast_waitstream(current->confchan, "");
02297 }
02298 }
02299 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
02300
02301 ast_filedelete(current->namerecloc, NULL);
02302 }
02303 }
02304 }
02305
02306
02307 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02308
02309 if (!current->vmrec) {
02310 ast_filedelete(current->namerecloc, NULL);
02311 }
02312 ao2_ref(current, -1);
02313 }
02314 return NULL;
02315 }
02316
02317 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
02318 {
02319 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02320 return 1;
02321 }
02322
02323 return (ast_channel_state(chan) == AST_STATE_UP);
02324 }
02325
02326 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
02327 {
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
02344 "Channel: %s\r\n"
02345 "Uniqueid: %s\r\n"
02346 "Meetme: %s\r\n"
02347 "Usernum: %d\r\n"
02348 "Status: %s\r\n",
02349 ast_channel_name(chan), ast_channel_uniqueid(chan),
02350 conf->confno,
02351 user->user_no, talking ? "on" : "off");
02352 }
02353
02354 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
02355 {
02356 int last_talking = user->talking;
02357 if (last_talking == talking)
02358 return;
02359
02360 user->talking = talking;
02361
02362 if (monitor) {
02363
02364 int was_talking = (last_talking > 0);
02365 int now_talking = (talking > 0);
02366 if (was_talking != now_talking) {
02367 send_talking_event(chan, conf, user, now_talking);
02368 }
02369 }
02370 }
02371
02372 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
02373 {
02374 struct ast_conf_user *user = obj;
02375
02376
02377 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02378 user->adminflags |= ADMINFLAG_HANGUP;
02379 }
02380 return 0;
02381 }
02382
02383 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
02384 {
02385 struct ast_conf_user *user = obj;
02386
02387
02388 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02389 user->adminflags |= ADMINFLAG_KICKME;
02390 }
02391 return 0;
02392 }
02393
02394 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
02395 {
02396 struct ast_conf_user *user = obj;
02397
02398
02399 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02400 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
02401 }
02402 return 0;
02403 }
02404
02405 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
02406 {
02407 struct ast_conf_user *user = obj;
02408
02409
02410 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02411 user->adminflags |= ADMINFLAG_MUTED;
02412 }
02413 return 0;
02414 }
02415
02416 enum menu_modes {
02417 MENU_DISABLED = 0,
02418 MENU_NORMAL,
02419 MENU_ADMIN,
02420 MENU_ADMIN_EXTENDED,
02421 };
02422
02423
02424
02425
02426
02427
02428
02429
02430
02431
02432
02433 static void meetme_menu_normal(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
02434 {
02435 switch (*dtmf) {
02436 case '1':
02437 *menu_mode = MENU_DISABLED;
02438
02439
02440 user->adminflags ^= ADMINFLAG_SELFMUTED;
02441
02442
02443 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02444 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02445 ast_waitstream(chan, "");
02446 }
02447 } else {
02448 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02449 ast_waitstream(chan, "");
02450 }
02451 }
02452 break;
02453
02454 case '2':
02455 *menu_mode = MENU_DISABLED;
02456 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02457 user->adminflags |= ADMINFLAG_T_REQUEST;
02458 }
02459
02460 if (user->adminflags & ADMINFLAG_T_REQUEST) {
02461 if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
02462 ast_waitstream(chan, "");
02463 }
02464 }
02465 break;
02466
02467 case '4':
02468 tweak_listen_volume(user, VOL_DOWN);
02469 break;
02470 case '5':
02471
02472 if (rt_schedule) {
02473 rt_extend_conf(conf->confno);
02474 }
02475 *menu_mode = MENU_DISABLED;
02476 break;
02477
02478 case '6':
02479 tweak_listen_volume(user, VOL_UP);
02480 break;
02481
02482 case '7':
02483 tweak_talk_volume(user, VOL_DOWN);
02484 break;
02485
02486 case '8':
02487 *menu_mode = MENU_DISABLED;
02488 break;
02489
02490 case '9':
02491 tweak_talk_volume(user, VOL_UP);
02492 break;
02493
02494 default:
02495 *menu_mode = MENU_DISABLED;
02496 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02497 ast_waitstream(chan, "");
02498 }
02499 break;
02500 }
02501 }
02502
02503
02504
02505
02506
02507
02508
02509
02510
02511
02512
02513 static void meetme_menu_admin(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
02514 {
02515 switch(*dtmf) {
02516 case '1':
02517 *menu_mode = MENU_DISABLED;
02518
02519 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02520 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02521 } else {
02522 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02523 }
02524
02525 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02526 if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02527 ast_waitstream(chan, "");
02528 }
02529 } else {
02530 if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02531 ast_waitstream(chan, "");
02532 }
02533 }
02534 break;
02535
02536 case '2':
02537 *menu_mode = MENU_DISABLED;
02538 if (conf->locked) {
02539 conf->locked = 0;
02540 if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
02541 ast_waitstream(chan, "");
02542 }
02543 } else {
02544 conf->locked = 1;
02545 if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
02546 ast_waitstream(chan, "");
02547 }
02548 }
02549 break;
02550
02551 case '3':
02552 {
02553 struct ast_conf_user *usr = NULL;
02554 int max_no = 0;
02555 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
02556 *menu_mode = MENU_DISABLED;
02557 usr = ao2_find(conf->usercontainer, &max_no, 0);
02558 if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
02559 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02560 ast_waitstream(chan, "");
02561 }
02562 } else {
02563 usr->adminflags |= ADMINFLAG_KICKME;
02564 }
02565 ao2_ref(usr, -1);
02566 ast_stopstream(chan);
02567 break;
02568 }
02569
02570 case '4':
02571 tweak_listen_volume(user, VOL_DOWN);
02572 break;
02573
02574 case '5':
02575
02576 if (rt_schedule) {
02577 if (!rt_extend_conf(conf->confno)) {
02578 if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
02579 ast_waitstream(chan, "");
02580 }
02581 } else {
02582 if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
02583 ast_waitstream(chan, "");
02584 }
02585 }
02586 ast_stopstream(chan);
02587 }
02588 *menu_mode = MENU_DISABLED;
02589 break;
02590
02591 case '6':
02592 tweak_listen_volume(user, VOL_UP);
02593 break;
02594
02595 case '7':
02596 tweak_talk_volume(user, VOL_DOWN);
02597 break;
02598
02599 case '8':
02600 if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
02601
02602 *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02603 ast_stopstream(chan);
02604 }
02605 *menu_mode = MENU_ADMIN_EXTENDED;
02606 break;
02607
02608 case '9':
02609 tweak_talk_volume(user, VOL_UP);
02610 break;
02611 default:
02612 menu_mode = MENU_DISABLED;
02613
02614 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02615 ast_waitstream(chan, "");
02616 }
02617 break;
02618 }
02619
02620 }
02621
02622
02623
02624
02625
02626
02627
02628
02629
02630
02631
02632
02633 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
02634 struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02635 struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
02636 struct ast_format_cap *cap_slin)
02637 {
02638 int keepplaying;
02639 int playednamerec;
02640 int res;
02641 struct ao2_iterator user_iter;
02642 struct ast_conf_user *usr = NULL;
02643
02644 switch(*dtmf) {
02645 case '1':
02646 keepplaying = 1;
02647 playednamerec = 0;
02648 if (conf->users == 1) {
02649 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
02650 res = ast_waitstream(chan, AST_DIGIT_ANY);
02651 ast_stopstream(chan);
02652 if (res > 0) {
02653 keepplaying = 0;
02654 }
02655 }
02656 } else if (conf->users == 2) {
02657 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
02658 res = ast_waitstream(chan, AST_DIGIT_ANY);
02659 ast_stopstream(chan);
02660 if (res > 0) {
02661 keepplaying = 0;
02662 }
02663 }
02664 } else {
02665 if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
02666 res = ast_waitstream(chan, AST_DIGIT_ANY);
02667 ast_stopstream(chan);
02668 if (res > 0) {
02669 keepplaying = 0;
02670 }
02671 }
02672 if (keepplaying) {
02673 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
02674 ast_stopstream(chan);
02675 if (res > 0) {
02676 keepplaying = 0;
02677 }
02678 }
02679 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
02680 res = ast_waitstream(chan, AST_DIGIT_ANY);
02681 ast_stopstream(chan);
02682 if (res > 0) {
02683 keepplaying = 0;
02684 }
02685 }
02686 }
02687 user_iter = ao2_iterator_init(conf->usercontainer, 0);
02688 while((usr = ao2_iterator_next(&user_iter))) {
02689 if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
02690 if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
02691 res = ast_waitstream(chan, AST_DIGIT_ANY);
02692 ast_stopstream(chan);
02693 if (res > 0) {
02694 keepplaying = 0;
02695 }
02696 }
02697 playednamerec = 1;
02698 }
02699 ao2_ref(usr, -1);
02700 }
02701 ao2_iterator_destroy(&user_iter);
02702 if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
02703 res = ast_waitstream(chan, AST_DIGIT_ANY);
02704 ast_stopstream(chan);
02705 if (res > 0) {
02706 keepplaying = 0;
02707 }
02708 }
02709
02710 *menu_mode = MENU_DISABLED;
02711 break;
02712
02713 case '2':
02714 if (conf->users == 1) {
02715 if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02716 ast_waitstream(chan, "");
02717 }
02718 } else {
02719 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
02720 }
02721 ast_stopstream(chan);
02722 *menu_mode = MENU_DISABLED;
02723 break;
02724
02725 case '3':
02726 if(conf->gmuted) {
02727 conf->gmuted = 0;
02728 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
02729 if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
02730 ast_waitstream(chan, "");
02731 }
02732 } else {
02733 conf->gmuted = 1;
02734 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
02735 if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
02736 ast_waitstream(chan, "");
02737 }
02738 }
02739 ast_stopstream(chan);
02740 *menu_mode = MENU_DISABLED;
02741 break;
02742
02743 case '4':
02744 if (conf->recording != MEETME_RECORD_ACTIVE) {
02745 ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
02746 if (!conf->recordingfilename) {
02747 const char *var;
02748 ast_channel_lock(chan);
02749 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02750 conf->recordingfilename = ast_strdup(var);
02751 }
02752 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02753 conf->recordingformat = ast_strdup(var);
02754 }
02755 ast_channel_unlock(chan);
02756 if (!conf->recordingfilename) {
02757 snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
02758 conf->recordingfilename = ast_strdup(recordingtmp);
02759 }
02760 if (!conf->recordingformat) {
02761 conf->recordingformat = ast_strdup("wav");
02762 }
02763 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02764 conf->confno, conf->recordingfilename, conf->recordingformat);
02765 }
02766
02767 ast_mutex_lock(&conf->recordthreadlock);
02768 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
02769 struct dahdi_confinfo dahdic;
02770
02771 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02772 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02773 dahdic.chan = 0;
02774 dahdic.confno = conf->dahdiconf;
02775 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02776 if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
02777 ast_log(LOG_WARNING, "Error starting listen channel\n");
02778 ast_hangup(conf->lchan);
02779 conf->lchan = NULL;
02780 } else {
02781 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02782 }
02783 }
02784 ast_mutex_unlock(&conf->recordthreadlock);
02785 if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
02786 ast_waitstream(chan, "");
02787 }
02788 }
02789
02790 ast_stopstream(chan);
02791 *menu_mode = MENU_DISABLED;
02792 break;
02793
02794 case '8':
02795 ast_stopstream(chan);
02796 *menu_mode = MENU_DISABLED;
02797 break;
02798
02799 default:
02800 if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02801 ast_waitstream(chan, "");
02802 }
02803 ast_stopstream(chan);
02804 *menu_mode = MENU_DISABLED;
02805 break;
02806 }
02807 }
02808
02809
02810
02811
02812
02813
02814
02815
02816
02817
02818
02819
02820 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
02821 struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02822 struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
02823 struct ast_format_cap *cap_slin)
02824 {
02825 switch (*menu_mode) {
02826 case MENU_DISABLED:
02827 break;
02828 case MENU_NORMAL:
02829 meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
02830 break;
02831 case MENU_ADMIN:
02832 meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
02833
02834 if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
02835 break;
02836 }
02837 case MENU_ADMIN_EXTENDED:
02838 meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
02839 recordingtmp, recordingtmp_size, cap_slin);
02840 break;
02841 }
02842 }
02843
02844 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02845 {
02846 struct ast_conf_user *user = NULL;
02847 int fd;
02848 struct dahdi_confinfo dahdic, dahdic_empty;
02849 struct ast_frame *f;
02850 struct ast_channel *c;
02851 struct ast_frame fr;
02852 int outfd;
02853 int ms;
02854 int nfds;
02855 int res;
02856 int retrydahdi;
02857 int origfd;
02858 int musiconhold = 0, mohtempstopped = 0;
02859 int firstpass = 0;
02860 int lastmarked = 0;
02861 int currentmarked = 0;
02862 int ret = -1;
02863 int x;
02864 enum menu_modes menu_mode = MENU_DISABLED;
02865 int talkreq_manager = 0;
02866 int using_pseudo = 0;
02867 int duration = 20;
02868 int sent_event = 0;
02869 int checked = 0;
02870 int announcement_played = 0;
02871 struct timeval now;
02872 struct ast_dsp *dsp = NULL;
02873 struct ast_app *agi_app;
02874 char *agifile, *mod_speex;
02875 const char *agifiledefault = "conf-background.agi", *tmpvar;
02876 char meetmesecs[30] = "";
02877 char exitcontext[AST_MAX_CONTEXT] = "";
02878 char recordingtmp[AST_MAX_EXTENSION] = "";
02879 char members[10] = "";
02880 int dtmf = 0, opt_waitmarked_timeout = 0;
02881 time_t timeout = 0;
02882 struct dahdi_bufferinfo bi;
02883 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02884 char *buf = __buf + AST_FRIENDLY_OFFSET;
02885 char *exitkeys = NULL;
02886 unsigned int calldurationlimit = 0;
02887 long timelimit = 0;
02888 long play_warning = 0;
02889 long warning_freq = 0;
02890 const char *warning_sound = NULL;
02891 const char *end_sound = NULL;
02892 char *parse;
02893 long time_left_ms = 0;
02894 struct timeval nexteventts = { 0, };
02895 int to;
02896 int setusercount = 0;
02897 int confsilence = 0, totalsilence = 0;
02898 char *mailbox, *context;
02899 struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
02900 struct ast_format tmpfmt;
02901
02902 if (!cap_slin) {
02903 goto conf_run_cleanup;
02904 }
02905 ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
02906
02907 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02908 goto conf_run_cleanup;
02909 }
02910
02911
02912 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02913 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02914 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02915 (opt_waitmarked_timeout > 0)) {
02916 timeout = time(NULL) + opt_waitmarked_timeout;
02917 }
02918
02919 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02920 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02921 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02922 }
02923
02924 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02925 char *limit_str, *warning_str, *warnfreq_str;
02926 const char *var;
02927
02928 parse = optargs[OPT_ARG_DURATION_LIMIT];
02929 limit_str = strsep(&parse, ":");
02930 warning_str = strsep(&parse, ":");
02931 warnfreq_str = parse;
02932
02933 timelimit = atol(limit_str);
02934 if (warning_str)
02935 play_warning = atol(warning_str);
02936 if (warnfreq_str)
02937 warning_freq = atol(warnfreq_str);
02938
02939 if (!timelimit) {
02940 timelimit = play_warning = warning_freq = 0;
02941 warning_sound = NULL;
02942 } else if (play_warning > timelimit) {
02943 if (!warning_freq) {
02944 play_warning = 0;
02945 } else {
02946 while (play_warning > timelimit)
02947 play_warning -= warning_freq;
02948 if (play_warning < 1)
02949 play_warning = warning_freq = 0;
02950 }
02951 }
02952
02953 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02954 if (play_warning) {
02955 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02956 }
02957 if (warning_freq) {
02958 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02959 }
02960
02961 ast_channel_lock(chan);
02962 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02963 var = ast_strdupa(var);
02964 }
02965 ast_channel_unlock(chan);
02966
02967 warning_sound = var ? var : "timeleft";
02968
02969 ast_channel_lock(chan);
02970 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02971 var = ast_strdupa(var);
02972 }
02973 ast_channel_unlock(chan);
02974
02975 end_sound = var ? var : NULL;
02976
02977
02978 calldurationlimit = 0;
02979
02980 if (!play_warning && !end_sound && timelimit) {
02981 calldurationlimit = timelimit / 1000;
02982 timelimit = play_warning = warning_freq = 0;
02983 } else {
02984 ast_debug(2, "Limit Data for this call:\n");
02985 ast_debug(2, "- timelimit = %ld\n", timelimit);
02986 ast_debug(2, "- play_warning = %ld\n", play_warning);
02987 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
02988 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02989 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
02990 }
02991 }
02992
02993
02994 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02995 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02996 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02997 else
02998 exitkeys = ast_strdupa("#");
02999 }
03000
03001 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03002 if (!conf->recordingfilename) {
03003 const char *var;
03004 ast_channel_lock(chan);
03005 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03006 conf->recordingfilename = ast_strdup(var);
03007 }
03008 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03009 conf->recordingformat = ast_strdup(var);
03010 }
03011 ast_channel_unlock(chan);
03012 if (!conf->recordingfilename) {
03013 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03014 conf->recordingfilename = ast_strdup(recordingtmp);
03015 }
03016 if (!conf->recordingformat) {
03017 conf->recordingformat = ast_strdup("wav");
03018 }
03019 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03020 conf->confno, conf->recordingfilename, conf->recordingformat);
03021 }
03022 }
03023
03024 ast_mutex_lock(&conf->recordthreadlock);
03025 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
03026 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
03027 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03028 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03029 dahdic.chan = 0;
03030 dahdic.confno = conf->dahdiconf;
03031 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03032 if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03033 ast_log(LOG_WARNING, "Error starting listen channel\n");
03034 ast_hangup(conf->lchan);
03035 conf->lchan = NULL;
03036 } else {
03037 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03038 }
03039 }
03040 ast_mutex_unlock(&conf->recordthreadlock);
03041
03042 ast_mutex_lock(&conf->announcethreadlock);
03043 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03044 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
03045 ast_mutex_init(&conf->announcelistlock);
03046 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
03047 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
03048 }
03049 ast_mutex_unlock(&conf->announcethreadlock);
03050
03051 time(&user->jointime);
03052
03053 user->timelimit = timelimit;
03054 user->play_warning = play_warning;
03055 user->warning_freq = warning_freq;
03056 user->warning_sound = warning_sound;
03057 user->end_sound = end_sound;
03058
03059 if (calldurationlimit > 0) {
03060 time(&user->kicktime);
03061 user->kicktime = user->kicktime + calldurationlimit;
03062 }
03063
03064 if (ast_tvzero(user->start_time))
03065 user->start_time = ast_tvnow();
03066 time_left_ms = user->timelimit;
03067
03068 if (user->timelimit) {
03069 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03070 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
03071 }
03072
03073 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
03074
03075 if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
03076 ast_waitstream(chan, "");
03077 goto outrun;
03078 }
03079
03080 ast_mutex_lock(&conf->playlock);
03081
03082 if (rt_schedule && conf->maxusers) {
03083 if (conf->users >= conf->maxusers) {
03084
03085 ast_mutex_unlock(&conf->playlock);
03086 if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
03087 ast_waitstream(chan, "");
03088 goto outrun;
03089 }
03090 }
03091
03092 ao2_lock(conf->usercontainer);
03093 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03094 user->user_no++;
03095 ao2_link(conf->usercontainer, user);
03096 ao2_unlock(conf->usercontainer);
03097
03098 user->chan = chan;
03099 user->userflags = *confflags;
03100 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03101 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03102 user->talking = -1;
03103
03104 ast_mutex_unlock(&conf->playlock);
03105
03106 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
03107 char destdir[PATH_MAX];
03108
03109 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03110
03111 if (ast_mkdir(destdir, 0777) != 0) {
03112 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03113 goto outrun;
03114 }
03115
03116 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03117 context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
03118 mailbox = strsep(&context, "@");
03119
03120 if (ast_strlen_zero(mailbox)) {
03121
03122 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
03123 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
03124 } else {
03125 if (ast_strlen_zero(context)) {
03126 context = "default";
03127 }
03128
03129 snprintf(user->namerecloc, sizeof(user->namerecloc),
03130 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
03131
03132
03133 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
03134 snprintf(user->namerecloc, sizeof(user->namerecloc),
03135 "%s/meetme-username-%s-%d", destdir,
03136 conf->confno, user->user_no);
03137 ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
03138 }
03139 }
03140 } else {
03141 snprintf(user->namerecloc, sizeof(user->namerecloc),
03142 "%s/meetme-username-%s-%d", destdir,
03143 conf->confno, user->user_no);
03144 }
03145
03146 res = 0;
03147 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
03148 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
03149 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
03150 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03151 if (res == -1)
03152 goto outrun;
03153
03154 }
03155
03156 ast_mutex_lock(&conf->playlock);
03157
03158 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03159 conf->markedusers++;
03160 conf->users++;
03161 if (rt_log_members) {
03162
03163 snprintf(members, sizeof(members), "%d", conf->users);
03164 ast_realtime_require_field("meetme",
03165 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03166 "members", RQ_UINTEGER1, strlen(members),
03167 NULL);
03168 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03169 }
03170 setusercount = 1;
03171
03172
03173 if (conf->users == 1)
03174 ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03175
03176 ast_mutex_unlock(&conf->playlock);
03177
03178
03179 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03180
03181 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03182 ast_channel_lock(chan);
03183 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03184 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03185 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
03186 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
03187 } else {
03188 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
03189 }
03190 ast_channel_unlock(chan);
03191 }
03192
03193
03194 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03195 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03196 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
03197 ast_waitstream(chan, "");
03198 }
03199 }
03200
03201 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03202 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03203 if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
03204 ast_waitstream(chan, "");
03205 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03206 if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
03207 ast_waitstream(chan, "");
03208 }
03209
03210 if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
03211 int keepplaying = 1;
03212
03213 if (conf->users == 2) {
03214 if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
03215 res = ast_waitstream(chan, AST_DIGIT_ANY);
03216 ast_stopstream(chan);
03217 if (res > 0)
03218 keepplaying = 0;
03219 else if (res == -1)
03220 goto outrun;
03221 }
03222 } else {
03223 if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
03224 res = ast_waitstream(chan, AST_DIGIT_ANY);
03225 ast_stopstream(chan);
03226 if (res > 0)
03227 keepplaying = 0;
03228 else if (res == -1)
03229 goto outrun;
03230 }
03231 if (keepplaying) {
03232 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03233 if (res > 0)
03234 keepplaying = 0;
03235 else if (res == -1)
03236 goto outrun;
03237 }
03238 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03239 res = ast_waitstream(chan, AST_DIGIT_ANY);
03240 ast_stopstream(chan);
03241 if (res > 0)
03242 keepplaying = 0;
03243 else if (res == -1)
03244 goto outrun;
03245 }
03246 }
03247 }
03248
03249 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03250
03251 ast_indicate(chan, -1);
03252 }
03253
03254 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03255 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
03256 goto outrun;
03257 }
03258
03259 if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03260 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
03261 goto outrun;
03262 }
03263
03264
03265 if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03266 (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03267 ast_free(mod_speex);
03268 ast_func_write(chan, "DENOISE(rx)", "on");
03269 }
03270
03271 retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
03272 user->dahdichannel = !retrydahdi;
03273
03274 dahdiretry:
03275 origfd = ast_channel_fd(chan, 0);
03276 if (retrydahdi) {
03277
03278 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03279 if (fd < 0) {
03280 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03281 goto outrun;
03282 }
03283 using_pseudo = 1;
03284
03285 memset(&bi, 0, sizeof(bi));
03286 bi.bufsize = CONF_SIZE / 2;
03287 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03288 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03289 bi.numbufs = audio_buffers;
03290 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03291 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03292 close(fd);
03293 goto outrun;
03294 }
03295 x = 1;
03296 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03297 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03298 close(fd);
03299 goto outrun;
03300 }
03301 nfds = 1;
03302 } else {
03303
03304 fd = ast_channel_fd(chan, 0);
03305 nfds = 0;
03306 }
03307 memset(&dahdic, 0, sizeof(dahdic));
03308 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03309
03310 dahdic.chan = 0;
03311 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03312 ast_log(LOG_WARNING, "Error getting conference\n");
03313 close(fd);
03314 goto outrun;
03315 }
03316 if (dahdic.confmode) {
03317
03318 if (!retrydahdi) {
03319 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03320 retrydahdi = 1;
03321 goto dahdiretry;
03322 }
03323 }
03324 memset(&dahdic, 0, sizeof(dahdic));
03325
03326 dahdic.chan = 0;
03327 dahdic.confno = conf->dahdiconf;
03328
03329 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03330 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
03331 struct announce_listitem *item;
03332 if (!(item = ao2_alloc(sizeof(*item), NULL)))
03333 goto outrun;
03334 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03335 ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
03336 item->confchan = conf->chan;
03337 item->confusers = conf->users;
03338 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03339 item->vmrec = 1;
03340 }
03341 item->announcetype = CONF_HASJOIN;
03342 ast_mutex_lock(&conf->announcelistlock);
03343 ao2_ref(item, +1);
03344 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03345 ast_cond_signal(&conf->announcelist_addition);
03346 ast_mutex_unlock(&conf->announcelistlock);
03347
03348 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03349 ;
03350 }
03351 ao2_ref(item, -1);
03352 }
03353
03354 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03355 dahdic.confmode = DAHDI_CONF_CONF;
03356 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03357 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03358 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03359 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03360 else
03361 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03362
03363 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03364 ast_log(LOG_WARNING, "Error setting conference\n");
03365 close(fd);
03366 goto outrun;
03367 }
03368 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
03369
03370 if (!sent_event) {
03371
03372
03373
03374
03375
03376
03377
03378
03379
03380
03381
03382
03383
03384
03385
03386
03387
03388 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
03389 "Channel: %s\r\n"
03390 "Uniqueid: %s\r\n"
03391 "Meetme: %s\r\n"
03392 "Usernum: %d\r\n"
03393 "CallerIDnum: %s\r\n"
03394 "CallerIDname: %s\r\n"
03395 "ConnectedLineNum: %s\r\n"
03396 "ConnectedLineName: %s\r\n",
03397 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
03398 user->user_no,
03399 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
03400 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
03401 S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
03402 S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>")
03403 );
03404 sent_event = 1;
03405 }
03406
03407 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03408 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03409 firstpass = 1;
03410 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03411 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03412 (conf->markedusers >= 1))) {
03413 conf_play(chan, conf, ENTER);
03414 }
03415 }
03416
03417 conf_flush(fd, chan);
03418
03419 if (dsp)
03420 ast_dsp_free(dsp);
03421
03422 if (!(dsp = ast_dsp_new())) {
03423 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03424 res = -1;
03425 }
03426
03427 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03428
03429
03430
03431 ast_channel_lock(chan);
03432 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03433 agifile = ast_strdupa(tmpvar);
03434 } else {
03435 agifile = ast_strdupa(agifiledefault);
03436 }
03437 ast_channel_unlock(chan);
03438
03439 if (user->dahdichannel) {
03440
03441 x = 1;
03442 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03443 }
03444
03445 agi_app = pbx_findapp("agi");
03446 if (agi_app) {
03447 ret = pbx_exec(chan, agi_app, agifile);
03448 } else {
03449 ast_log(LOG_WARNING, "Could not find application (agi)\n");
03450 ret = -2;
03451 }
03452 if (user->dahdichannel) {
03453
03454 x = 0;
03455 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03456 }
03457 } else {
03458 int lastusers = conf->users;
03459 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03460
03461 x = 1;
03462 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03463 }
03464
03465 for (;;) {
03466 int menu_was_active = 0;
03467
03468 outfd = -1;
03469 ms = -1;
03470 now = ast_tvnow();
03471
03472 if (rt_schedule && conf->endtime) {
03473 char currenttime[32];
03474 long localendtime = 0;
03475 int extended = 0;
03476 struct ast_tm tm;
03477 struct ast_variable *var, *origvar;
03478 struct timeval tmp;
03479
03480 if (now.tv_sec % 60 == 0) {
03481 if (!checked) {
03482 ast_localtime(&now, &tm, NULL);
03483 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03484 var = origvar = ast_load_realtime("meetme", "confno",
03485 conf->confno, "starttime <=", currenttime,
03486 "endtime >=", currenttime, NULL);
03487
03488 for ( ; var; var = var->next) {
03489 if (!strcasecmp(var->name, "endtime")) {
03490 struct ast_tm endtime_tm;
03491 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03492 tmp = ast_mktime(&endtime_tm, NULL);
03493 localendtime = tmp.tv_sec;
03494 }
03495 }
03496 ast_variables_destroy(origvar);
03497
03498
03499
03500 if (localendtime > conf->endtime){
03501 conf->endtime = localendtime;
03502 extended = 1;
03503 }
03504
03505 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03506 ast_verbose("Quitting time...\n");
03507 goto outrun;
03508 }
03509
03510 if (!announcement_played && conf->endalert) {
03511 if (now.tv_sec + conf->endalert >= conf->endtime) {
03512 if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
03513 ast_waitstream(chan, "");
03514 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
03515 if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
03516 ast_waitstream(chan, "");
03517 if (musiconhold) {
03518 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03519 }
03520 announcement_played = 1;
03521 }
03522 }
03523
03524 if (extended) {
03525 announcement_played = 0;
03526 }
03527
03528 checked = 1;
03529 }
03530 } else {
03531 checked = 0;
03532 }
03533 }
03534
03535 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03536 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03537 ret = 0;
03538 } else {
03539 ret = -1;
03540 }
03541 break;
03542 }
03543
03544 to = -1;
03545 if (user->timelimit) {
03546 int minutes = 0, seconds = 0, remain = 0;
03547
03548 to = ast_tvdiff_ms(nexteventts, now);
03549 if (to < 0) {
03550 to = 0;
03551 }
03552 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03553 if (time_left_ms < to) {
03554 to = time_left_ms;
03555 }
03556
03557 if (time_left_ms <= 0) {
03558 if (user->end_sound) {
03559 res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
03560 res = ast_waitstream(chan, "");
03561 }
03562 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03563 ret = 0;
03564 } else {
03565 ret = -1;
03566 }
03567 break;
03568 }
03569
03570 if (!to) {
03571 if (time_left_ms >= 5000) {
03572
03573 remain = (time_left_ms + 500) / 1000;
03574 if (remain / 60 >= 1) {
03575 minutes = remain / 60;
03576 seconds = remain % 60;
03577 } else {
03578 seconds = remain;
03579 }
03580
03581
03582 if (user->warning_sound && user->play_warning) {
03583 if (!strcmp(user->warning_sound, "timeleft")) {
03584
03585 res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
03586 res = ast_waitstream(chan, "");
03587 if (minutes) {
03588 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03589 res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
03590 res = ast_waitstream(chan, "");
03591 }
03592 if (seconds) {
03593 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03594 res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
03595 res = ast_waitstream(chan, "");
03596 }
03597 } else {
03598 res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
03599 res = ast_waitstream(chan, "");
03600 }
03601 if (musiconhold) {
03602 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03603 }
03604 }
03605 }
03606 if (user->warning_freq) {
03607 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03608 } else {
03609 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03610 }
03611 }
03612 }
03613
03614 now = ast_tvnow();
03615 if (timeout && now.tv_sec >= timeout) {
03616 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03617 ret = 0;
03618 } else {
03619 ret = -1;
03620 }
03621 break;
03622 }
03623
03624
03625
03626
03627 if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03628 set_talk_volume(user, user->listen.desired);
03629 }
03630
03631 menu_was_active = menu_mode;
03632
03633 currentmarked = conf->markedusers;
03634 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03635 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03636 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03637 lastmarked == 0) {
03638 if (currentmarked == 1 && conf->users > 1) {
03639 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03640 if (conf->users - 1 == 1) {
03641 if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
03642 ast_waitstream(chan, "");
03643 }
03644 } else {
03645 if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
03646 ast_waitstream(chan, "");
03647 }
03648 }
03649 }
03650 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03651 if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
03652 ast_waitstream(chan, "");
03653 }
03654 }
03655 }
03656
03657
03658 user->userflags = *confflags;
03659
03660 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03661 if (currentmarked == 0) {
03662 if (lastmarked != 0) {
03663 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03664 if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
03665 ast_waitstream(chan, "");
03666 }
03667 }
03668 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03669 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03670 ret = 0;
03671 }
03672 break;
03673 } else {
03674 dahdic.confmode = DAHDI_CONF_CONF;
03675 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03676 ast_log(LOG_WARNING, "Error setting conference\n");
03677 close(fd);
03678 goto outrun;
03679 }
03680 }
03681 }
03682 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03683 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03684 musiconhold = 1;
03685 }
03686 } else if (currentmarked >= 1 && lastmarked == 0) {
03687
03688 timeout = 0;
03689 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03690 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03691 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03692 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03693 } else {
03694 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03695 }
03696 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03697 ast_log(LOG_WARNING, "Error setting conference\n");
03698 close(fd);
03699 goto outrun;
03700 }
03701 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03702 ast_moh_stop(chan);
03703 musiconhold = 0;
03704 }
03705 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03706 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03707 if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
03708 ast_waitstream(chan, "");
03709 }
03710 conf_play(chan, conf, ENTER);
03711 }
03712 }
03713 }
03714
03715
03716 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03717 if (conf->users == 1) {
03718 if (!musiconhold) {
03719 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03720 musiconhold = 1;
03721 }
03722 } else {
03723 if (musiconhold) {
03724 ast_moh_stop(chan);
03725 musiconhold = 0;
03726 }
03727 }
03728 }
03729
03730
03731 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03732 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03733 ret = 0;
03734 } else {
03735 ret = -1;
03736 }
03737 break;
03738 }
03739
03740
03741 if (conf->users != lastusers) {
03742 if (conf->users < lastusers) {
03743 ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
03744 }
03745 lastusers = conf->users;
03746 }
03747
03748
03749
03750
03751 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03752 dahdic.confmode ^= DAHDI_CONF_TALKER;
03753 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03754 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03755 ret = -1;
03756 break;
03757 }
03758
03759
03760 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03761 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03762 }
03763
03764
03765
03766
03767
03768
03769
03770
03771
03772
03773
03774
03775
03776
03777
03778 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03779 "Channel: %s\r\n"
03780 "Uniqueid: %s\r\n"
03781 "Meetme: %s\r\n"
03782 "Usernum: %d\r\n"
03783 "Status: on\r\n",
03784 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03785 }
03786
03787
03788 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03789 dahdic.confmode |= DAHDI_CONF_TALKER;
03790 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03791 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03792 ret = -1;
03793 break;
03794 }
03795
03796
03797
03798
03799
03800 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03801 "Channel: %s\r\n"
03802 "Uniqueid: %s\r\n"
03803 "Meetme: %s\r\n"
03804 "Usernum: %d\r\n"
03805 "Status: off\r\n",
03806 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03807 }
03808
03809 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03810 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03811 talkreq_manager = 1;
03812
03813
03814
03815
03816
03817
03818
03819
03820
03821
03822
03823
03824
03825
03826
03827
03828 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03829 "Channel: %s\r\n"
03830 "Uniqueid: %s\r\n"
03831 "Meetme: %s\r\n"
03832 "Usernum: %d\r\n"
03833 "Status: on\r\n",
03834 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03835 }
03836
03837 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03838 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03839 talkreq_manager = 0;
03840
03841
03842
03843
03844
03845 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03846 "Channel: %s\r\n"
03847 "Uniqueid: %s\r\n"
03848 "Meetme: %s\r\n"
03849 "Usernum: %d\r\n"
03850 "Status: off\r\n",
03851 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03852 }
03853
03854
03855 if (user->adminflags & ADMINFLAG_HANGUP) {
03856 ret = 0;
03857 break;
03858 }
03859
03860
03861 if (user->adminflags & ADMINFLAG_KICKME) {
03862
03863 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03864 !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
03865 ast_waitstream(chan, "");
03866 }
03867 ret = 0;
03868 break;
03869 }
03870
03871
03872 if (ast_check_hangup(chan)) {
03873 break;
03874 }
03875
03876 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03877
03878 if (c) {
03879 char dtmfstr[2] = "";
03880
03881 if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
03882 if (using_pseudo) {
03883
03884 close(fd);
03885 using_pseudo = 0;
03886 }
03887 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03888 retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
03889 user->dahdichannel = !retrydahdi;
03890 goto dahdiretry;
03891 }
03892 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03893 f = ast_read_noaudio(c);
03894 } else {
03895 f = ast_read(c);
03896 }
03897 if (!f) {
03898 break;
03899 }
03900 if (f->frametype == AST_FRAME_DTMF) {
03901 dtmfstr[0] = f->subclass.integer;
03902 dtmfstr[1] = '\0';
03903 }
03904
03905 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
03906 if (user->talk.actual) {
03907 ast_frame_adjust_volume(f, user->talk.actual);
03908 }
03909
03910 if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03911 if (user->talking == -1) {
03912 user->talking = 0;
03913 }
03914
03915 res = ast_dsp_silence(dsp, f, &totalsilence);
03916 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03917 set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03918 }
03919
03920 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03921 set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03922 }
03923 }
03924 if (using_pseudo) {
03925
03926
03927
03928
03929
03930
03931
03932
03933
03934
03935
03936
03937 if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03938 careful_write(fd, f->data.ptr, f->datalen, 0);
03939 }
03940 }
03941 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
03942 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03943 conf_queue_dtmf(conf, user, f);
03944 }
03945
03946 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03947 ast_log(LOG_WARNING, "Error setting conference\n");
03948 close(fd);
03949 ast_frfree(f);
03950 goto outrun;
03951 }
03952
03953
03954
03955
03956 if (!menu_mode && user->talk.desired && !user->talk.actual) {
03957 set_talk_volume(user, 0);
03958 }
03959
03960 if (musiconhold) {
03961 ast_moh_stop(chan);
03962 } else if (!menu_mode) {
03963 char *menu_to_play;
03964 if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03965 menu_mode = MENU_ADMIN;
03966 menu_to_play = "conf-adminmenu-18";
03967 } else {
03968 menu_mode = MENU_NORMAL;
03969 menu_to_play = "conf-usermenu-162";
03970 }
03971
03972 if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
03973 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03974 ast_stopstream(chan);
03975 } else {
03976 dtmf = 0;
03977 }
03978 } else {
03979 dtmf = f->subclass.integer;
03980 }
03981
03982 if (dtmf > 0) {
03983 meetme_menu(&menu_mode, &dtmf, conf, confflags,
03984 chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
03985 }
03986
03987 if (musiconhold && !menu_mode) {
03988 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03989 }
03990
03991
03992 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03993 ast_log(LOG_WARNING, "Error setting conference\n");
03994 close(fd);
03995 ast_frfree(f);
03996 goto outrun;
03997 }
03998
03999 conf_flush(fd, chan);
04000
04001
04002
04003
04004 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
04005 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04006 conf_queue_dtmf(conf, user, f);
04007 }
04008
04009 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
04010 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
04011 ret = 0;
04012 ast_frfree(f);
04013 break;
04014 } else {
04015 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
04016 }
04017 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
04018 (strchr(exitkeys, f->subclass.integer))) {
04019 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
04020
04021 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04022 conf_queue_dtmf(conf, user, f);
04023 }
04024 ret = 0;
04025 ast_frfree(f);
04026 break;
04027 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
04028 && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04029 conf_queue_dtmf(conf, user, f);
04030 } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
04031 switch (f->subclass.integer) {
04032 case AST_CONTROL_HOLD:
04033 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
04034 break;
04035 default:
04036 break;
04037 }
04038 } else if (f->frametype == AST_FRAME_NULL) {
04039
04040 } else if (f->frametype == AST_FRAME_CONTROL) {
04041 switch (f->subclass.integer) {
04042 case AST_CONTROL_BUSY:
04043 case AST_CONTROL_CONGESTION:
04044 ast_frfree(f);
04045 goto outrun;
04046 break;
04047 default:
04048 ast_debug(1,
04049 "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04050 ast_channel_name(chan), f->frametype, f->subclass.integer);
04051 }
04052 } else {
04053 ast_debug(1,
04054 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04055 ast_channel_name(chan), f->frametype, f->subclass.integer);
04056 }
04057 ast_frfree(f);
04058 } else if (outfd > -1) {
04059 res = read(outfd, buf, CONF_SIZE);
04060 if (res > 0) {
04061 memset(&fr, 0, sizeof(fr));
04062 fr.frametype = AST_FRAME_VOICE;
04063 ast_format_set(&fr.subclass.format, AST_FORMAT_SLINEAR, 0);
04064 fr.datalen = res;
04065 fr.samples = res / 2;
04066 fr.data.ptr = buf;
04067 fr.offset = AST_FRIENDLY_OFFSET;
04068 if (!user->listen.actual &&
04069 (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
04070 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
04071 (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
04072 )) {
04073 int idx;
04074 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
04075 if (ast_format_to_old_bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
04076 break;
04077 }
04078 }
04079 if (idx >= AST_FRAME_BITS) {
04080 goto bailoutandtrynormal;
04081 }
04082 ast_mutex_lock(&conf->listenlock);
04083 if (!conf->transframe[idx]) {
04084 if (conf->origframe) {
04085 if (musiconhold
04086 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04087 && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
04088 && confsilence < MEETME_DELAYDETECTTALK) {
04089 ast_moh_stop(chan);
04090 mohtempstopped = 1;
04091 }
04092 if (!conf->transpath[idx]) {
04093 struct ast_format src;
04094 struct ast_format dst;
04095 ast_format_set(&src, AST_FORMAT_SLINEAR, 0);
04096 ast_format_from_old_bitfield(&dst, (1 << idx));
04097 conf->transpath[idx] = ast_translator_build_path(&dst, &src);
04098 }
04099 if (conf->transpath[idx]) {
04100 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
04101 if (!conf->transframe[idx]) {
04102 conf->transframe[idx] = &ast_null_frame;
04103 }
04104 }
04105 }
04106 }
04107 if (conf->transframe[idx]) {
04108 if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
04109 can_write(chan, confflags)) {
04110 struct ast_frame *cur;
04111
04112
04113
04114 for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
04115 if (ast_write(chan, cur)) {
04116 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04117 break;
04118 }
04119 }
04120 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04121 mohtempstopped = 0;
04122 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04123 }
04124 }
04125 } else {
04126 ast_mutex_unlock(&conf->listenlock);
04127 goto bailoutandtrynormal;
04128 }
04129 ast_mutex_unlock(&conf->listenlock);
04130 } else {
04131 bailoutandtrynormal:
04132 if (musiconhold
04133 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04134 && !ast_dsp_silence(dsp, &fr, &confsilence)
04135 && confsilence < MEETME_DELAYDETECTTALK) {
04136 ast_moh_stop(chan);
04137 mohtempstopped = 1;
04138 }
04139 if (user->listen.actual) {
04140 ast_frame_adjust_volume(&fr, user->listen.actual);
04141 }
04142 if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
04143 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04144 }
04145 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04146 mohtempstopped = 0;
04147 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04148 }
04149 }
04150 } else {
04151 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
04152 }
04153 }
04154 lastmarked = currentmarked;
04155 }
04156 }
04157
04158 if (musiconhold) {
04159 ast_moh_stop(chan);
04160 }
04161
04162 if (using_pseudo) {
04163 close(fd);
04164 } else {
04165
04166 dahdic.chan = 0;
04167 dahdic.confno = 0;
04168 dahdic.confmode = 0;
04169 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04170 ast_log(LOG_WARNING, "Error setting conference\n");
04171 }
04172 }
04173
04174 reset_volumes(user);
04175
04176 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
04177 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04178 conf_play(chan, conf, LEAVE);
04179 }
04180
04181 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
04182 struct announce_listitem *item;
04183 if (!(item = ao2_alloc(sizeof(*item), NULL)))
04184 goto outrun;
04185 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
04186 ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
04187 item->confchan = conf->chan;
04188 item->confusers = conf->users;
04189 item->announcetype = CONF_HASLEFT;
04190 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
04191 item->vmrec = 1;
04192 }
04193 ast_mutex_lock(&conf->announcelistlock);
04194 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04195 ast_cond_signal(&conf->announcelist_addition);
04196 ast_mutex_unlock(&conf->announcelistlock);
04197 } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) && !ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC) && conf->users == 1) {
04198
04199 ast_filedelete(user->namerecloc, NULL);
04200 }
04201
04202 outrun:
04203 AST_LIST_LOCK(&confs);
04204
04205 if (dsp) {
04206 ast_dsp_free(dsp);
04207 }
04208
04209 if (user->user_no) {
04210
04211 now = ast_tvnow();
04212
04213 if (sent_event) {
04214
04215
04216
04217
04218
04219
04220
04221
04222
04223
04224
04225
04226
04227
04228
04229 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
04230 "Channel: %s\r\n"
04231 "Uniqueid: %s\r\n"
04232 "Meetme: %s\r\n"
04233 "Usernum: %d\r\n"
04234 "CallerIDNum: %s\r\n"
04235 "CallerIDName: %s\r\n"
04236 "ConnectedLineNum: %s\r\n"
04237 "ConnectedLineName: %s\r\n"
04238 "Duration: %ld\r\n",
04239 ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
04240 user->user_no,
04241 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
04242 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
04243 S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
04244 S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>"),
04245 (long)(now.tv_sec - user->jointime));
04246 }
04247
04248 if (setusercount) {
04249 conf->users--;
04250 if (rt_log_members) {
04251
04252 snprintf(members, sizeof(members), "%d", conf->users);
04253 ast_realtime_require_field("meetme",
04254 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04255 "members", RQ_UINTEGER1, strlen(members),
04256 NULL);
04257 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04258 }
04259 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04260 conf->markedusers--;
04261 }
04262 }
04263
04264 ao2_unlink(conf->usercontainer, user);
04265
04266
04267 if (!conf->users) {
04268 ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04269 }
04270
04271
04272 if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
04273 ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
04274 }
04275
04276
04277 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04278 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04279
04280
04281 if (rt_schedule) {
04282 pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04283 }
04284 }
04285 ao2_ref(user, -1);
04286 AST_LIST_UNLOCK(&confs);
04287
04288
04289 conf_run_cleanup:
04290 cap_slin = ast_format_cap_destroy(cap_slin);
04291
04292 return ret;
04293 }
04294
04295 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04296 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04297 {
04298 struct ast_variable *var, *origvar;
04299 struct ast_conference *cnf;
04300
04301 *too_early = 0;
04302
04303
04304 AST_LIST_LOCK(&confs);
04305 AST_LIST_TRAVERSE(&confs, cnf, list) {
04306 if (!strcmp(confno, cnf->confno)) {
04307 break;
04308 }
04309 }
04310 if (cnf) {
04311 cnf->refcount += refcount;
04312 }
04313 AST_LIST_UNLOCK(&confs);
04314
04315 if (!cnf) {
04316 char *pin = NULL, *pinadmin = NULL;
04317 int maxusers = 0;
04318 struct timeval now;
04319 char recordingfilename[256] = "";
04320 char recordingformat[11] = "";
04321 char currenttime[32] = "";
04322 char eatime[32] = "";
04323 char bookid[51] = "";
04324 char recordingtmp[AST_MAX_EXTENSION] = "";
04325 char useropts[OPTIONS_LEN + 1] = "";
04326 char adminopts[OPTIONS_LEN + 1] = "";
04327 struct ast_tm tm, etm;
04328 struct timeval endtime = { .tv_sec = 0 };
04329 const char *var2;
04330
04331 if (rt_schedule) {
04332 now = ast_tvnow();
04333
04334 ast_localtime(&now, &tm, NULL);
04335 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04336
04337 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04338
04339 var = ast_load_realtime("meetme", "confno",
04340 confno, "starttime <= ", currenttime, "endtime >= ",
04341 currenttime, NULL);
04342
04343 if (!var && fuzzystart) {
04344 now = ast_tvnow();
04345 now.tv_sec += fuzzystart;
04346
04347 ast_localtime(&now, &tm, NULL);
04348 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04349 var = ast_load_realtime("meetme", "confno",
04350 confno, "starttime <= ", currenttime, "endtime >= ",
04351 currenttime, NULL);
04352 }
04353
04354 if (!var && earlyalert) {
04355 now = ast_tvnow();
04356 now.tv_sec += earlyalert;
04357 ast_localtime(&now, &etm, NULL);
04358 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04359 var = ast_load_realtime("meetme", "confno",
04360 confno, "starttime <= ", eatime, "endtime >= ",
04361 currenttime, NULL);
04362 if (var) {
04363 *too_early = 1;
04364 }
04365 }
04366
04367 } else {
04368 var = ast_load_realtime("meetme", "confno", confno, NULL);
04369 }
04370
04371 if (!var) {
04372 return NULL;
04373 }
04374
04375 if (rt_schedule && *too_early) {
04376
04377 if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
04378 ast_waitstream(chan, "");
04379 }
04380 ast_variables_destroy(var);
04381 return NULL;
04382 }
04383
04384 for (origvar = var; var; var = var->next) {
04385 if (!strcasecmp(var->name, "pin")) {
04386 pin = ast_strdupa(var->value);
04387 } else if (!strcasecmp(var->name, "adminpin")) {
04388 pinadmin = ast_strdupa(var->value);
04389 } else if (!strcasecmp(var->name, "bookId")) {
04390 ast_copy_string(bookid, var->value, sizeof(bookid));
04391 } else if (!strcasecmp(var->name, "opts")) {
04392 ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04393 } else if (!strcasecmp(var->name, "maxusers")) {
04394 maxusers = atoi(var->value);
04395 } else if (!strcasecmp(var->name, "adminopts")) {
04396 ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04397 } else if (!strcasecmp(var->name, "recordingfilename")) {
04398 ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04399 } else if (!strcasecmp(var->name, "recordingformat")) {
04400 ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04401 } else if (!strcasecmp(var->name, "endtime")) {
04402 struct ast_tm endtime_tm;
04403 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04404 endtime = ast_mktime(&endtime_tm, NULL);
04405 }
04406 }
04407
04408 ast_variables_destroy(origvar);
04409
04410 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04411
04412 if (cnf) {
04413 struct ast_flags64 tmp_flags;
04414
04415 cnf->maxusers = maxusers;
04416 cnf->endalert = endalert;
04417 cnf->endtime = endtime.tv_sec;
04418 cnf->useropts = ast_strdup(useropts);
04419 cnf->adminopts = ast_strdup(adminopts);
04420 cnf->bookid = ast_strdup(bookid);
04421 if (!ast_strlen_zero(recordingfilename)) {
04422 cnf->recordingfilename = ast_strdup(recordingfilename);
04423 }
04424 if (!ast_strlen_zero(recordingformat)) {
04425 cnf->recordingformat = ast_strdup(recordingformat);
04426 }
04427
04428
04429
04430 ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04431 ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04432
04433 if (strchr(cnf->useropts, 'r')) {
04434 if (ast_strlen_zero(recordingfilename)) {
04435 ast_channel_lock(chan);
04436 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04437 ast_free(cnf->recordingfilename);
04438 cnf->recordingfilename = ast_strdup(var2);
04439 }
04440 ast_channel_unlock(chan);
04441 if (ast_strlen_zero(cnf->recordingfilename)) {
04442 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
04443 ast_free(cnf->recordingfilename);
04444 cnf->recordingfilename = ast_strdup(recordingtmp);
04445 }
04446 }
04447 if (ast_strlen_zero(cnf->recordingformat)) {
04448 ast_channel_lock(chan);
04449 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04450 ast_free(cnf->recordingformat);
04451 cnf->recordingformat = ast_strdup(var2);
04452 }
04453 ast_channel_unlock(chan);
04454 if (ast_strlen_zero(cnf->recordingformat)) {
04455 ast_free(cnf->recordingformat);
04456 cnf->recordingformat = ast_strdup("wav");
04457 }
04458 }
04459 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04460 }
04461 }
04462 }
04463
04464 if (cnf) {
04465 if (confflags->flags && !cnf->chan &&
04466 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04467 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
04468 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04469 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04470 }
04471
04472 if (confflags && !cnf->chan &&
04473 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04474 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04475 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04476 }
04477 }
04478
04479 return cnf;
04480 }
04481
04482
04483 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04484 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04485 {
04486 struct ast_config *cfg;
04487 struct ast_variable *var;
04488 struct ast_flags config_flags = { 0 };
04489 struct ast_conference *cnf;
04490
04491 AST_DECLARE_APP_ARGS(args,
04492 AST_APP_ARG(confno);
04493 AST_APP_ARG(pin);
04494 AST_APP_ARG(pinadmin);
04495 );
04496
04497
04498 ast_debug(1, "The requested confno is '%s'?\n", confno);
04499 AST_LIST_LOCK(&confs);
04500 AST_LIST_TRAVERSE(&confs, cnf, list) {
04501 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04502 if (!strcmp(confno, cnf->confno))
04503 break;
04504 }
04505 if (cnf) {
04506 cnf->refcount += refcount;
04507 }
04508 AST_LIST_UNLOCK(&confs);
04509
04510 if (!cnf) {
04511 if (dynamic) {
04512
04513 ast_debug(1, "Building dynamic conference '%s'\n", confno);
04514 if (dynamic_pin) {
04515 if (dynamic_pin[0] == 'q') {
04516
04517 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04518 return NULL;
04519 }
04520 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04521 } else {
04522 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04523 }
04524 } else {
04525
04526 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04527 if (!cfg) {
04528 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04529 return NULL;
04530 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04531 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
04532 return NULL;
04533 }
04534
04535 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04536 char parse[MAX_SETTINGS];
04537
04538 if (strcasecmp(var->name, "conf"))
04539 continue;
04540
04541 ast_copy_string(parse, var->value, sizeof(parse));
04542
04543 AST_STANDARD_APP_ARGS(args, parse);
04544 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04545 if (!strcasecmp(args.confno, confno)) {
04546
04547 cnf = build_conf(args.confno,
04548 S_OR(args.pin, ""),
04549 S_OR(args.pinadmin, ""),
04550 make, dynamic, refcount, chan, NULL);
04551 break;
04552 }
04553 }
04554 if (!var) {
04555 ast_debug(1, "%s isn't a valid conference\n", confno);
04556 }
04557 ast_config_destroy(cfg);
04558 }
04559 } else if (dynamic_pin) {
04560
04561
04562
04563 if (dynamic_pin[0] == 'q') {
04564 dynamic_pin[0] = '\0';
04565 }
04566 }
04567
04568 if (cnf) {
04569 if (confflags && !cnf->chan &&
04570 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04571 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
04572 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04573 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04574 }
04575
04576 if (confflags && !cnf->chan &&
04577 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04578 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04579 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04580 }
04581 }
04582
04583 return cnf;
04584 }
04585
04586
04587 static int count_exec(struct ast_channel *chan, const char *data)
04588 {
04589 int res = 0;
04590 struct ast_conference *conf;
04591 int count;
04592 char *localdata;
04593 char val[80] = "0";
04594 AST_DECLARE_APP_ARGS(args,
04595 AST_APP_ARG(confno);
04596 AST_APP_ARG(varname);
04597 );
04598
04599 if (ast_strlen_zero(data)) {
04600 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04601 return -1;
04602 }
04603
04604 localdata = ast_strdupa(data);
04605
04606 AST_STANDARD_APP_ARGS(args, localdata);
04607
04608 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04609
04610 if (conf) {
04611 count = conf->users;
04612 dispose_conf(conf);
04613 conf = NULL;
04614 } else
04615 count = 0;
04616
04617 if (!ast_strlen_zero(args.varname)) {
04618
04619 snprintf(val, sizeof(val), "%d", count);
04620 pbx_builtin_setvar_helper(chan, args.varname, val);
04621 } else {
04622 if (ast_channel_state(chan) != AST_STATE_UP) {
04623 ast_answer(chan);
04624 }
04625 res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL);
04626 }
04627
04628 return res;
04629 }
04630
04631
04632 static int conf_exec(struct ast_channel *chan, const char *data)
04633 {
04634 int res = -1;
04635 char confno[MAX_CONFNUM] = "";
04636 int allowretry = 0;
04637 int retrycnt = 0;
04638 struct ast_conference *cnf = NULL;
04639 struct ast_flags64 confflags = {0};
04640 struct ast_flags config_flags = { 0 };
04641 int dynamic = 0;
04642 int empty = 0, empty_no_pin = 0;
04643 int always_prompt = 0;
04644 const char *notdata;
04645 char *info, the_pin[MAX_PIN] = "";
04646 AST_DECLARE_APP_ARGS(args,
04647 AST_APP_ARG(confno);
04648 AST_APP_ARG(options);
04649 AST_APP_ARG(pin);
04650 );
04651 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04652
04653 if (ast_strlen_zero(data)) {
04654 allowretry = 1;
04655 notdata = "";
04656 } else {
04657 notdata = data;
04658 }
04659
04660 if (ast_channel_state(chan) != AST_STATE_UP)
04661 ast_answer(chan);
04662
04663 info = ast_strdupa(notdata);
04664
04665 AST_STANDARD_APP_ARGS(args, info);
04666
04667 if (args.confno) {
04668 ast_copy_string(confno, args.confno, sizeof(confno));
04669 if (ast_strlen_zero(confno)) {
04670 allowretry = 1;
04671 }
04672 }
04673
04674 if (args.pin)
04675 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04676
04677 if (args.options) {
04678 ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04679 dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04680 if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04681 strcpy(the_pin, "q");
04682
04683 empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04684 empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04685 always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04686 }
04687
04688 do {
04689 if (retrycnt > 3)
04690 allowretry = 0;
04691 if (empty) {
04692 int i;
04693 struct ast_config *cfg;
04694 struct ast_variable *var;
04695 int confno_int;
04696
04697
04698 if ((empty_no_pin) || (!dynamic)) {
04699 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04700 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04701 var = ast_variable_browse(cfg, "rooms");
04702 while (var) {
04703 char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04704 if (!strcasecmp(var->name, "conf")) {
04705 int found = 0;
04706 ast_copy_string(parse, var->value, sizeof(parse));
04707 confno_tmp = strsep(&stringp, "|,");
04708 if (!dynamic) {
04709
04710 AST_LIST_LOCK(&confs);
04711 AST_LIST_TRAVERSE(&confs, cnf, list) {
04712 if (!strcmp(confno_tmp, cnf->confno)) {
04713
04714 found = 1;
04715 break;
04716 }
04717 }
04718 AST_LIST_UNLOCK(&confs);
04719 cnf = NULL;
04720 if (!found) {
04721
04722 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04723
04724
04725
04726
04727 ast_copy_string(confno, confno_tmp, sizeof(confno));
04728 break;
04729 }
04730 }
04731 }
04732 }
04733 var = var->next;
04734 }
04735 ast_config_destroy(cfg);
04736 }
04737
04738 if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04739 const char *catg;
04740 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04741 const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04742 const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04743 if (ast_strlen_zero(confno_tmp)) {
04744 continue;
04745 }
04746 if (!dynamic) {
04747 int found = 0;
04748
04749 AST_LIST_LOCK(&confs);
04750 AST_LIST_TRAVERSE(&confs, cnf, list) {
04751 if (!strcmp(confno_tmp, cnf->confno)) {
04752
04753 found = 1;
04754 break;
04755 }
04756 }
04757 AST_LIST_UNLOCK(&confs);
04758 if (!found) {
04759
04760 if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04761
04762
04763
04764
04765 ast_copy_string(confno, confno_tmp, sizeof(confno));
04766 break;
04767 }
04768 }
04769 }
04770 }
04771 ast_config_destroy(cfg);
04772 }
04773 }
04774
04775
04776 if (ast_strlen_zero(confno) && dynamic) {
04777 AST_LIST_LOCK(&confs);
04778 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04779 if (!conf_map[i]) {
04780 snprintf(confno, sizeof(confno), "%d", i);
04781 conf_map[i] = 1;
04782 break;
04783 }
04784 }
04785 AST_LIST_UNLOCK(&confs);
04786 }
04787
04788
04789 if (ast_strlen_zero(confno)) {
04790 res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
04791 ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04792 if (!res)
04793 ast_waitstream(chan, "");
04794 } else {
04795 if (sscanf(confno, "%30d", &confno_int) == 1) {
04796 if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04797 res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
04798 if (!res) {
04799 ast_waitstream(chan, "");
04800 res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
04801 }
04802 }
04803 } else {
04804 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04805 }
04806 }
04807 }
04808
04809 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04810
04811 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04812 if (res < 0) {
04813
04814 confno[0] = '\0';
04815 allowretry = 0;
04816 break;
04817 }
04818 }
04819 if (!ast_strlen_zero(confno)) {
04820
04821 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
04822 sizeof(the_pin), 1, &confflags);
04823 if (!cnf) {
04824 int too_early = 0;
04825
04826 cnf = find_conf_realtime(chan, confno, 1, dynamic,
04827 the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04828 if (rt_schedule && too_early)
04829 allowretry = 0;
04830 }
04831
04832 if (!cnf) {
04833 if (allowretry) {
04834 confno[0] = '\0';
04835 res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
04836 if (!res)
04837 ast_waitstream(chan, "");
04838 res = -1;
04839 }
04840 } else {
04841
04842 int req_pin = !ast_strlen_zero(cnf->pin) ||
04843 (!ast_strlen_zero(cnf->pinadmin) &&
04844 ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04845
04846
04847
04848
04849
04850
04851
04852
04853
04854
04855
04856
04857
04858 int not_exempt = !cnf->isdynamic;
04859 not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04860 not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04861 if (req_pin && not_exempt) {
04862 char pin[MAX_PIN] = "";
04863 int j;
04864
04865
04866 for (j = 0; j < 3; j++) {
04867 if (*the_pin && (always_prompt == 0)) {
04868 ast_copy_string(pin, the_pin, sizeof(pin));
04869 res = 0;
04870 } else {
04871
04872 ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
04873 "Channel: %s",
04874 ast_channel_name(chan));
04875 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04876 }
04877 if (res >= 0) {
04878 if ((!strcasecmp(pin, cnf->pin) &&
04879 (ast_strlen_zero(cnf->pinadmin) ||
04880 !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04881 (!ast_strlen_zero(cnf->pinadmin) &&
04882 !strcasecmp(pin, cnf->pinadmin))) {
04883
04884 allowretry = 0;
04885 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04886 if (!ast_strlen_zero(cnf->adminopts)) {
04887 char *opts = ast_strdupa(cnf->adminopts);
04888 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04889 }
04890 } else {
04891 if (!ast_strlen_zero(cnf->useropts)) {
04892 char *opts = ast_strdupa(cnf->useropts);
04893 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04894 }
04895 }
04896
04897 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04898 res = conf_run(chan, cnf, &confflags, optargs);
04899 break;
04900 } else {
04901
04902 if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
04903 res = ast_waitstream(chan, AST_DIGIT_ANY);
04904 ast_stopstream(chan);
04905 } else {
04906 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04907 break;
04908 }
04909 if (res < 0)
04910 break;
04911 pin[0] = res;
04912 pin[1] = '\0';
04913 res = -1;
04914 if (allowretry)
04915 confno[0] = '\0';
04916 }
04917 } else {
04918
04919 res = -1;
04920 allowretry = 0;
04921
04922 break;
04923 }
04924
04925
04926 if (*the_pin && (always_prompt == 0)) {
04927 break;
04928 }
04929 }
04930 } else {
04931
04932 allowretry = 0;
04933
04934
04935
04936
04937 if (!ast_strlen_zero(cnf->useropts)) {
04938 char *opts = ast_strdupa(cnf->useropts);
04939 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04940 }
04941
04942
04943 res = conf_run(chan, cnf, &confflags, optargs);
04944 }
04945 dispose_conf(cnf);
04946 cnf = NULL;
04947 }
04948 }
04949 } while (allowretry);
04950
04951 if (cnf)
04952 dispose_conf(cnf);
04953
04954 return res;
04955 }
04956
04957 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04958 {
04959 struct ast_conf_user *user = NULL;
04960 int cid;
04961
04962 if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04963 user = ao2_find(conf->usercontainer, &cid, 0);
04964
04965 return user;
04966 }
04967 return NULL;
04968 }
04969
04970 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04971 {
04972 struct ast_conf_user *user = obj;
04973 tweak_listen_volume(user, VOL_UP);
04974 return 0;
04975 }
04976
04977 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04978 {
04979 struct ast_conf_user *user = obj;
04980 tweak_listen_volume(user, VOL_DOWN);
04981 return 0;
04982 }
04983
04984 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04985 {
04986 struct ast_conf_user *user = obj;
04987 tweak_talk_volume(user, VOL_UP);
04988 return 0;
04989 }
04990
04991 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04992 {
04993 struct ast_conf_user *user = obj;
04994 tweak_talk_volume(user, VOL_DOWN);
04995 return 0;
04996 }
04997
04998 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04999 {
05000 struct ast_conf_user *user = obj;
05001 reset_volumes(user);
05002 return 0;
05003 }
05004
05005 static int user_chan_cb(void *obj, void *args, int flags)
05006 {
05007 struct ast_conf_user *user = obj;
05008 const char *channel = args;
05009
05010 if (!strcmp(ast_channel_name(user->chan), channel)) {
05011 return (CMP_MATCH | CMP_STOP);
05012 }
05013
05014 return 0;
05015 }
05016
05017
05018
05019
05020 static int admin_exec(struct ast_channel *chan, const char *data) {
05021 char *params;
05022 struct ast_conference *cnf;
05023 struct ast_conf_user *user = NULL;
05024 AST_DECLARE_APP_ARGS(args,
05025 AST_APP_ARG(confno);
05026 AST_APP_ARG(command);
05027 AST_APP_ARG(user);
05028 );
05029 int res = 0;
05030
05031 if (ast_strlen_zero(data)) {
05032 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
05033 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05034 return -1;
05035 }
05036
05037 params = ast_strdupa(data);
05038 AST_STANDARD_APP_ARGS(args, params);
05039
05040 if (!args.command) {
05041 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
05042 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05043 return -1;
05044 }
05045
05046 AST_LIST_LOCK(&confs);
05047 AST_LIST_TRAVERSE(&confs, cnf, list) {
05048 if (!strcmp(cnf->confno, args.confno))
05049 break;
05050 }
05051
05052 if (!cnf) {
05053 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
05054 AST_LIST_UNLOCK(&confs);
05055 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
05056 return 0;
05057 }
05058
05059 ast_atomic_fetchadd_int(&cnf->refcount, 1);
05060
05061 if (args.user) {
05062 user = find_user(cnf, args.user);
05063 if (!user) {
05064 ast_log(LOG_NOTICE, "Specified User not found!\n");
05065 res = -2;
05066 goto usernotfound;
05067 }
05068 } else {
05069
05070 switch (*args.command) {
05071 case 'm':
05072 case 'M':
05073 case 't':
05074 case 'T':
05075 case 'u':
05076 case 'U':
05077 case 'r':
05078 case 'k':
05079 res = -2;
05080 ast_log(LOG_NOTICE, "No user specified!\n");
05081 goto usernotfound;
05082 default:
05083 break;
05084 }
05085 }
05086
05087 switch (*args.command) {
05088 case 76:
05089 cnf->locked = 1;
05090 break;
05091 case 108:
05092 cnf->locked = 0;
05093 break;
05094 case 75:
05095 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
05096 break;
05097 case 101:
05098 {
05099 int max_no = 0;
05100 RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
05101
05102 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
05103 eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
05104 if (!eject_user) {
05105 res = -1;
05106 ast_log(LOG_NOTICE, "No last user to kick!\n");
05107 break;
05108 }
05109
05110 if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
05111 eject_user->adminflags |= ADMINFLAG_KICKME;
05112 } else {
05113 res = -1;
05114 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
05115 }
05116 break;
05117 }
05118 case 77:
05119 user->adminflags |= ADMINFLAG_MUTED;
05120 break;
05121 case 78:
05122 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
05123 break;
05124 case 109:
05125 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05126 break;
05127 case 110:
05128 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
05129 break;
05130 case 107:
05131 user->adminflags |= ADMINFLAG_KICKME;
05132 break;
05133 case 118:
05134 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
05135 break;
05136 case 86:
05137 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
05138 break;
05139 case 115:
05140 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
05141 break;
05142 case 83:
05143 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
05144 break;
05145 case 82:
05146 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
05147 break;
05148 case 114:
05149 reset_volumes(user);
05150 break;
05151 case 85:
05152 tweak_listen_volume(user, VOL_UP);
05153 break;
05154 case 117:
05155 tweak_listen_volume(user, VOL_DOWN);
05156 break;
05157 case 84:
05158 tweak_talk_volume(user, VOL_UP);
05159 break;
05160 case 116:
05161 tweak_talk_volume(user, VOL_DOWN);
05162 break;
05163 case 'E':
05164 if (rt_extend_conf(args.confno)) {
05165 res = -1;
05166 }
05167 break;
05168 }
05169
05170 if (args.user) {
05171
05172 ao2_ref(user, -1);
05173 }
05174 usernotfound:
05175 AST_LIST_UNLOCK(&confs);
05176
05177 dispose_conf(cnf);
05178 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
05179
05180 return 0;
05181 }
05182
05183
05184
05185 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
05186 char *params;
05187 struct ast_conference *conf = NULL;
05188 struct ast_conf_user *user = NULL;
05189 AST_DECLARE_APP_ARGS(args,
05190 AST_APP_ARG(channel);
05191 AST_APP_ARG(command);
05192 );
05193
05194 if (ast_strlen_zero(data)) {
05195 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
05196 return -1;
05197 }
05198
05199 params = ast_strdupa(data);
05200 AST_STANDARD_APP_ARGS(args, params);
05201
05202 if (!args.channel) {
05203 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
05204 return -1;
05205 }
05206
05207 if (!args.command) {
05208 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
05209 return -1;
05210 }
05211
05212 AST_LIST_LOCK(&confs);
05213 AST_LIST_TRAVERSE(&confs, conf, list) {
05214 if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05215 break;
05216 }
05217 }
05218
05219 if (!user) {
05220 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05221 AST_LIST_UNLOCK(&confs);
05222 return 0;
05223 }
05224
05225
05226 switch (*args.command) {
05227 case 77:
05228 user->adminflags |= ADMINFLAG_MUTED;
05229 break;
05230 case 109:
05231 user->adminflags &= ~ADMINFLAG_MUTED;
05232 break;
05233 case 107:
05234 user->adminflags |= ADMINFLAG_KICKME;
05235 break;
05236 default:
05237 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05238 break;
05239 }
05240 ao2_ref(user, -1);
05241 AST_LIST_UNLOCK(&confs);
05242
05243 return 0;
05244 }
05245
05246 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05247 {
05248 struct ast_conference *conf;
05249 struct ast_conf_user *user;
05250 const char *confid = astman_get_header(m, "Meetme");
05251 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05252 int userno;
05253
05254 if (ast_strlen_zero(confid)) {
05255 astman_send_error(s, m, "Meetme conference not specified");
05256 return 0;
05257 }
05258
05259 if (ast_strlen_zero(userid)) {
05260 astman_send_error(s, m, "Meetme user number not specified");
05261 return 0;
05262 }
05263
05264 userno = strtoul(userid, &userid, 10);
05265
05266 if (*userid) {
05267 astman_send_error(s, m, "Invalid user number");
05268 return 0;
05269 }
05270
05271
05272 AST_LIST_LOCK(&confs);
05273 AST_LIST_TRAVERSE(&confs, conf, list) {
05274 if (!strcmp(confid, conf->confno))
05275 break;
05276 }
05277
05278 if (!conf) {
05279 AST_LIST_UNLOCK(&confs);
05280 astman_send_error(s, m, "Meetme conference does not exist");
05281 return 0;
05282 }
05283
05284 user = ao2_find(conf->usercontainer, &userno, 0);
05285
05286 if (!user) {
05287 AST_LIST_UNLOCK(&confs);
05288 astman_send_error(s, m, "User number not found");
05289 return 0;
05290 }
05291
05292 if (mute)
05293 user->adminflags |= ADMINFLAG_MUTED;
05294 else
05295 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05296
05297 AST_LIST_UNLOCK(&confs);
05298
05299 ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, ast_channel_name(user->chan), ast_channel_uniqueid(user->chan));
05300
05301 ao2_ref(user, -1);
05302 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05303 return 0;
05304 }
05305
05306 static int action_meetmemute(struct mansession *s, const struct message *m)
05307 {
05308 return meetmemute(s, m, 1);
05309 }
05310
05311 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05312 {
05313 return meetmemute(s, m, 0);
05314 }
05315
05316 static int action_meetmelist(struct mansession *s, const struct message *m)
05317 {
05318 const char *actionid = astman_get_header(m, "ActionID");
05319 const char *conference = astman_get_header(m, "Conference");
05320 char idText[80] = "";
05321 struct ast_conference *cnf;
05322 struct ast_conf_user *user;
05323 struct ao2_iterator user_iter;
05324 int total = 0;
05325
05326 if (!ast_strlen_zero(actionid))
05327 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05328
05329 if (AST_LIST_EMPTY(&confs)) {
05330 astman_send_error(s, m, "No active conferences.");
05331 return 0;
05332 }
05333
05334 astman_send_listack(s, m, "Meetme user list will follow", "start");
05335
05336
05337 AST_LIST_LOCK(&confs);
05338 AST_LIST_TRAVERSE(&confs, cnf, list) {
05339
05340 if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05341 continue;
05342
05343
05344 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05345 while ((user = ao2_iterator_next(&user_iter))) {
05346 total++;
05347 astman_append(s,
05348 "Event: MeetmeList\r\n"
05349 "%s"
05350 "Conference: %s\r\n"
05351 "UserNumber: %d\r\n"
05352 "CallerIDNum: %s\r\n"
05353 "CallerIDName: %s\r\n"
05354 "ConnectedLineNum: %s\r\n"
05355 "ConnectedLineName: %s\r\n"
05356 "Channel: %s\r\n"
05357 "Admin: %s\r\n"
05358 "Role: %s\r\n"
05359 "MarkedUser: %s\r\n"
05360 "Muted: %s\r\n"
05361 "Talking: %s\r\n"
05362 "\r\n",
05363 idText,
05364 cnf->confno,
05365 user->user_no,
05366 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
05367 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
05368 S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
05369 S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
05370 ast_channel_name(user->chan),
05371 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05372 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05373 ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05374 user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05375 user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05376 ao2_ref(user, -1);
05377 }
05378 ao2_iterator_destroy(&user_iter);
05379 }
05380 AST_LIST_UNLOCK(&confs);
05381
05382 astman_append(s,
05383 "Event: MeetmeListComplete\r\n"
05384 "EventList: Complete\r\n"
05385 "ListItems: %d\r\n"
05386 "%s"
05387 "\r\n", total, idText);
05388 return 0;
05389 }
05390
05391 static int action_meetmelistrooms(struct mansession *s, const struct message *m)
05392 {
05393 const char *actionid = astman_get_header(m, "ActionID");
05394 char idText[80] = "";
05395 struct ast_conference *cnf;
05396 int totalitems = 0;
05397 int hr, min, sec;
05398 time_t now;
05399 char markedusers[5];
05400
05401 if (!ast_strlen_zero(actionid)) {
05402 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05403 }
05404
05405 if (AST_LIST_EMPTY(&confs)) {
05406 astman_send_error(s, m, "No active conferences.");
05407 return 0;
05408 }
05409
05410 astman_send_listack(s, m, "Meetme conferences will follow", "start");
05411
05412 now = time(NULL);
05413
05414
05415 AST_LIST_LOCK(&confs);
05416 AST_LIST_TRAVERSE(&confs, cnf, list) {
05417 totalitems++;
05418
05419 if (cnf->markedusers == 0) {
05420 strcpy(markedusers, "N/A");
05421 } else {
05422 sprintf(markedusers, "%.4d", cnf->markedusers);
05423 }
05424 hr = (now - cnf->start) / 3600;
05425 min = ((now - cnf->start) % 3600) / 60;
05426 sec = (now - cnf->start) % 60;
05427
05428 astman_append(s,
05429 "Event: MeetmeListRooms\r\n"
05430 "%s"
05431 "Conference: %s\r\n"
05432 "Parties: %d\r\n"
05433 "Marked: %s\r\n"
05434 "Activity: %2.2d:%2.2d:%2.2d\r\n"
05435 "Creation: %s\r\n"
05436 "Locked: %s\r\n"
05437 "\r\n",
05438 idText,
05439 cnf->confno,
05440 cnf->users,
05441 markedusers,
05442 hr, min, sec,
05443 cnf->isdynamic ? "Dynamic" : "Static",
05444 cnf->locked ? "Yes" : "No");
05445 }
05446 AST_LIST_UNLOCK(&confs);
05447
05448
05449 astman_append(s,
05450 "Event: MeetmeListRoomsComplete\r\n"
05451 "EventList: Complete\r\n"
05452 "ListItems: %d\r\n"
05453 "%s"
05454 "\r\n", totalitems, idText);
05455 return 0;
05456 }
05457
05458
05459
05460
05461
05462
05463
05464 static void filename_parse(char *filename, char *buffer)
05465 {
05466 char *slash;
05467 if (ast_strlen_zero(filename)) {
05468 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05469 } else if (filename[0] != '/') {
05470 snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05471 } else {
05472 ast_copy_string(buffer, filename, PATH_MAX);
05473 }
05474
05475 slash = buffer;
05476 if ((slash = strrchr(slash, '/'))) {
05477 *slash = '\0';
05478 ast_mkdir(buffer, 0777);
05479 *slash = '/';
05480 }
05481 }
05482
05483 static void *recordthread(void *args)
05484 {
05485 struct ast_conference *cnf = args;
05486 struct ast_frame *f = NULL;
05487 int flags;
05488 struct ast_filestream *s = NULL;
05489 int res = 0;
05490 int x;
05491 const char *oldrecordingfilename = NULL;
05492 char filename_buffer[PATH_MAX];
05493
05494 if (!cnf || !cnf->lchan) {
05495 pthread_exit(0);
05496 }
05497
05498 filename_buffer[0] = '\0';
05499 filename_parse(cnf->recordingfilename, filename_buffer);
05500
05501 ast_stopstream(cnf->lchan);
05502 flags = O_CREAT | O_TRUNC | O_WRONLY;
05503
05504
05505 cnf->recording = MEETME_RECORD_ACTIVE;
05506 while (ast_waitfor(cnf->lchan, -1) > -1) {
05507 if (cnf->recording == MEETME_RECORD_TERMINATE) {
05508 AST_LIST_LOCK(&confs);
05509 AST_LIST_UNLOCK(&confs);
05510 break;
05511 }
05512 if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05513 s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05514 oldrecordingfilename = filename_buffer;
05515 }
05516
05517 f = ast_read(cnf->lchan);
05518 if (!f) {
05519 res = -1;
05520 break;
05521 }
05522 if (f->frametype == AST_FRAME_VOICE) {
05523 ast_mutex_lock(&cnf->listenlock);
05524 for (x = 0; x < AST_FRAME_BITS; x++) {
05525
05526 if (cnf->transframe[x]) {
05527 ast_frfree(cnf->transframe[x]);
05528 cnf->transframe[x] = NULL;
05529 }
05530 }
05531 if (cnf->origframe)
05532 ast_frfree(cnf->origframe);
05533 cnf->origframe = ast_frdup(f);
05534 ast_mutex_unlock(&cnf->listenlock);
05535 if (s)
05536 res = ast_writestream(s, f);
05537 if (res) {
05538 ast_frfree(f);
05539 break;
05540 }
05541 }
05542 ast_frfree(f);
05543 }
05544 cnf->recording = MEETME_RECORD_OFF;
05545 if (s)
05546 ast_closestream(s);
05547
05548 pthread_exit(0);
05549 }
05550
05551
05552 static enum ast_device_state meetmestate(const char *data)
05553 {
05554 struct ast_conference *conf;
05555
05556
05557 AST_LIST_LOCK(&confs);
05558 AST_LIST_TRAVERSE(&confs, conf, list) {
05559 if (!strcmp(data, conf->confno))
05560 break;
05561 }
05562 AST_LIST_UNLOCK(&confs);
05563 if (!conf)
05564 return AST_DEVICE_INVALID;
05565
05566
05567
05568 if (!conf->users)
05569 return AST_DEVICE_NOT_INUSE;
05570
05571 return AST_DEVICE_INUSE;
05572 }
05573
05574 static void load_config_meetme(void)
05575 {
05576 struct ast_config *cfg;
05577 struct ast_flags config_flags = { 0 };
05578 const char *val;
05579
05580 if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05581 return;
05582 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05583 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
05584 return;
05585 }
05586
05587 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05588
05589
05590 rt_schedule = 0;
05591 fuzzystart = 0;
05592 earlyalert = 0;
05593 endalert = 0;
05594 extendby = 0;
05595
05596
05597 rt_log_members = 1;
05598
05599 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05600 if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05601 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05602 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05603 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05604 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05605 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05606 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05607 }
05608 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05609 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05610 }
05611
05612 if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05613 rt_schedule = ast_true(val);
05614 if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05615 rt_log_members = ast_true(val);
05616 if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05617 if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05618 ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05619 fuzzystart = 0;
05620 }
05621 }
05622 if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05623 if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05624 ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05625 earlyalert = 0;
05626 }
05627 }
05628 if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05629 if ((sscanf(val, "%30d", &endalert) != 1)) {
05630 ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05631 endalert = 0;
05632 }
05633 }
05634 if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05635 if ((sscanf(val, "%30d", &extendby) != 1)) {
05636 ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05637 extendby = 0;
05638 }
05639 }
05640
05641 ast_config_destroy(cfg);
05642 }
05643
05644
05645
05646
05647
05648 static struct sla_trunk *sla_find_trunk(const char *name)
05649 {
05650 struct sla_trunk tmp_trunk = {
05651 .name = name,
05652 };
05653
05654 return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05655 }
05656
05657
05658
05659
05660
05661 static struct sla_station *sla_find_station(const char *name)
05662 {
05663 struct sla_station tmp_station = {
05664 .name = name,
05665 };
05666
05667 return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05668 }
05669
05670 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05671 const struct sla_station *station)
05672 {
05673 struct sla_station_ref *station_ref;
05674 struct sla_trunk_ref *trunk_ref;
05675
05676
05677 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05678 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05679 if (trunk_ref->trunk != trunk || station_ref->station == station)
05680 continue;
05681 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05682 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05683 return 1;
05684 return 0;
05685 }
05686 }
05687
05688 return 0;
05689 }
05690
05691
05692
05693
05694
05695
05696
05697
05698
05699
05700 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05701 const char *name)
05702 {
05703 struct sla_trunk_ref *trunk_ref = NULL;
05704
05705 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05706 if (strcasecmp(trunk_ref->trunk->name, name))
05707 continue;
05708
05709 if ( (trunk_ref->trunk->barge_disabled
05710 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05711 (trunk_ref->trunk->hold_stations
05712 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05713 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05714 sla_check_station_hold_access(trunk_ref->trunk, station) )
05715 {
05716 trunk_ref = NULL;
05717 }
05718
05719 break;
05720 }
05721
05722 if (trunk_ref) {
05723 ao2_ref(trunk_ref, 1);
05724 }
05725
05726 return trunk_ref;
05727 }
05728
05729 static void sla_station_ref_destructor(void *obj)
05730 {
05731 struct sla_station_ref *station_ref = obj;
05732
05733 if (station_ref->station) {
05734 ao2_ref(station_ref->station, -1);
05735 station_ref->station = NULL;
05736 }
05737 }
05738
05739 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05740 {
05741 struct sla_station_ref *station_ref;
05742
05743 if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05744 return NULL;
05745 }
05746
05747 ao2_ref(station, 1);
05748 station_ref->station = station;
05749
05750 return station_ref;
05751 }
05752
05753 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05754 {
05755 struct sla_ringing_station *ringing_station;
05756
05757 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05758 return NULL;
05759
05760 ao2_ref(station, 1);
05761 ringing_station->station = station;
05762 ringing_station->ring_begin = ast_tvnow();
05763
05764 return ringing_station;
05765 }
05766
05767 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05768 {
05769 if (ringing_station->station) {
05770 ao2_ref(ringing_station->station, -1);
05771 ringing_station->station = NULL;
05772 }
05773
05774 ast_free(ringing_station);
05775 }
05776
05777 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05778 {
05779 struct sla_failed_station *failed_station;
05780
05781 if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05782 return NULL;
05783 }
05784
05785 ao2_ref(station, 1);
05786 failed_station->station = station;
05787 failed_station->last_try = ast_tvnow();
05788
05789 return failed_station;
05790 }
05791
05792 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05793 {
05794 if (failed_station->station) {
05795 ao2_ref(failed_station->station, -1);
05796 failed_station->station = NULL;
05797 }
05798
05799 ast_free(failed_station);
05800 }
05801
05802 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05803 {
05804 switch (state) {
05805 case SLA_TRUNK_STATE_IDLE:
05806 return AST_DEVICE_NOT_INUSE;
05807 case SLA_TRUNK_STATE_RINGING:
05808 return AST_DEVICE_RINGING;
05809 case SLA_TRUNK_STATE_UP:
05810 return AST_DEVICE_INUSE;
05811 case SLA_TRUNK_STATE_ONHOLD:
05812 case SLA_TRUNK_STATE_ONHOLD_BYME:
05813 return AST_DEVICE_ONHOLD;
05814 }
05815
05816 return AST_DEVICE_UNKNOWN;
05817 }
05818
05819 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
05820 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05821 {
05822 struct sla_station *station;
05823 struct sla_trunk_ref *trunk_ref;
05824 struct ao2_iterator i;
05825
05826 i = ao2_iterator_init(sla_stations, 0);
05827 while ((station = ao2_iterator_next(&i))) {
05828 ao2_lock(station);
05829 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05830 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05831 || trunk_ref == exclude) {
05832 continue;
05833 }
05834 trunk_ref->state = state;
05835 ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
05836 "SLA:%s_%s", station->name, trunk->name);
05837 break;
05838 }
05839 ao2_unlock(station);
05840 ao2_ref(station, -1);
05841 }
05842 ao2_iterator_destroy(&i);
05843 }
05844
05845 struct run_station_args {
05846 struct sla_station *station;
05847 struct sla_trunk_ref *trunk_ref;
05848 ast_mutex_t *cond_lock;
05849 ast_cond_t *cond;
05850 };
05851
05852 static void answer_trunk_chan(struct ast_channel *chan)
05853 {
05854 ast_answer(chan);
05855 ast_indicate(chan, -1);
05856 }
05857
05858 static void *run_station(void *data)
05859 {
05860 RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
05861 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
05862 struct ast_str *conf_name = ast_str_create(16);
05863 struct ast_flags64 conf_flags = { 0 };
05864 struct ast_conference *conf;
05865
05866 {
05867 struct run_station_args *args = data;
05868 station = args->station;
05869 trunk_ref = args->trunk_ref;
05870 ast_mutex_lock(args->cond_lock);
05871 ast_cond_signal(args->cond);
05872 ast_mutex_unlock(args->cond_lock);
05873
05874 }
05875
05876 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05877 ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05878 ast_set_flag64(&conf_flags,
05879 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05880 answer_trunk_chan(trunk_ref->chan);
05881 conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05882 if (conf) {
05883 conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05884 dispose_conf(conf);
05885 conf = NULL;
05886 }
05887 trunk_ref->chan = NULL;
05888 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05889 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05890 ast_str_append(&conf_name, 0, ",K");
05891 admin_exec(NULL, ast_str_buffer(conf_name));
05892 trunk_ref->trunk->hold_stations = 0;
05893 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05894 }
05895
05896 ast_dial_join(station->dial);
05897 ast_dial_destroy(station->dial);
05898 station->dial = NULL;
05899 ast_free(conf_name);
05900
05901 return NULL;
05902 }
05903
05904 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
05905
05906 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05907 {
05908 char buf[80];
05909 struct sla_station_ref *station_ref;
05910
05911 snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05912 admin_exec(NULL, buf);
05913 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05914
05915 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
05916 ao2_ref(station_ref, -1);
05917 }
05918
05919 sla_ringing_trunk_destroy(ringing_trunk);
05920 }
05921
05922 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05923 enum sla_station_hangup hangup)
05924 {
05925 struct sla_ringing_trunk *ringing_trunk;
05926 struct sla_trunk_ref *trunk_ref;
05927 struct sla_station_ref *station_ref;
05928
05929 ast_dial_join(ringing_station->station->dial);
05930 ast_dial_destroy(ringing_station->station->dial);
05931 ringing_station->station->dial = NULL;
05932
05933 if (hangup == SLA_STATION_HANGUP_NORMAL)
05934 goto done;
05935
05936
05937
05938
05939
05940
05941 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05942 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05943 if (ringing_trunk->trunk == trunk_ref->trunk)
05944 break;
05945 }
05946 if (!trunk_ref)
05947 continue;
05948 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05949 continue;
05950 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05951 }
05952
05953 done:
05954 sla_ringing_station_destroy(ringing_station);
05955 }
05956
05957 static void sla_dial_state_callback(struct ast_dial *dial)
05958 {
05959 sla_queue_event(SLA_EVENT_DIAL_STATE);
05960 }
05961
05962
05963
05964
05965 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05966 const struct sla_station *station)
05967 {
05968 struct sla_station_ref *timed_out_station;
05969
05970 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05971 if (station == timed_out_station->station)
05972 return 1;
05973 }
05974
05975 return 0;
05976 }
05977
05978
05979
05980
05981
05982
05983
05984
05985
05986 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
05987 struct sla_trunk_ref **trunk_ref, int rm)
05988 {
05989 struct sla_trunk_ref *s_trunk_ref;
05990 struct sla_ringing_trunk *ringing_trunk = NULL;
05991
05992 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05993 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05994
05995 if (s_trunk_ref->trunk != ringing_trunk->trunk)
05996 continue;
05997
05998
05999
06000 if (sla_check_timed_out_station(ringing_trunk, station))
06001 continue;
06002
06003 if (rm)
06004 AST_LIST_REMOVE_CURRENT(entry);
06005
06006 if (trunk_ref) {
06007 ao2_ref(s_trunk_ref, 1);
06008 *trunk_ref = s_trunk_ref;
06009 }
06010
06011 break;
06012 }
06013 AST_LIST_TRAVERSE_SAFE_END;
06014
06015 if (ringing_trunk)
06016 break;
06017 }
06018
06019 return ringing_trunk;
06020 }
06021
06022 static void sla_handle_dial_state_event(void)
06023 {
06024 struct sla_ringing_station *ringing_station;
06025
06026 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06027 RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
06028 struct sla_ringing_trunk *ringing_trunk = NULL;
06029 struct run_station_args args;
06030 enum ast_dial_result dial_res;
06031 pthread_t dont_care;
06032 ast_mutex_t cond_lock;
06033 ast_cond_t cond;
06034
06035 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
06036 case AST_DIAL_RESULT_HANGUP:
06037 case AST_DIAL_RESULT_INVALID:
06038 case AST_DIAL_RESULT_FAILED:
06039 case AST_DIAL_RESULT_TIMEOUT:
06040 case AST_DIAL_RESULT_UNANSWERED:
06041 AST_LIST_REMOVE_CURRENT(entry);
06042 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
06043 break;
06044 case AST_DIAL_RESULT_ANSWERED:
06045 AST_LIST_REMOVE_CURRENT(entry);
06046
06047 ast_mutex_lock(&sla.lock);
06048 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
06049 ast_mutex_unlock(&sla.lock);
06050 if (!ringing_trunk) {
06051
06052
06053
06054
06055
06056 ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
06057 ast_dial_join(ringing_station->station->dial);
06058 ast_dial_destroy(ringing_station->station->dial);
06059 ringing_station->station->dial = NULL;
06060 sla_ringing_station_destroy(ringing_station);
06061 break;
06062 }
06063
06064 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
06065
06066 answer_trunk_chan(ringing_trunk->trunk->chan);
06067 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06068
06069
06070
06071 ao2_ref(s_trunk_ref, 1);
06072 args.trunk_ref = s_trunk_ref;
06073 ao2_ref(ringing_station->station, 1);
06074 args.station = ringing_station->station;
06075 args.cond = &cond;
06076 args.cond_lock = &cond_lock;
06077 sla_ringing_trunk_destroy(ringing_trunk);
06078 sla_ringing_station_destroy(ringing_station);
06079 ast_mutex_init(&cond_lock);
06080 ast_cond_init(&cond, NULL);
06081 ast_mutex_lock(&cond_lock);
06082 ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
06083 ast_cond_wait(&cond, &cond_lock);
06084 ast_mutex_unlock(&cond_lock);
06085 ast_mutex_destroy(&cond_lock);
06086 ast_cond_destroy(&cond);
06087 break;
06088 case AST_DIAL_RESULT_TRYING:
06089 case AST_DIAL_RESULT_RINGING:
06090 case AST_DIAL_RESULT_PROGRESS:
06091 case AST_DIAL_RESULT_PROCEEDING:
06092 break;
06093 }
06094 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
06095
06096 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06097 sla_queue_event(SLA_EVENT_DIAL_STATE);
06098 break;
06099 }
06100 }
06101 AST_LIST_TRAVERSE_SAFE_END;
06102 }
06103
06104
06105
06106
06107 static int sla_check_ringing_station(const struct sla_station *station)
06108 {
06109 struct sla_ringing_station *ringing_station;
06110
06111 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
06112 if (station == ringing_station->station)
06113 return 1;
06114 }
06115
06116 return 0;
06117 }
06118
06119
06120
06121
06122 static int sla_check_failed_station(const struct sla_station *station)
06123 {
06124 struct sla_failed_station *failed_station;
06125 int res = 0;
06126
06127 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
06128 if (station != failed_station->station)
06129 continue;
06130 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
06131 AST_LIST_REMOVE_CURRENT(entry);
06132 sla_failed_station_destroy(failed_station);
06133 break;
06134 }
06135 res = 1;
06136 }
06137 AST_LIST_TRAVERSE_SAFE_END
06138
06139 return res;
06140 }
06141
06142
06143
06144
06145 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
06146 {
06147 char *tech, *tech_data;
06148 struct ast_dial *dial;
06149 struct sla_ringing_station *ringing_station;
06150 enum ast_dial_result res;
06151 int caller_is_saved;
06152 struct ast_party_caller caller;
06153
06154 if (!(dial = ast_dial_create()))
06155 return -1;
06156
06157 ast_dial_set_state_callback(dial, sla_dial_state_callback);
06158 tech_data = ast_strdupa(station->device);
06159 tech = strsep(&tech_data, "/");
06160
06161 if (ast_dial_append(dial, tech, tech_data) == -1) {
06162 ast_dial_destroy(dial);
06163 return -1;
06164 }
06165
06166
06167 caller_is_saved = 0;
06168 if (!sla.attempt_callerid) {
06169 caller_is_saved = 1;
06170 caller = *ast_channel_caller(ringing_trunk->trunk->chan);
06171 ast_party_caller_init(ast_channel_caller(ringing_trunk->trunk->chan));
06172 }
06173
06174 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
06175
06176
06177 if (caller_is_saved) {
06178 ast_party_caller_free(ast_channel_caller(ringing_trunk->trunk->chan));
06179 ast_channel_caller_set(ringing_trunk->trunk->chan, &caller);
06180 }
06181
06182 if (res != AST_DIAL_RESULT_TRYING) {
06183 struct sla_failed_station *failed_station;
06184 ast_dial_destroy(dial);
06185 if ((failed_station = sla_create_failed_station(station))) {
06186 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
06187 }
06188 return -1;
06189 }
06190 if (!(ringing_station = sla_create_ringing_station(station))) {
06191 ast_dial_join(dial);
06192 ast_dial_destroy(dial);
06193 return -1;
06194 }
06195
06196 station->dial = dial;
06197
06198 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
06199
06200 return 0;
06201 }
06202
06203
06204
06205 static int sla_check_inuse_station(const struct sla_station *station)
06206 {
06207 struct sla_trunk_ref *trunk_ref;
06208
06209 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06210 if (trunk_ref->chan)
06211 return 1;
06212 }
06213
06214 return 0;
06215 }
06216
06217 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
06218 const struct sla_trunk *trunk)
06219 {
06220 struct sla_trunk_ref *trunk_ref = NULL;
06221
06222 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06223 if (trunk_ref->trunk == trunk)
06224 break;
06225 }
06226
06227 ao2_ref(trunk_ref, 1);
06228
06229 return trunk_ref;
06230 }
06231
06232
06233
06234
06235
06236
06237 static int sla_check_station_delay(struct sla_station *station,
06238 struct sla_ringing_trunk *ringing_trunk)
06239 {
06240 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06241 unsigned int delay = UINT_MAX;
06242 int time_left, time_elapsed;
06243
06244 if (!ringing_trunk)
06245 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
06246 else
06247 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
06248
06249 if (!ringing_trunk || !trunk_ref)
06250 return delay;
06251
06252
06253
06254
06255 delay = trunk_ref->ring_delay;
06256 if (!delay)
06257 delay = station->ring_delay;
06258 if (!delay)
06259 return INT_MAX;
06260
06261 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06262 time_left = (delay * 1000) - time_elapsed;
06263
06264 return time_left;
06265 }
06266
06267
06268
06269
06270 static void sla_ring_stations(void)
06271 {
06272 struct sla_station_ref *station_ref;
06273 struct sla_ringing_trunk *ringing_trunk;
06274
06275
06276
06277 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06278 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
06279 int time_left;
06280
06281
06282 if (sla_check_ringing_station(station_ref->station))
06283 continue;
06284
06285
06286 if (sla_check_inuse_station(station_ref->station))
06287 continue;
06288
06289
06290
06291 if (sla_check_failed_station(station_ref->station))
06292 continue;
06293
06294
06295
06296 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
06297 continue;
06298
06299
06300 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
06301 if (time_left != INT_MAX && time_left > 0)
06302 continue;
06303
06304
06305 sla_ring_station(ringing_trunk, station_ref->station);
06306 }
06307 }
06308
06309 }
06310
06311 static void sla_hangup_stations(void)
06312 {
06313 struct sla_trunk_ref *trunk_ref;
06314 struct sla_ringing_station *ringing_station;
06315
06316 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06317 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06318 struct sla_ringing_trunk *ringing_trunk;
06319 ast_mutex_lock(&sla.lock);
06320 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06321 if (trunk_ref->trunk == ringing_trunk->trunk)
06322 break;
06323 }
06324 ast_mutex_unlock(&sla.lock);
06325 if (ringing_trunk)
06326 break;
06327 }
06328 if (!trunk_ref) {
06329 AST_LIST_REMOVE_CURRENT(entry);
06330 ast_dial_join(ringing_station->station->dial);
06331 ast_dial_destroy(ringing_station->station->dial);
06332 ringing_station->station->dial = NULL;
06333 sla_ringing_station_destroy(ringing_station);
06334 }
06335 }
06336 AST_LIST_TRAVERSE_SAFE_END
06337 }
06338
06339 static void sla_handle_ringing_trunk_event(void)
06340 {
06341 ast_mutex_lock(&sla.lock);
06342 sla_ring_stations();
06343 ast_mutex_unlock(&sla.lock);
06344
06345
06346 sla_hangup_stations();
06347 }
06348
06349 static void sla_handle_hold_event(struct sla_event *event)
06350 {
06351 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
06352 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
06353 ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
06354 event->station->name, event->trunk_ref->trunk->name);
06355 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
06356 INACTIVE_TRUNK_REFS, event->trunk_ref);
06357
06358 if (event->trunk_ref->trunk->active_stations == 1) {
06359
06360
06361 event->trunk_ref->trunk->on_hold = 1;
06362 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
06363 }
06364
06365 ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
06366 event->trunk_ref->chan = NULL;
06367 }
06368
06369
06370
06371
06372
06373 static int sla_calc_trunk_timeouts(unsigned int *timeout)
06374 {
06375 struct sla_ringing_trunk *ringing_trunk;
06376 int res = 0;
06377
06378 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06379 int time_left, time_elapsed;
06380 if (!ringing_trunk->trunk->ring_timeout)
06381 continue;
06382 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06383 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
06384 if (time_left <= 0) {
06385 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
06386 AST_LIST_REMOVE_CURRENT(entry);
06387 sla_stop_ringing_trunk(ringing_trunk);
06388 res = 1;
06389 continue;
06390 }
06391 if (time_left < *timeout)
06392 *timeout = time_left;
06393 }
06394 AST_LIST_TRAVERSE_SAFE_END;
06395
06396 return res;
06397 }
06398
06399
06400
06401
06402
06403 static int sla_calc_station_timeouts(unsigned int *timeout)
06404 {
06405 struct sla_ringing_trunk *ringing_trunk;
06406 struct sla_ringing_station *ringing_station;
06407 int res = 0;
06408
06409 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06410 unsigned int ring_timeout = 0;
06411 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
06412 struct sla_trunk_ref *trunk_ref;
06413
06414
06415
06416
06417 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06418 struct sla_station_ref *station_ref;
06419 int trunk_time_elapsed, trunk_time_left;
06420
06421 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06422 if (ringing_trunk->trunk == trunk_ref->trunk)
06423 break;
06424 }
06425 if (!ringing_trunk)
06426 continue;
06427
06428
06429
06430 if (!trunk_ref->ring_timeout)
06431 break;
06432
06433
06434
06435
06436 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
06437 if (station_ref->station == ringing_station->station)
06438 break;
06439 }
06440 if (station_ref)
06441 continue;
06442
06443 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06444 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
06445 if (trunk_time_left > final_trunk_time_left)
06446 final_trunk_time_left = trunk_time_left;
06447 }
06448
06449
06450 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
06451 continue;
06452
06453
06454 if (ringing_station->station->ring_timeout) {
06455 ring_timeout = ringing_station->station->ring_timeout;
06456 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
06457 time_left = (ring_timeout * 1000) - time_elapsed;
06458 }
06459
06460
06461
06462 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
06463 time_left = final_trunk_time_left;
06464
06465
06466 if (time_left <= 0) {
06467 AST_LIST_REMOVE_CURRENT(entry);
06468 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
06469 res = 1;
06470 continue;
06471 }
06472
06473
06474
06475 if (time_left < *timeout)
06476 *timeout = time_left;
06477 }
06478 AST_LIST_TRAVERSE_SAFE_END;
06479
06480 return res;
06481 }
06482
06483
06484
06485
06486 static int sla_calc_station_delays(unsigned int *timeout)
06487 {
06488 struct sla_station *station;
06489 int res = 0;
06490 struct ao2_iterator i;
06491
06492 i = ao2_iterator_init(sla_stations, 0);
06493 for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
06494 struct sla_ringing_trunk *ringing_trunk;
06495 int time_left;
06496
06497
06498 if (sla_check_ringing_station(station))
06499 continue;
06500
06501
06502 if (sla_check_inuse_station(station))
06503 continue;
06504
06505
06506 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
06507 continue;
06508
06509 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
06510 continue;
06511
06512
06513
06514
06515 if (time_left <= 0) {
06516 res = 1;
06517 continue;
06518 }
06519
06520 if (time_left < *timeout)
06521 *timeout = time_left;
06522 }
06523 ao2_iterator_destroy(&i);
06524
06525 return res;
06526 }
06527
06528
06529
06530 static int sla_process_timers(struct timespec *ts)
06531 {
06532 unsigned int timeout = UINT_MAX;
06533 struct timeval wait;
06534 unsigned int change_made = 0;
06535
06536
06537 if (sla_calc_trunk_timeouts(&timeout))
06538 change_made = 1;
06539
06540
06541 if (sla_calc_station_timeouts(&timeout))
06542 change_made = 1;
06543
06544
06545 if (sla_calc_station_delays(&timeout))
06546 change_made = 1;
06547
06548
06549 if (change_made)
06550 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
06551
06552
06553 if (timeout == UINT_MAX)
06554 return 0;
06555
06556 if (ts) {
06557 wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
06558 ts->tv_sec = wait.tv_sec;
06559 ts->tv_nsec = wait.tv_usec * 1000;
06560 }
06561
06562 return 1;
06563 }
06564
06565 static void sla_event_destroy(struct sla_event *event)
06566 {
06567 if (event->trunk_ref) {
06568 ao2_ref(event->trunk_ref, -1);
06569 event->trunk_ref = NULL;
06570 }
06571
06572 if (event->station) {
06573 ao2_ref(event->station, -1);
06574 event->station = NULL;
06575 }
06576
06577 ast_free(event);
06578 }
06579
06580 static void *sla_thread(void *data)
06581 {
06582 struct sla_failed_station *failed_station;
06583 struct sla_ringing_station *ringing_station;
06584
06585 ast_mutex_lock(&sla.lock);
06586
06587 while (!sla.stop) {
06588 struct sla_event *event;
06589 struct timespec ts = { 0, };
06590 unsigned int have_timeout = 0;
06591
06592 if (AST_LIST_EMPTY(&sla.event_q)) {
06593 if ((have_timeout = sla_process_timers(&ts)))
06594 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
06595 else
06596 ast_cond_wait(&sla.cond, &sla.lock);
06597 if (sla.stop)
06598 break;
06599 }
06600
06601 if (have_timeout)
06602 sla_process_timers(NULL);
06603
06604 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
06605 ast_mutex_unlock(&sla.lock);
06606 switch (event->type) {
06607 case SLA_EVENT_HOLD:
06608 sla_handle_hold_event(event);
06609 break;
06610 case SLA_EVENT_DIAL_STATE:
06611 sla_handle_dial_state_event();
06612 break;
06613 case SLA_EVENT_RINGING_TRUNK:
06614 sla_handle_ringing_trunk_event();
06615 break;
06616 }
06617 sla_event_destroy(event);
06618 ast_mutex_lock(&sla.lock);
06619 }
06620 }
06621
06622 ast_mutex_unlock(&sla.lock);
06623
06624 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
06625 sla_ringing_station_destroy(ringing_station);
06626 }
06627
06628 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
06629 sla_failed_station_destroy(failed_station);
06630 }
06631
06632 return NULL;
06633 }
06634
06635 struct dial_trunk_args {
06636 struct sla_trunk_ref *trunk_ref;
06637 struct sla_station *station;
06638 ast_mutex_t *cond_lock;
06639 ast_cond_t *cond;
06640 };
06641
06642 static void *dial_trunk(void *data)
06643 {
06644 struct dial_trunk_args *args = data;
06645 struct ast_dial *dial;
06646 char *tech, *tech_data;
06647 enum ast_dial_result dial_res;
06648 char conf_name[MAX_CONFNUM];
06649 struct ast_conference *conf;
06650 struct ast_flags64 conf_flags = { 0 };
06651 RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
06652 RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
06653 int caller_is_saved;
06654 struct ast_party_caller caller;
06655 int last_state = 0;
06656 int current_state = 0;
06657
06658 if (!(dial = ast_dial_create())) {
06659 ast_mutex_lock(args->cond_lock);
06660 ast_cond_signal(args->cond);
06661 ast_mutex_unlock(args->cond_lock);
06662 return NULL;
06663 }
06664
06665 tech_data = ast_strdupa(trunk_ref->trunk->device);
06666 tech = strsep(&tech_data, "/");
06667 if (ast_dial_append(dial, tech, tech_data) == -1) {
06668 ast_mutex_lock(args->cond_lock);
06669 ast_cond_signal(args->cond);
06670 ast_mutex_unlock(args->cond_lock);
06671 ast_dial_destroy(dial);
06672 return NULL;
06673 }
06674
06675
06676 caller_is_saved = 0;
06677 if (!sla.attempt_callerid) {
06678 caller_is_saved = 1;
06679 caller = *ast_channel_caller(trunk_ref->chan);
06680 ast_party_caller_init(ast_channel_caller(trunk_ref->chan));
06681 }
06682
06683 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06684
06685
06686 if (caller_is_saved) {
06687 ast_party_caller_free(ast_channel_caller(trunk_ref->chan));
06688 ast_channel_caller_set(trunk_ref->chan, &caller);
06689 }
06690
06691 if (dial_res != AST_DIAL_RESULT_TRYING) {
06692 ast_mutex_lock(args->cond_lock);
06693 ast_cond_signal(args->cond);
06694 ast_mutex_unlock(args->cond_lock);
06695 ast_dial_destroy(dial);
06696 return NULL;
06697 }
06698
06699 for (;;) {
06700 unsigned int done = 0;
06701 switch ((dial_res = ast_dial_state(dial))) {
06702 case AST_DIAL_RESULT_ANSWERED:
06703 trunk_ref->trunk->chan = ast_dial_answered(dial);
06704 case AST_DIAL_RESULT_HANGUP:
06705 case AST_DIAL_RESULT_INVALID:
06706 case AST_DIAL_RESULT_FAILED:
06707 case AST_DIAL_RESULT_TIMEOUT:
06708 case AST_DIAL_RESULT_UNANSWERED:
06709 done = 1;
06710 break;
06711 case AST_DIAL_RESULT_TRYING:
06712 current_state = AST_CONTROL_PROGRESS;
06713 break;
06714 case AST_DIAL_RESULT_RINGING:
06715 case AST_DIAL_RESULT_PROGRESS:
06716 case AST_DIAL_RESULT_PROCEEDING:
06717 current_state = AST_CONTROL_RINGING;
06718 break;
06719 }
06720 if (done)
06721 break;
06722
06723
06724 if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06725 ast_debug(3, "Originating station device %s no longer active\n", station->device);
06726 trunk_ref->trunk->chan = NULL;
06727 break;
06728 }
06729
06730
06731 if (current_state != last_state) {
06732 ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, ast_channel_name(trunk_ref->chan));
06733 ast_indicate(trunk_ref->chan, current_state);
06734 last_state = current_state;
06735 }
06736
06737
06738 ast_safe_sleep(trunk_ref->chan, 100);
06739 }
06740
06741 if (!trunk_ref->trunk->chan) {
06742 ast_mutex_lock(args->cond_lock);
06743 ast_cond_signal(args->cond);
06744 ast_mutex_unlock(args->cond_lock);
06745 ast_dial_join(dial);
06746 ast_dial_destroy(dial);
06747 return NULL;
06748 }
06749
06750 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06751 ast_set_flag64(&conf_flags,
06752 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
06753 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06754 conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06755
06756 ast_mutex_lock(args->cond_lock);
06757 ast_cond_signal(args->cond);
06758 ast_mutex_unlock(args->cond_lock);
06759
06760 if (conf) {
06761 conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06762 dispose_conf(conf);
06763 conf = NULL;
06764 }
06765
06766
06767 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06768
06769 trunk_ref->trunk->chan = NULL;
06770 trunk_ref->trunk->on_hold = 0;
06771
06772 ast_dial_join(dial);
06773 ast_dial_destroy(dial);
06774
06775 return NULL;
06776 }
06777
06778
06779
06780
06781
06782 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06783 {
06784 struct sla_trunk_ref *trunk_ref = NULL;
06785
06786 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06787 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
06788 ao2_ref(trunk_ref, 1);
06789 break;
06790 }
06791 }
06792
06793 return trunk_ref;
06794 }
06795
06796 static int sla_station_exec(struct ast_channel *chan, const char *data)
06797 {
06798 char *station_name, *trunk_name;
06799 RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
06800 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06801 char conf_name[MAX_CONFNUM];
06802 struct ast_flags64 conf_flags = { 0 };
06803 struct ast_conference *conf;
06804
06805 if (ast_strlen_zero(data)) {
06806 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06807 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06808 return 0;
06809 }
06810
06811 trunk_name = ast_strdupa(data);
06812 station_name = strsep(&trunk_name, "_");
06813
06814 if (ast_strlen_zero(station_name)) {
06815 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06816 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06817 return 0;
06818 }
06819
06820 station = sla_find_station(station_name);
06821
06822 if (!station) {
06823 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06824 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06825 return 0;
06826 }
06827
06828 ao2_lock(station);
06829 if (!ast_strlen_zero(trunk_name)) {
06830 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06831 } else {
06832 trunk_ref = sla_choose_idle_trunk(station);
06833 }
06834 ao2_unlock(station);
06835
06836 if (!trunk_ref) {
06837 if (ast_strlen_zero(trunk_name))
06838 ast_log(LOG_NOTICE, "No trunks available for call.\n");
06839 else {
06840 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06841 "'%s' due to access controls.\n", trunk_name);
06842 }
06843 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06844 return 0;
06845 }
06846
06847 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06848 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06849 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06850 else {
06851 trunk_ref->state = SLA_TRUNK_STATE_UP;
06852 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
06853 "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06854 }
06855 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06856 struct sla_ringing_trunk *ringing_trunk;
06857
06858 ast_mutex_lock(&sla.lock);
06859 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06860 if (ringing_trunk->trunk == trunk_ref->trunk) {
06861 AST_LIST_REMOVE_CURRENT(entry);
06862 break;
06863 }
06864 }
06865 AST_LIST_TRAVERSE_SAFE_END
06866 ast_mutex_unlock(&sla.lock);
06867
06868 if (ringing_trunk) {
06869 answer_trunk_chan(ringing_trunk->trunk->chan);
06870 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06871
06872 sla_ringing_trunk_destroy(ringing_trunk);
06873
06874
06875 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06876 sla_queue_event(SLA_EVENT_DIAL_STATE);
06877 }
06878 }
06879
06880 trunk_ref->chan = chan;
06881
06882 if (!trunk_ref->trunk->chan) {
06883 ast_mutex_t cond_lock;
06884 ast_cond_t cond;
06885 pthread_t dont_care;
06886 struct dial_trunk_args args = {
06887 .trunk_ref = trunk_ref,
06888 .station = station,
06889 .cond_lock = &cond_lock,
06890 .cond = &cond,
06891 };
06892 ao2_ref(trunk_ref, 1);
06893 ao2_ref(station, 1);
06894 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06895
06896
06897
06898 ast_autoservice_start(chan);
06899 ast_mutex_init(&cond_lock);
06900 ast_cond_init(&cond, NULL);
06901 ast_mutex_lock(&cond_lock);
06902 ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06903 ast_cond_wait(&cond, &cond_lock);
06904 ast_mutex_unlock(&cond_lock);
06905 ast_mutex_destroy(&cond_lock);
06906 ast_cond_destroy(&cond);
06907 ast_autoservice_stop(chan);
06908 if (!trunk_ref->trunk->chan) {
06909 ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06910 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06911 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06912 trunk_ref->chan = NULL;
06913 return 0;
06914 }
06915 }
06916
06917 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06918 trunk_ref->trunk->on_hold) {
06919 trunk_ref->trunk->on_hold = 0;
06920 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06921 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06922 }
06923
06924 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06925 ast_set_flag64(&conf_flags,
06926 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06927 ast_answer(chan);
06928 conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06929 if (conf) {
06930 conf_run(chan, conf, &conf_flags, NULL);
06931 dispose_conf(conf);
06932 conf = NULL;
06933 }
06934 trunk_ref->chan = NULL;
06935 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06936 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06937 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06938 admin_exec(NULL, conf_name);
06939 trunk_ref->trunk->hold_stations = 0;
06940 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06941 }
06942
06943 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06944
06945 return 0;
06946 }
06947
06948 static void sla_trunk_ref_destructor(void *obj)
06949 {
06950 struct sla_trunk_ref *trunk_ref = obj;
06951
06952 if (trunk_ref->trunk) {
06953 ao2_ref(trunk_ref->trunk, -1);
06954 trunk_ref->trunk = NULL;
06955 }
06956 }
06957
06958 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06959 {
06960 struct sla_trunk_ref *trunk_ref;
06961
06962 if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
06963 return NULL;
06964 }
06965
06966 ao2_ref(trunk, 1);
06967 trunk_ref->trunk = trunk;
06968
06969 return trunk_ref;
06970 }
06971
06972 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06973 {
06974 struct sla_ringing_trunk *ringing_trunk;
06975
06976 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
06977 return NULL;
06978 }
06979
06980 ao2_ref(trunk, 1);
06981 ringing_trunk->trunk = trunk;
06982 ringing_trunk->ring_begin = ast_tvnow();
06983
06984 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06985
06986 ast_mutex_lock(&sla.lock);
06987 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06988 ast_mutex_unlock(&sla.lock);
06989
06990 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06991
06992 return ringing_trunk;
06993 }
06994
06995 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
06996 {
06997 if (ringing_trunk->trunk) {
06998 ao2_ref(ringing_trunk->trunk, -1);
06999 ringing_trunk->trunk = NULL;
07000 }
07001
07002 ast_free(ringing_trunk);
07003 }
07004
07005 enum {
07006 SLA_TRUNK_OPT_MOH = (1 << 0),
07007 };
07008
07009 enum {
07010 SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
07011 SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
07012 };
07013
07014 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
07015 AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
07016 END_OPTIONS );
07017
07018 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
07019 {
07020 char conf_name[MAX_CONFNUM];
07021 struct ast_conference *conf;
07022 struct ast_flags64 conf_flags = { 0 };
07023 RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07024 struct sla_ringing_trunk *ringing_trunk;
07025 AST_DECLARE_APP_ARGS(args,
07026 AST_APP_ARG(trunk_name);
07027 AST_APP_ARG(options);
07028 );
07029 char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
07030 struct ast_flags opt_flags = { 0 };
07031 char *parse;
07032
07033 if (ast_strlen_zero(data)) {
07034 ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
07035 return -1;
07036 }
07037
07038 parse = ast_strdupa(data);
07039 AST_STANDARD_APP_ARGS(args, parse);
07040 if (args.argc == 2) {
07041 if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
07042 ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
07043 return -1;
07044 }
07045 }
07046
07047 trunk = sla_find_trunk(args.trunk_name);
07048
07049 if (!trunk) {
07050 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
07051 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07052 return 0;
07053 }
07054
07055 if (trunk->chan) {
07056 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
07057 args.trunk_name);
07058 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07059 return 0;
07060 }
07061
07062 trunk->chan = chan;
07063
07064 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
07065 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07066 return 0;
07067 }
07068
07069 snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
07070 conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
07071 if (!conf) {
07072 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07073 return 0;
07074 }
07075 ast_set_flag64(&conf_flags,
07076 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
07077
07078 if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
07079 ast_indicate(chan, -1);
07080 ast_set_flag64(&conf_flags, CONFFLAG_MOH);
07081 } else
07082 ast_indicate(chan, AST_CONTROL_RINGING);
07083
07084 conf_run(chan, conf, &conf_flags, opts);
07085 dispose_conf(conf);
07086 conf = NULL;
07087 trunk->chan = NULL;
07088 trunk->on_hold = 0;
07089
07090 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
07091
07092 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
07093 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
07094
07095
07096 ast_mutex_lock(&sla.lock);
07097 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
07098 if (ringing_trunk->trunk == trunk) {
07099 AST_LIST_REMOVE_CURRENT(entry);
07100 break;
07101 }
07102 }
07103 AST_LIST_TRAVERSE_SAFE_END;
07104 ast_mutex_unlock(&sla.lock);
07105 if (ringing_trunk) {
07106 sla_ringing_trunk_destroy(ringing_trunk);
07107 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
07108
07109
07110 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
07111 }
07112
07113 return 0;
07114 }
07115
07116 static enum ast_device_state sla_state(const char *data)
07117 {
07118 char *buf, *station_name, *trunk_name;
07119 RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07120 struct sla_trunk_ref *trunk_ref;
07121 enum ast_device_state res = AST_DEVICE_INVALID;
07122
07123 trunk_name = buf = ast_strdupa(data);
07124 station_name = strsep(&trunk_name, "_");
07125
07126 station = sla_find_station(station_name);
07127 if (station) {
07128 ao2_lock(station);
07129 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07130 if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
07131 res = sla_state_to_devstate(trunk_ref->state);
07132 break;
07133 }
07134 }
07135 ao2_unlock(station);
07136 }
07137
07138 if (res == AST_DEVICE_INVALID) {
07139 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
07140 trunk_name, station_name);
07141 }
07142
07143 return res;
07144 }
07145
07146 static int sla_trunk_release_refs(void *obj, void *arg, int flags)
07147 {
07148 struct sla_trunk *trunk = obj;
07149 struct sla_station_ref *station_ref;
07150
07151 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
07152 ao2_ref(station_ref, -1);
07153 }
07154
07155 return 0;
07156 }
07157
07158 static int sla_station_release_refs(void *obj, void *arg, int flags)
07159 {
07160 struct sla_station *station = obj;
07161 struct sla_trunk_ref *trunk_ref;
07162
07163 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
07164 ao2_ref(trunk_ref, -1);
07165 }
07166
07167 return 0;
07168 }
07169
07170 static void sla_station_destructor(void *obj)
07171 {
07172 struct sla_station *station = obj;
07173
07174 ast_debug(1, "sla_station destructor for '%s'\n", station->name);
07175
07176 if (!ast_strlen_zero(station->autocontext)) {
07177 struct sla_trunk_ref *trunk_ref;
07178
07179 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07180 char exten[AST_MAX_EXTENSION];
07181 char hint[AST_MAX_APP];
07182 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07183 snprintf(hint, sizeof(hint), "SLA:%s", exten);
07184 ast_context_remove_extension(station->autocontext, exten,
07185 1, sla_registrar);
07186 ast_context_remove_extension(station->autocontext, hint,
07187 PRIORITY_HINT, sla_registrar);
07188 }
07189 }
07190
07191 sla_station_release_refs(station, NULL, 0);
07192
07193 ast_string_field_free_memory(station);
07194 }
07195
07196 static int sla_trunk_hash(const void *obj, const int flags)
07197 {
07198 const struct sla_trunk *trunk = obj;
07199
07200 return ast_str_case_hash(trunk->name);
07201 }
07202
07203 static int sla_trunk_cmp(void *obj, void *arg, int flags)
07204 {
07205 struct sla_trunk *trunk = obj, *trunk2 = arg;
07206
07207 return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
07208 }
07209
07210 static int sla_station_hash(const void *obj, const int flags)
07211 {
07212 const struct sla_station *station = obj;
07213
07214 return ast_str_case_hash(station->name);
07215 }
07216
07217 static int sla_station_cmp(void *obj, void *arg, int flags)
07218 {
07219 struct sla_station *station = obj, *station2 = arg;
07220
07221 return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
07222 }
07223
07224 static void sla_destroy(void)
07225 {
07226 if (sla.thread != AST_PTHREADT_NULL) {
07227 ast_mutex_lock(&sla.lock);
07228 sla.stop = 1;
07229 ast_cond_signal(&sla.cond);
07230 ast_mutex_unlock(&sla.lock);
07231 pthread_join(sla.thread, NULL);
07232 }
07233
07234
07235 ast_context_destroy(NULL, sla_registrar);
07236
07237 ast_mutex_destroy(&sla.lock);
07238 ast_cond_destroy(&sla.cond);
07239
07240 ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
07241 ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
07242
07243 ao2_ref(sla_trunks, -1);
07244 sla_trunks = NULL;
07245
07246 ao2_ref(sla_stations, -1);
07247 sla_stations = NULL;
07248 }
07249
07250 static int sla_check_device(const char *device)
07251 {
07252 char *tech, *tech_data;
07253
07254 tech_data = ast_strdupa(device);
07255 tech = strsep(&tech_data, "/");
07256
07257 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
07258 return -1;
07259
07260 return 0;
07261 }
07262
07263 static void sla_trunk_destructor(void *obj)
07264 {
07265 struct sla_trunk *trunk = obj;
07266
07267 ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
07268
07269 if (!ast_strlen_zero(trunk->autocontext)) {
07270 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
07271 }
07272
07273 sla_trunk_release_refs(trunk, NULL, 0);
07274
07275 ast_string_field_free_memory(trunk);
07276 }
07277
07278 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
07279 {
07280 RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07281 struct ast_variable *var;
07282 const char *dev;
07283 int existing_trunk = 0;
07284
07285 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07286 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
07287 return -1;
07288 }
07289
07290 if (sla_check_device(dev)) {
07291 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
07292 cat, dev);
07293 return -1;
07294 }
07295
07296 if ((trunk = sla_find_trunk(cat))) {
07297 trunk->mark = 0;
07298 existing_trunk = 1;
07299 } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
07300 if (ast_string_field_init(trunk, 32)) {
07301 return -1;
07302 }
07303 ast_string_field_set(trunk, name, cat);
07304 } else {
07305 return -1;
07306 }
07307
07308 ao2_lock(trunk);
07309
07310 ast_string_field_set(trunk, device, dev);
07311
07312 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07313 if (!strcasecmp(var->name, "autocontext"))
07314 ast_string_field_set(trunk, autocontext, var->value);
07315 else if (!strcasecmp(var->name, "ringtimeout")) {
07316 if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
07317 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
07318 var->value, trunk->name);
07319 trunk->ring_timeout = 0;
07320 }
07321 } else if (!strcasecmp(var->name, "barge"))
07322 trunk->barge_disabled = ast_false(var->value);
07323 else if (!strcasecmp(var->name, "hold")) {
07324 if (!strcasecmp(var->value, "private"))
07325 trunk->hold_access = SLA_HOLD_PRIVATE;
07326 else if (!strcasecmp(var->value, "open"))
07327 trunk->hold_access = SLA_HOLD_OPEN;
07328 else {
07329 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
07330 var->value, trunk->name);
07331 }
07332 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07333 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07334 var->name, var->lineno, SLA_CONFIG_FILE);
07335 }
07336 }
07337
07338 ao2_unlock(trunk);
07339
07340 if (!ast_strlen_zero(trunk->autocontext)) {
07341 struct ast_context *context;
07342 context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
07343 if (!context) {
07344 ast_log(LOG_ERROR, "Failed to automatically find or create "
07345 "context '%s' for SLA!\n", trunk->autocontext);
07346 return -1;
07347 }
07348 if (ast_add_extension2(context, 0 , "s", 1,
07349 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
07350 ast_log(LOG_ERROR, "Failed to automatically create extension "
07351 "for trunk '%s'!\n", trunk->name);
07352 return -1;
07353 }
07354 }
07355
07356 if (!existing_trunk) {
07357 ao2_link(sla_trunks, trunk);
07358 }
07359
07360 return 0;
07361 }
07362
07363
07364
07365
07366
07367 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
07368 {
07369 RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07370 struct sla_trunk_ref *trunk_ref = NULL;
07371 struct sla_station_ref *station_ref;
07372 char *trunk_name, *options, *cur;
07373 int existing_trunk_ref = 0;
07374 int existing_station_ref = 0;
07375
07376 options = ast_strdupa(var->value);
07377 trunk_name = strsep(&options, ",");
07378
07379 trunk = sla_find_trunk(trunk_name);
07380 if (!trunk) {
07381 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
07382 return;
07383 }
07384
07385 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07386 if (trunk_ref->trunk == trunk) {
07387 trunk_ref->mark = 0;
07388 existing_trunk_ref = 1;
07389 break;
07390 }
07391 }
07392
07393 if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
07394 return;
07395 }
07396
07397 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
07398
07399 while ((cur = strsep(&options, ","))) {
07400 char *name, *value = cur;
07401 name = strsep(&value, "=");
07402 if (!strcasecmp(name, "ringtimeout")) {
07403 if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
07404 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
07405 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07406 trunk_ref->ring_timeout = 0;
07407 }
07408 } else if (!strcasecmp(name, "ringdelay")) {
07409 if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
07410 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
07411 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07412 trunk_ref->ring_delay = 0;
07413 }
07414 } else {
07415 ast_log(LOG_WARNING, "Invalid option '%s' for "
07416 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
07417 }
07418 }
07419
07420 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07421 if (station_ref->station == station) {
07422 station_ref->mark = 0;
07423 existing_station_ref = 1;
07424 break;
07425 }
07426 }
07427
07428 if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
07429 if (!existing_trunk_ref) {
07430 ao2_ref(trunk_ref, -1);
07431 } else {
07432 trunk_ref->mark = 1;
07433 }
07434 return;
07435 }
07436
07437 if (!existing_station_ref) {
07438 ao2_lock(trunk);
07439 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
07440 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
07441 ao2_unlock(trunk);
07442 }
07443
07444 if (!existing_trunk_ref) {
07445 ao2_lock(station);
07446 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
07447 ao2_unlock(station);
07448 }
07449 }
07450
07451 static int sla_build_station(struct ast_config *cfg, const char *cat)
07452 {
07453 RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07454 struct ast_variable *var;
07455 const char *dev;
07456 int existing_station = 0;
07457
07458 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07459 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
07460 return -1;
07461 }
07462
07463 if ((station = sla_find_station(cat))) {
07464 station->mark = 0;
07465 existing_station = 1;
07466 } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
07467 if (ast_string_field_init(station, 32)) {
07468 return -1;
07469 }
07470 ast_string_field_set(station, name, cat);
07471 } else {
07472 return -1;
07473 }
07474
07475 ao2_lock(station);
07476
07477 ast_string_field_set(station, device, dev);
07478
07479 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07480 if (!strcasecmp(var->name, "trunk")) {
07481 ao2_unlock(station);
07482 sla_add_trunk_to_station(station, var);
07483 ao2_lock(station);
07484 } else if (!strcasecmp(var->name, "autocontext")) {
07485 ast_string_field_set(station, autocontext, var->value);
07486 } else if (!strcasecmp(var->name, "ringtimeout")) {
07487 if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
07488 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
07489 var->value, station->name);
07490 station->ring_timeout = 0;
07491 }
07492 } else if (!strcasecmp(var->name, "ringdelay")) {
07493 if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
07494 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
07495 var->value, station->name);
07496 station->ring_delay = 0;
07497 }
07498 } else if (!strcasecmp(var->name, "hold")) {
07499 if (!strcasecmp(var->value, "private"))
07500 station->hold_access = SLA_HOLD_PRIVATE;
07501 else if (!strcasecmp(var->value, "open"))
07502 station->hold_access = SLA_HOLD_OPEN;
07503 else {
07504 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
07505 var->value, station->name);
07506 }
07507
07508 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07509 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07510 var->name, var->lineno, SLA_CONFIG_FILE);
07511 }
07512 }
07513
07514 ao2_unlock(station);
07515
07516 if (!ast_strlen_zero(station->autocontext)) {
07517 struct ast_context *context;
07518 struct sla_trunk_ref *trunk_ref;
07519 context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
07520 if (!context) {
07521 ast_log(LOG_ERROR, "Failed to automatically find or create "
07522 "context '%s' for SLA!\n", station->autocontext);
07523 return -1;
07524 }
07525
07526
07527 if (ast_add_extension2(context, 0 , station->name, 1,
07528 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
07529 ast_log(LOG_ERROR, "Failed to automatically create extension "
07530 "for trunk '%s'!\n", station->name);
07531 return -1;
07532 }
07533 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07534 char exten[AST_MAX_EXTENSION];
07535 char hint[AST_MAX_APP];
07536 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07537 snprintf(hint, sizeof(hint), "SLA:%s", exten);
07538
07539
07540 if (ast_add_extension2(context, 0 , exten, 1,
07541 NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
07542 ast_log(LOG_ERROR, "Failed to automatically create extension "
07543 "for trunk '%s'!\n", station->name);
07544 return -1;
07545 }
07546
07547
07548 if (ast_add_extension2(context, 0 , exten, PRIORITY_HINT,
07549 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
07550 ast_log(LOG_ERROR, "Failed to automatically create hint "
07551 "for trunk '%s'!\n", station->name);
07552 return -1;
07553 }
07554 }
07555 }
07556
07557 if (!existing_station) {
07558 ao2_link(sla_stations, station);
07559 }
07560
07561 return 0;
07562 }
07563
07564 static int sla_trunk_mark(void *obj, void *arg, int flags)
07565 {
07566 struct sla_trunk *trunk = obj;
07567 struct sla_station_ref *station_ref;
07568
07569 ao2_lock(trunk);
07570
07571 trunk->mark = 1;
07572
07573 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07574 station_ref->mark = 1;
07575 }
07576
07577 ao2_unlock(trunk);
07578
07579 return 0;
07580 }
07581
07582 static int sla_station_mark(void *obj, void *arg, int flags)
07583 {
07584 struct sla_station *station = obj;
07585 struct sla_trunk_ref *trunk_ref;
07586
07587 ao2_lock(station);
07588
07589 station->mark = 1;
07590
07591 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07592 trunk_ref->mark = 1;
07593 }
07594
07595 ao2_unlock(station);
07596
07597 return 0;
07598 }
07599
07600 static int sla_trunk_is_marked(void *obj, void *arg, int flags)
07601 {
07602 struct sla_trunk *trunk = obj;
07603
07604 ao2_lock(trunk);
07605
07606 if (trunk->mark) {
07607
07608 sla_trunk_release_refs(trunk, NULL, 0);
07609 } else {
07610 struct sla_station_ref *station_ref;
07611
07612
07613 AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
07614 if (!station_ref->mark) {
07615 continue;
07616 }
07617 AST_LIST_REMOVE_CURRENT(entry);
07618 ao2_ref(station_ref, -1);
07619 }
07620 AST_LIST_TRAVERSE_SAFE_END
07621 }
07622
07623 ao2_unlock(trunk);
07624
07625 return trunk->mark ? CMP_MATCH : 0;
07626 }
07627
07628 static int sla_station_is_marked(void *obj, void *arg, int flags)
07629 {
07630 struct sla_station *station = obj;
07631
07632 ao2_lock(station);
07633
07634 if (station->mark) {
07635
07636 sla_station_release_refs(station, NULL, 0);
07637 } else {
07638 struct sla_trunk_ref *trunk_ref;
07639
07640
07641 AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
07642 if (!trunk_ref->mark) {
07643 continue;
07644 }
07645 AST_LIST_REMOVE_CURRENT(entry);
07646 ao2_ref(trunk_ref, -1);
07647 }
07648 AST_LIST_TRAVERSE_SAFE_END
07649 }
07650
07651 ao2_unlock(station);
07652
07653 return station->mark ? CMP_MATCH : 0;
07654 }
07655
07656 static int sla_in_use(void)
07657 {
07658 return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
07659 }
07660
07661 static int sla_load_config(int reload)
07662 {
07663 struct ast_config *cfg;
07664 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07665 const char *cat = NULL;
07666 int res = 0;
07667 const char *val;
07668
07669 if (!reload) {
07670 ast_mutex_init(&sla.lock);
07671 ast_cond_init(&sla.cond, NULL);
07672 sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
07673 sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
07674 }
07675
07676 if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
07677 return 0;
07678 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07679 return 0;
07680 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07681 ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format. Aborting.\n");
07682 return 0;
07683 }
07684
07685 if (reload) {
07686 ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
07687 ao2_callback(sla_stations, 0, sla_station_mark, NULL);
07688 }
07689
07690 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
07691 sla.attempt_callerid = ast_true(val);
07692
07693 while ((cat = ast_category_browse(cfg, cat)) && !res) {
07694 const char *type;
07695 if (!strcasecmp(cat, "general"))
07696 continue;
07697 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
07698 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
07699 SLA_CONFIG_FILE);
07700 continue;
07701 }
07702 if (!strcasecmp(type, "trunk"))
07703 res = sla_build_trunk(cfg, cat);
07704 else if (!strcasecmp(type, "station"))
07705 res = sla_build_station(cfg, cat);
07706 else {
07707 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
07708 SLA_CONFIG_FILE, type);
07709 }
07710 }
07711
07712 ast_config_destroy(cfg);
07713
07714 if (reload) {
07715 ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
07716 ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
07717 }
07718
07719
07720 if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
07721 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
07722 }
07723
07724 return res;
07725 }
07726
07727 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
07728 {
07729 if (!strcasecmp("lock", keyword)) {
07730 return conf->locked;
07731 } else if (!strcasecmp("parties", keyword)) {
07732 return conf->users;
07733 } else if (!strcasecmp("activity", keyword)) {
07734 time_t now;
07735 now = time(NULL);
07736 return (now - conf->start);
07737 } else if (!strcasecmp("dynamic", keyword)) {
07738 return conf->isdynamic;
07739 } else {
07740 return -1;
07741 }
07742
07743 }
07744
07745 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
07746 {
07747 struct ast_conference *conf;
07748 char *parse;
07749 int result = -2;
07750 AST_DECLARE_APP_ARGS(args,
07751 AST_APP_ARG(keyword);
07752 AST_APP_ARG(confno);
07753 );
07754
07755 if (ast_strlen_zero(data)) {
07756 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07757 return -1;
07758 }
07759
07760 parse = ast_strdupa(data);
07761 AST_STANDARD_APP_ARGS(args, parse);
07762
07763 if (ast_strlen_zero(args.keyword)) {
07764 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07765 return -1;
07766 }
07767
07768 if (ast_strlen_zero(args.confno)) {
07769 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07770 return -1;
07771 }
07772
07773 AST_LIST_LOCK(&confs);
07774 AST_LIST_TRAVERSE(&confs, conf, list) {
07775 if (!strcmp(args.confno, conf->confno)) {
07776 result = acf_meetme_info_eval(args.keyword, conf);
07777 break;
07778 }
07779 }
07780 AST_LIST_UNLOCK(&confs);
07781
07782 if (result > -1) {
07783 snprintf(buf, len, "%d", result);
07784 } else if (result == -1) {
07785 ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07786 snprintf(buf, len, "0");
07787 } else if (result == -2) {
07788 ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno);
07789 snprintf(buf, len, "0");
07790 }
07791
07792 return 0;
07793 }
07794
07795
07796 static struct ast_custom_function meetme_info_acf = {
07797 .name = "MEETME_INFO",
07798 .read = acf_meetme_info,
07799 };
07800
07801
07802 static int load_config(int reload)
07803 {
07804 load_config_meetme();
07805 return sla_load_config(reload);
07806 }
07807
07808 #define MEETME_DATA_EXPORT(MEMBER) \
07809 MEMBER(ast_conference, confno, AST_DATA_STRING) \
07810 MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER) \
07811 MEMBER(ast_conference, users, AST_DATA_INTEGER) \
07812 MEMBER(ast_conference, markedusers, AST_DATA_INTEGER) \
07813 MEMBER(ast_conference, maxusers, AST_DATA_INTEGER) \
07814 MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN) \
07815 MEMBER(ast_conference, locked, AST_DATA_BOOLEAN) \
07816 MEMBER(ast_conference, recordingfilename, AST_DATA_STRING) \
07817 MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
07818 MEMBER(ast_conference, pin, AST_DATA_PASSWORD) \
07819 MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD) \
07820 MEMBER(ast_conference, start, AST_DATA_TIMESTAMP) \
07821 MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
07822
07823 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07824
07825 #define MEETME_USER_DATA_EXPORT(MEMBER) \
07826 MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER) \
07827 MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN) \
07828 MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN) \
07829 MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP) \
07830 MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP) \
07831 MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS) \
07832 MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS) \
07833 MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07834
07835 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07836
07837 static int user_add_provider_cb(void *obj, void *arg, int flags)
07838 {
07839 struct ast_data *data_meetme_user;
07840 struct ast_data *data_meetme_user_channel;
07841 struct ast_data *data_meetme_user_volume;
07842
07843 struct ast_conf_user *user = obj;
07844 struct ast_data *data_meetme_users = arg;
07845
07846 data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07847 if (!data_meetme_user) {
07848 return 0;
07849 }
07850
07851 ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07852
07853
07854 data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07855 if (!data_meetme_user_channel) {
07856 return 0;
07857 }
07858
07859 ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07860
07861
07862 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07863 if (!data_meetme_user_volume) {
07864 return 0;
07865 }
07866 ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07867 ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07868
07869 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07870 if (!data_meetme_user_volume) {
07871 return 0;
07872 }
07873 ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07874 ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07875
07876 return 0;
07877 }
07878
07879
07880
07881
07882
07883 static int meetme_data_provider_get(const struct ast_data_search *search,
07884 struct ast_data *data_root)
07885 {
07886 struct ast_conference *cnf;
07887 struct ast_data *data_meetme, *data_meetme_users;
07888
07889 AST_LIST_LOCK(&confs);
07890 AST_LIST_TRAVERSE(&confs, cnf, list) {
07891 data_meetme = ast_data_add_node(data_root, "meetme");
07892 if (!data_meetme) {
07893 continue;
07894 }
07895
07896 ast_data_add_structure(ast_conference, data_meetme, cnf);
07897
07898 if (ao2_container_count(cnf->usercontainer)) {
07899 data_meetme_users = ast_data_add_node(data_meetme, "users");
07900 if (!data_meetme_users) {
07901 ast_data_remove_node(data_root, data_meetme);
07902 continue;
07903 }
07904
07905 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users);
07906 }
07907
07908 if (!ast_data_search_match(search, data_meetme)) {
07909 ast_data_remove_node(data_root, data_meetme);
07910 }
07911 }
07912 AST_LIST_UNLOCK(&confs);
07913
07914 return 0;
07915 }
07916
07917 static const struct ast_data_handler meetme_data_provider = {
07918 .version = AST_DATA_HANDLER_VERSION,
07919 .get = meetme_data_provider_get
07920 };
07921
07922 static const struct ast_data_entry meetme_data_providers[] = {
07923 AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07924 };
07925
07926 #ifdef TEST_FRAMEWORK
07927 AST_TEST_DEFINE(test_meetme_data_provider)
07928 {
07929 struct ast_channel *chan;
07930 struct ast_conference *cnf;
07931 struct ast_data *node;
07932 struct ast_data_query query = {
07933 .path = "/asterisk/application/meetme/list",
07934 .search = "list/meetme/confno=9898"
07935 };
07936
07937 switch (cmd) {
07938 case TEST_INIT:
07939 info->name = "meetme_get_data_test";
07940 info->category = "/main/data/app_meetme/list/";
07941 info->summary = "Meetme data provider unit test";
07942 info->description =
07943 "Tests whether the Meetme data provider implementation works as expected.";
07944 return AST_TEST_NOT_RUN;
07945 case TEST_EXECUTE:
07946 break;
07947 }
07948
07949 chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07950 if (!chan) {
07951 ast_test_status_update(test, "Channel allocation failed\n");
07952 return AST_TEST_FAIL;
07953 }
07954
07955 cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07956 if (!cnf) {
07957 ast_test_status_update(test, "Build of test conference 9898 failed\n");
07958 ast_hangup(chan);
07959 return AST_TEST_FAIL;
07960 }
07961
07962 node = ast_data_get(&query);
07963 if (!node) {
07964 ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07965 dispose_conf(cnf);
07966 ast_hangup(chan);
07967 return AST_TEST_FAIL;
07968 }
07969
07970 if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07971 ast_test_status_update(test, "Query returned the wrong conference\n");
07972 dispose_conf(cnf);
07973 ast_hangup(chan);
07974 ast_data_free(node);
07975 return AST_TEST_FAIL;
07976 }
07977
07978 ast_data_free(node);
07979 dispose_conf(cnf);
07980 ast_hangup(chan);
07981
07982 return AST_TEST_PASS;
07983 }
07984 #endif
07985
07986 static int unload_module(void)
07987 {
07988 int res = 0;
07989
07990 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07991 res = ast_manager_unregister("MeetmeMute");
07992 res |= ast_manager_unregister("MeetmeUnmute");
07993 res |= ast_manager_unregister("MeetmeList");
07994 res |= ast_manager_unregister("MeetmeListRooms");
07995 res |= ast_unregister_application(app4);
07996 res |= ast_unregister_application(app3);
07997 res |= ast_unregister_application(app2);
07998 res |= ast_unregister_application(app);
07999 res |= ast_unregister_application(slastation_app);
08000 res |= ast_unregister_application(slatrunk_app);
08001
08002 #ifdef TEST_FRAMEWORK
08003 AST_TEST_UNREGISTER(test_meetme_data_provider);
08004 #endif
08005 ast_data_unregister(NULL);
08006
08007 ast_devstate_prov_del("Meetme");
08008 ast_devstate_prov_del("SLA");
08009
08010 sla_destroy();
08011
08012 res |= ast_custom_function_unregister(&meetme_info_acf);
08013 ast_unload_realtime("meetme");
08014
08015 return res;
08016 }
08017
08018 static int load_module(void)
08019 {
08020 int res = 0;
08021
08022 res |= load_config(0);
08023
08024 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
08025 res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
08026 res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
08027 res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
08028 res |= ast_manager_register_xml("MeetmeListRooms", EVENT_FLAG_REPORTING, action_meetmelistrooms);
08029 res |= ast_register_application_xml(app4, channel_admin_exec);
08030 res |= ast_register_application_xml(app3, admin_exec);
08031 res |= ast_register_application_xml(app2, count_exec);
08032 res |= ast_register_application_xml(app, conf_exec);
08033 res |= ast_register_application_xml(slastation_app, sla_station_exec);
08034 res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
08035
08036 #ifdef TEST_FRAMEWORK
08037 AST_TEST_REGISTER(test_meetme_data_provider);
08038 #endif
08039 ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
08040
08041 res |= ast_devstate_prov_add("Meetme", meetmestate);
08042 res |= ast_devstate_prov_add("SLA", sla_state);
08043
08044 res |= ast_custom_function_register(&meetme_info_acf);
08045 ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
08046
08047 return res;
08048 }
08049
08050 static int reload(void)
08051 {
08052 ast_unload_realtime("meetme");
08053 return load_config(1);
08054 }
08055
08056 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
08057 .load = load_module,
08058 .unload = unload_module,
08059 .reload = reload,
08060 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
08061 );
08062