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