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