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