48#include <dahdi/user.h>
655#define CONFIG_FILE_NAME "meetme.conf"
656#define STR_CONCISE "concise"
659#define DEFAULT_AUDIO_BUFFERS 32
662#define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
673#define MEETME_DELAYDETECTTALK 300
674#define MEETME_DELAYDETECTENDTALK 1000
676#define AST_FRAME_BITS 32
757#define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
758#define CONFFLAG_INTROMSG (1ULL << 32)
759#define CONFFLAG_INTROUSER_VMREC (1ULL << 33)
761#define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
763#define CONFFLAG_DONT_DENOISE (1ULL << 35)
812static const char *
const app =
"MeetMe";
813static const char *
const app2 =
"MeetMeCount";
814static const char *
const app3 =
"MeetMeAdmin";
815static const char *
const app4 =
"MeetMeChannelAdmin";
827#define MAX_CONFNUM 80
829#define OPTIONS_LEN 100
832#define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
1006 meetme_leave_type(),
1030 meetme_talking_type(),
1038 meetme_talk_request_type(),
1054 const char *conference_num;
1060 if (!channel_blob) {
1067 if (!message_type) {
1072 if (message_type == meetme_join_type()) {
1073 event =
"MeetmeJoin";
1074 }
else if (message_type == meetme_leave_type()) {
1075 event =
"MeetmeLeave";
1076 }
else if (message_type == meetme_end_type()) {
1077 event =
"MeetmeEnd";
1078 }
else if (message_type == meetme_mute_type()) {
1079 event =
"MeetmeMute";
1080 }
else if (message_type == meetme_talking_type()) {
1081 event =
"MeetmeTalking";
1082 }
else if (message_type == meetme_talk_request_type()) {
1083 event =
"MeetmeTalkRequest";
1095 if (!conference_num) {
1112 if (!user_prop_str) {
1116 ast_str_set(&user_prop_str, 0,
"%d", user_number);
1149 "status", on ?
"on" :
"off");
1172 "Meetme", meetme_conference->
confno);
1184 long duration = (long)(now.tv_sec -
user->jointime);
1186 struct ast_json *json_user_duration;
1195 if (!json_user_duration
1225 return "(unmonitored)";
1227 return "(not talking)";
1237 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1238 res = ioctl(fd, DAHDI_IOMUX, &x);
1242 res = write(fd, data,
len);
1244 if (
errno != EAGAIN) {
1323 user->talk.actual = 0;
1325 user->talk.actual =
user->talk.desired;
1335 user->listen.actual = 0;
1337 user->listen.actual =
user->listen.desired;
1342 signed char zero_volume = 0;
1350 unsigned char *data;
1355 "Conference: %s\r\n"
1406 if (
user->user_no > *max_no) {
1407 *max_no =
user->user_no;
1433 struct dahdi_confinfo dahdic = { 0, };
1440 if (!strcmp(confno, cnf->
confno))
1444 if (cnf || (!make && !dynamic) || !cap_slin)
1473 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1474 cnf->
fd = open(
"/dev/dahdi/pseudo", O_RDWR);
1475 if (cnf->
fd < 0 || ioctl(cnf->
fd, DAHDI_SETCONF, &dahdic)) {
1505 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1534 if ((sscanf(cnf->
confno,
"%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1577 snprintf(usrno,
sizeof(usrno),
"%d", usr->
user_no);
1601 if (!strncasecmp(
word,
"all",
len)) {
1610 strtok_r(myline,
" ", &saved);
1611 strtok_r(
NULL,
" ", &saved);
1651 if (pos == 3 &&
state == 0) {
1658 strtok_r(myline,
" ", &saved);
1659 strtok_r(
NULL,
" ", &saved);
1683#define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
1684#define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
1690 "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
1691 " List all conferences or a specific conference.\n";
1697 if (
a->argc == 2 || (
a->argc == 3 && !strcasecmp(
a->argv[2],
STR_CONCISE))) {
1699 int concise = (
a->argc == 3);
1710 ast_cli(
a->fd,
"No active MeetMe conferences.\n");
1720 hr = (now - cnf->
start) / 3600;
1721 min = ((now - cnf->
start) % 3600) / 60;
1722 sec = (now - cnf->
start) % 60;
1733 ast_cli(
a->fd,
"%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1746 ast_cli(
a->fd,
"* Total number of MeetMe users: %d\n",
total);
1751 if (
a->argc == 3 || (
a->argc == 4 && !strcasecmp(
a->argv[3],
STR_CONCISE))) {
1753 int concise = (
a->argc == 4);
1758 ast_cli(
a->fd,
"No active MeetMe conferences.\n");
1765 if (strcmp(cnf->
confno,
a->argv[2]) == 0) {
1771 ast_cli(
a->fd,
"No such conference: %s.\n",
a->argv[2]);
1779 hr = (now -
user->jointime) / 3600;
1780 min = ((now -
user->jointime) % 3600) / 60;
1781 sec = (now -
user->jointime) % 60;
1783 ast_cli(
a->fd,
"User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1794 ast_cli(
a->fd,
"%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1809 ast_cli(
a->fd,
"%d users in that conference.\n", cnf->
users);
1829 if (strcasecmp(
a->argv[1],
"lock") == 0) {
1837 if (strcasecmp(
a->argv[1],
"mute") == 0) {
1839 if (strcasecmp(
a->argv[3],
"all") == 0) {
1846 if (strcasecmp(
a->argv[3],
"all") == 0) {
1852 }
else if (strcasecmp(
a->argv[1],
"kick") == 0) {
1853 if (strcasecmp(
a->argv[3],
"all") == 0) {
1881 e->
command =
"meetme {lock|unlock}";
1883 "Usage: meetme lock|unlock <confno>\n"
1884 " Lock or unlock a conference to new users.\n";
1903 "Usage: meetme kick <confno> all|<userno>\n"
1904 " Kick a conference or a user in a conference.\n";
1921 e->
command =
"meetme {mute|unmute}";
1923 "Usage: meetme mute|unmute <confno> all|<userno>\n"
1924 " Mute or unmute a conference or a user in a conference.\n";
1967 x = DAHDI_FLUSH_ALL;
1968 if (ioctl(fd, DAHDI_FLUSH, &x))
1998 if (
conf->transframe[x])
2000 if (
conf->transpath[x])
2005 conf->announcethread_stop = 1;
2009 pthread_join(
conf->announcethread,
NULL);
2021 if (
conf->origframe)
2027 if (
conf->recordingfilename) {
2030 if (
conf->usercontainer) {
2033 if (
conf->recordingformat) {
2053 if (
user == sender) {
2073 if ((sscanf(
conf->confno,
"%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2086 char currenttime[32];
2103 confno,
"startTime<= ", currenttime,
2104 "endtime>= ", currenttime,
NULL);
2110 if (!strcasecmp(
var->name,
"bookid")) {
2113 if (!strcasecmp(
var->name,
"endtime")) {
2128 strcat(currenttime,
"0");
2131 confno,
"startTime<= ", currenttime,
2132 "endtime>= ", currenttime,
NULL);
2136 ast_debug(3,
"Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2152 ast_channel_musicclass_set(chan, musicclass);
2158 ast_channel_musicclass_set(chan, original_moh);
2166 return "conf-hasleft";
2169 return "conf-hasjoin";
2185 while (!
conf->announcethread_stop) {
2187 if (
conf->announcethread_stop) {
2198 if (
conf->announcethread_stop) {
2250 int last_talking =
user->talking;
2251 if (last_talking == talking)
2254 user->talking = talking;
2258 int was_talking = (last_talking > 0);
2259 int now_talking = (talking > 0);
2260 if (was_talking != now_talking) {
2543 if (
conf->users == 1) {
2551 }
else if (
conf->users == 2) {
2609 if (
conf->users == 1) {
2641 if (!
conf->recordingfilename) {
2651 if (!
conf->recordingfilename) {
2655 if (!
conf->recordingformat) {
2658 ast_verb(4,
"Starting recording of MeetMe Conference %s into file %s.%s.\n",
2659 conf->confno,
conf->recordingfilename,
conf->recordingformat);
2664 struct dahdi_confinfo dahdic;
2669 dahdic.confno =
conf->dahdiconf;
2670 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2721 switch (*menu_mode) {
2735 recordingtmp, recordingtmp_size, cap_slin);
2744 struct dahdi_confinfo dahdic, dahdic_empty;
2754 int musiconhold = 0, mohtempstopped = 0;
2757 int currentmarked = 0;
2761 int talkreq_manager = 0;
2762 int using_pseudo = 0;
2766 int announcement_played = 0;
2771 const char *agifiledefault =
"conf-background.agi", *tmpvar;
2772 char meetmesecs[30] =
"";
2775 char members[10] =
"";
2776 int dtmf = 0, opt_waitmarked_timeout = 0;
2778 struct dahdi_bufferinfo bi;
2781 char *exitkeys =
NULL;
2782 unsigned int calldurationlimit = 0;
2784 long play_warning = 0;
2785 long warning_freq = 0;
2786 const char *warning_sound =
NULL;
2787 const char *end_sound =
NULL;
2789 long time_left_ms = 0;
2790 struct timeval nexteventts = { 0, };
2792 int setusercount = 0;
2793 int confsilence = 0, totalsilence = 0;
2798 goto conf_run_cleanup;
2803 goto conf_run_cleanup;
2810 (opt_waitmarked_timeout > 0)) {
2811 timeout = time(
NULL) + opt_waitmarked_timeout;
2816 ast_verb(3,
"Setting call duration limit to %u seconds.\n", calldurationlimit);
2820 char *limit_str, *warning_str, *warnfreq_str;
2824 limit_str =
strsep(&parse,
":");
2825 warning_str =
strsep(&parse,
":");
2826 warnfreq_str = parse;
2828 timelimit = atol(limit_str);
2830 play_warning = atol(warning_str);
2832 warning_freq = atol(warnfreq_str);
2835 timelimit = play_warning = warning_freq = 0;
2836 warning_sound =
NULL;
2837 }
else if (play_warning > timelimit) {
2838 if (!warning_freq) {
2841 while (play_warning > timelimit)
2842 play_warning -= warning_freq;
2843 if (play_warning < 1)
2844 play_warning = warning_freq = 0;
2848 ast_verb(3,
"Setting conference duration limit to: %ldms.\n", timelimit);
2850 ast_verb(3,
"Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2853 ast_verb(3,
"Setting warning frequency to %ldms.\n", warning_freq);
2862 warning_sound =
var ?
var :
"timeleft";
2873 calldurationlimit = 0;
2875 if (!play_warning && !end_sound && timelimit) {
2876 calldurationlimit = timelimit / 1000;
2877 timelimit = play_warning = warning_freq = 0;
2879 ast_debug(2,
"Limit Data for this call:\n");
2880 ast_debug(2,
"- timelimit = %ld\n", timelimit);
2881 ast_debug(2,
"- play_warning = %ld\n", play_warning);
2882 ast_debug(2,
"- warning_freq = %ld\n", warning_freq);
2883 ast_debug(2,
"- warning_sound = %s\n", warning_sound ? warning_sound :
"UNDEF");
2884 ast_debug(2,
"- end_sound = %s\n", end_sound ? end_sound :
"UNDEF");
2897 if (!
conf->recordingfilename) {
2907 if (!
conf->recordingfilename) {
2911 if (!
conf->recordingformat) {
2914 ast_verb(4,
"Starting recording of MeetMe Conference %s into file %s.%s.\n",
2915 conf->confno,
conf->recordingfilename,
conf->recordingformat);
2925 dahdic.confno =
conf->dahdiconf;
2926 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2946 time(&
user->jointime);
2948 user->timelimit = timelimit;
2949 user->play_warning = play_warning;
2950 user->warning_freq = warning_freq;
2951 user->warning_sound = warning_sound;
2952 user->end_sound = end_sound;
2954 if (calldurationlimit > 0) {
2955 time(&
user->kicktime);
2956 user->kicktime =
user->kicktime + calldurationlimit;
2961 time_left_ms =
user->timelimit;
2963 if (
user->timelimit) {
2978 if (
conf->users >=
conf->maxusers) {
2994 user->userflags = *confflags;
3026 snprintf(
user->namerecloc,
sizeof(
user->namerecloc),
3031 snprintf(
user->namerecloc,
sizeof(
user->namerecloc),
3032 "%s/meetme-username-%s-%d", destdir,
3038 snprintf(
user->namerecloc,
sizeof(
user->namerecloc),
3039 "%s/meetme-username-%s-%d", destdir,
3056 conf->markedusers++;
3060 snprintf(members,
sizeof(members),
"%d",
conf->users);
3070 if (
conf->users == 1)
3106 int keepplaying = 1;
3108 if (
conf->users == 2) {
3165 user->dahdichannel = !retrydahdi;
3171 fd = open(
"/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
3178 memset(&bi, 0,
sizeof(bi));
3180 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
3181 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
3183 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
3189 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
3200 memset(&dahdic, 0,
sizeof(dahdic));
3201 memset(&dahdic_empty, 0,
sizeof(dahdic_empty));
3204 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
3209 if (dahdic.confmode) {
3212 ast_debug(1,
"DAHDI channel is in a conference already, retrying with pseudo\n");
3217 memset(&dahdic, 0,
sizeof(dahdic));
3220 dahdic.confno =
conf->dahdiconf;
3248 dahdic.confmode = DAHDI_CONF_CONF;
3250 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3252 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3254 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3256 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3273 (
conf->markedusers >= 1))) {
3300 if (
user->dahdichannel) {
3308 ret =
pbx_exec(chan, agi_app, agifile);
3313 if (
user->dahdichannel) {
3319 int lastusers =
conf->users;
3327 int menu_was_active = 0;
3334 char currenttime[32];
3335 long localendtime = 0;
3341 if (now.tv_sec % 60 == 0) {
3346 conf->confno,
"starttime <=", currenttime,
3347 "endtime >=", currenttime,
NULL);
3350 if (!strcasecmp(
var->name,
"endtime")) {
3351 struct ast_tm endtime_tm;
3354 localendtime =
tmp.tv_sec;
3361 if (localendtime >
conf->endtime){
3362 conf->endtime = localendtime;
3366 if (
conf->endtime && (now.tv_sec >=
conf->endtime)) {
3371 if (!announcement_played &&
conf->endalert) {
3372 if (now.tv_sec +
conf->endalert >=
conf->endtime) {
3381 announcement_played = 1;
3386 announcement_played = 0;
3396 if (
user->kicktime && (
user->kicktime <= now.tv_sec)) {
3406 if (
user->timelimit) {
3407 int minutes = 0, seconds = 0, remain = 0;
3414 if (time_left_ms < to) {
3418 if (time_left_ms <= 0) {
3419 if (
user->end_sound) {
3432 if (time_left_ms >= 5000) {
3434 remain = (time_left_ms + 500) / 1000;
3435 if (remain / 60 >= 1) {
3436 minutes = remain / 60;
3437 seconds = remain % 60;
3443 if (
user->warning_sound &&
user->play_warning) {
3444 if (!strcmp(
user->warning_sound,
"timeleft")) {
3467 if (
user->warning_freq) {
3476 if (timeout && now.tv_sec >= timeout) {
3488 if (!menu_mode && menu_was_active &&
user->listen.desired && !
user->listen.actual) {
3492 menu_was_active = menu_mode;
3494 currentmarked =
conf->markedusers;
3499 if (currentmarked == 1 &&
conf->users > 1) {
3501 if (
conf->users - 1 == 1) {
3519 user->userflags = *confflags;
3522 if (currentmarked == 0) {
3523 if (lastmarked != 0) {
3535 dahdic.confmode = DAHDI_CONF_CONF;
3536 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3547 }
else if (currentmarked >= 1 && lastmarked == 0) {
3551 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
3553 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
3555 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
3557 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3578 if (
conf->users == 1) {
3602 if (
conf->users != lastusers) {
3603 if (
conf->users < lastusers) {
3606 lastusers =
conf->users;
3614 dahdic.confmode ^= DAHDI_CONF_TALKER;
3615 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3631 dahdic.confmode |= DAHDI_CONF_TALKER;
3632 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3644 talkreq_manager = 1;
3651 talkreq_manager = 0;
3680 char dtmfstr[2] =
"";
3688 ast_debug(1,
"Ooh, something swapped out under us, starting over\n");
3690 user->dahdichannel = !retrydahdi;
3707 if (
user->talk.actual) {
3712 if (
user->talking == -1) {
3747 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
3757 if (!menu_mode &&
user->talk.desired && !
user->talk.actual) {
3763 }
else if (!menu_mode) {
3767 menu_to_play =
"conf-adminmenu-18";
3770 menu_to_play =
"conf-usermenu-162";
3785 chan,
user, recordingtmp,
sizeof(recordingtmp), cap_slin);
3788 if (musiconhold && !menu_mode) {
3793 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3816 ast_debug(2,
"Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr,
exitcontext);
3842 "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
3847 "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
3851 }
else if (outfd > -1) {
3854 memset(&fr, 0,
sizeof(fr));
3861 if (!
user->listen.actual &&
3873 goto bailoutandtrynormal;
3876 if (!
conf->transframe[idx]) {
3877 if (
conf->origframe) {
3885 if (!
conf->transpath[idx]) {
3888 if (
conf->transpath[idx]) {
3890 if (!
conf->transframe[idx]) {
3896 if (
conf->transframe[idx]) {
3916 goto bailoutandtrynormal;
3928 if (
user->listen.actual) {
3943 lastmarked = currentmarked;
3957 dahdic.confmode = 0;
3958 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3998 if (
user->user_no) {
4010 snprintf(members,
sizeof(members),
"%d",
conf->users);
4018 conf->markedusers--;
4035 snprintf(meetmesecs,
sizeof(meetmesecs),
"%d", (
int) (time(
NULL) -
user->jointime));
4054 char *dynamic_pin,
size_t pin_buf_len,
int refcount,
struct ast_flags64 *confflags,
int *too_early,
char **optargs)
4077 char recordingfilename[256] =
"";
4078 char recordingformat[11] =
"";
4079 char currenttime[32] =
"";
4080 char eatime[32] =
"";
4081 char bookid[51] =
"";
4086 struct timeval endtime = { .tv_sec = 0 };
4095 ast_debug(1,
"Looking for conference %s that starts after %s\n", confno, currenttime);
4098 confno,
"starttime <= ", currenttime,
"endtime >= ",
4108 confno,
"starttime <= ", currenttime,
"endtime >= ",
4118 confno,
"starttime <= ", eatime,
"endtime >= ",
4143 if (!strcasecmp(
var->name,
"pin")) {
4145 }
else if (!strcasecmp(
var->name,
"adminpin")) {
4147 }
else if (!strcasecmp(
var->name,
"bookId")) {
4149 }
else if (!strcasecmp(
var->name,
"opts")) {
4151 }
else if (!strcasecmp(
var->name,
"maxusers")) {
4152 maxusers = atoi(
var->value);
4153 }
else if (!strcasecmp(
var->name,
"adminopts")) {
4155 }
else if (!strcasecmp(
var->name,
"recordingfilename")) {
4157 }
else if (!strcasecmp(
var->name,
"recordingformat")) {
4159 }
else if (!strcasecmp(
var->name,
"endtime")) {
4160 struct ast_tm endtime_tm;
4168 cnf =
build_conf(confno, pin ? pin :
"", pinadmin ? pinadmin :
"", make, dynamic, refcount, chan,
NULL);
4175 cnf->
endtime = endtime.tv_sec;
4226 ast_log(
LOG_WARNING,
"No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4230 if (confflags && !cnf->
chan &&
4232 ast_log(
LOG_WARNING,
"No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
4272 if (dynamic_pin[0] ==
'q') {
4295 if (strcasecmp(
var->name,
"conf"))
4316 }
else if (dynamic_pin) {
4320 if (dynamic_pin[0] ==
'q') {
4321 dynamic_pin[0] =
'\0';
4326 if (confflags && !cnf->
chan &&
4329 ast_log(
LOG_WARNING,
"No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
4333 if (confflags && !cnf->
chan &&
4335 ast_log(
LOG_WARNING,
"No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
4368 count =
conf->users;