48 #include <dahdi/user.h>
72 #include "asterisk/stasis_channels.h"
73 #include "asterisk/stasis_message_router.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
683 enum entrance_sound {
688 enum recording_state {
690 MEETME_RECORD_STARTED,
691 MEETME_RECORD_ACTIVE,
692 MEETME_RECORD_TERMINATE
695 #define CONF_SIZE 320
731 CONFFLAG_DYNAMIC = (1 << 16),
732 CONFFLAG_DYNAMICPIN = (1 << 17),
733 CONFFLAG_EMPTY = (1 << 18),
734 CONFFLAG_EMPTYNOPIN = (1 << 19),
735 CONFFLAG_ALWAYSPROMPT = (1 << 20),
750 CONFFLAG_DURATION_STOP = (1 << 27),
751 CONFFLAG_DURATION_LIMIT = (1 << 28),
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)
766 OPT_ARG_WAITMARKED = 0,
767 OPT_ARG_EXITKEYS = 1,
768 OPT_ARG_DURATION_STOP = 2,
769 OPT_ARG_DURATION_LIMIT = 3,
770 OPT_ARG_MOH_CLASS = 4,
771 OPT_ARG_INTROMSG = 5,
772 OPT_ARG_INTROUSER_VMREC = 6,
773 OPT_ARG_ARRAY_SIZE = 7,
812 static const char *
const app =
"MeetMe";
813 static const char *
const app2 =
"MeetMeCount";
814 static const char *
const app3 =
"MeetMeAdmin";
815 static const char *
const app4 =
"MeetMeChannelAdmin";
818 static int rt_schedule;
819 static int fuzzystart;
820 static int earlyalert;
827 #define MAX_CONFNUM 80
829 #define OPTIONS_LEN 100
832 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
846 enum announcetypes announcetype;
886 pthread_t announcethread;
888 unsigned int announcethread_stop:1;
889 ast_cond_t announcelist_addition;
896 static
unsigned int conf_map[1024] = {0, };
912 char namerecloc[PATH_MAX];
915 struct timeval start_time;
964 static void meetme_stasis_cleanup(
void)
966 if (meetme_event_message_router) {
968 meetme_event_message_router = NULL;
979 static int meetme_stasis_init(
void)
989 meetme_event_message_router = stasis_message_router_create(
992 if (!meetme_event_message_router) {
993 meetme_stasis_cleanup();
1001 meetme_stasis_cleanup();
1006 meetme_leave_type(),
1009 meetme_stasis_cleanup();
1017 meetme_stasis_cleanup();
1025 meetme_stasis_cleanup();
1030 meetme_talking_type(),
1033 meetme_stasis_cleanup();
1038 meetme_talk_request_type(),
1041 meetme_stasis_cleanup();
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);
1146 static struct ast_json *status_to_json(
int on)
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
1203 ast_channel_lock(chan);
1207 ast_channel_unlock(chan);
1220 static const char *istalking(
int x)
1225 return "(unmonitored)";
1227 return "(not talking)";
1230 static int careful_write(
int fd,
unsigned char *data,
int len,
int block)
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) {
1245 ast_log(LOG_WARNING,
"Failed to write audio data to conference: %s\n", strerror(errno));
1264 gain_adjust =
gain_map[volume + 5];
1276 gain_adjust =
gain_map[volume + 5];
1281 static void tweak_volume(
struct volume *vol,
enum volume_action action)
1316 static void tweak_talk_volume(
struct ast_conf_user *user,
enum volume_action action)
1318 tweak_volume(&user->talk, action);
1322 if (!set_talk_volume(user, user->talk.
desired))
1328 static void tweak_listen_volume(
struct ast_conf_user *user,
enum volume_action action)
1330 tweak_volume(&user->listen, action);
1334 if (!set_listen_volume(user, user->listen.
desired))
1342 signed char zero_volume = 0;
1350 unsigned char *data;
1355 "Conference: %s\r\n"
1357 ast_channel_name(chan),
1369 len =
sizeof(enter);
1373 len =
sizeof(leave);
1380 careful_write(conf->
fd, data, len, 1);
1389 static int user_no_cmp(
void *obj,
void *arg,
int flags)
1394 if (user->
user_no == *user_no) {
1401 static int user_max_cmp(
void *obj,
void *arg,
int flags)
1406 if (user->
user_no > *max_no) {
1429 const char *pinadmin,
int make,
int dynamic,
int refcount,
1433 struct dahdi_confinfo dahdic = { 0, };
1440 if (!strcmp(confno, cnf->
confno))
1444 if (cnf || (!make && !dynamic) || !cap_slin)
1456 if (!cnf->usercontainer) {
1464 cnf->announcethread = AST_PTHREADT_NULL;
1465 ast_mutex_init(&cnf->announcethreadlock);
1469 ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan),
sizeof(cnf->uniqueid));
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)) {
1480 ast_test_status_update(test,
"Unable to open DAHDI pseudo device\n");
1482 ast_log(LOG_WARNING,
"Unable to open DAHDI pseudo device\n");
1485 ao2_ref(cnf->usercontainer, -1);
1489 ast_mutex_destroy(&cnf->announcethreadlock);
1499 cnf->
chan =
ast_request(
"DAHDI", cap_slin, NULL, chan,
"pseudo", NULL);
1505 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1506 if (ioctl(ast_channel_fd(cnf->
chan, 0), DAHDI_SETCONF, &dahdic)) {
1508 ast_test_status_update(test,
"Error setting conference on pseudo channel\n");
1510 ast_log(LOG_WARNING,
"Error setting conference\n");
1515 ao2_ref(cnf->usercontainer, -1);
1519 ast_mutex_destroy(&cnf->announcethreadlock);
1527 cnf->
start = time(NULL);
1530 ast_verb(3,
"Created MeetMe conference %d for conference '%s'\n", cnf->
dahdiconf, cnf->
confno);
1534 if ((sscanf(cnf->
confno,
"%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1535 conf_map[confno_int] = 1;
1538 ao2_cleanup(cap_slin);
1547 static char *complete_confno(
const char *word,
int state)
1552 int len = strlen(word);
1556 if (!strncmp(word, cnf->
confno, len) && ++which > state) {
1566 static char *complete_userno(
struct ast_conference *cnf,
const char *word,
int state)
1573 int len = strlen(word);
1576 for (; (usr = ao2_iterator_next(&iter));
ao2_ref(usr, -1)) {
1577 snprintf(usrno,
sizeof(usrno),
"%d", usr->
user_no);
1578 if (!strncmp(word, usrno, len) && ++which > state) {
1588 static char *complete_meetmecmd_mute_kick(
const char *line,
const char *word,
int pos,
int state)
1591 return complete_confno(word, state);
1594 int len = strlen(word);
1601 if (!strncasecmp(word,
"all", len)) {
1610 strtok_r(myline,
" ", &saved);
1611 strtok_r(NULL,
" ", &saved);
1612 confno = strtok_r(NULL,
" ", &saved);
1616 if (!strcmp(confno, cnf->
confno)) {
1617 ret = complete_userno(cnf, word, state);
1628 static char *complete_meetmecmd_lock(
const char *word,
int pos,
int state)
1631 return complete_confno(word, state);
1636 static char *complete_meetmecmd_list(
const char *line,
const char *word,
int pos,
int state)
1642 if (!strncasecmp(word, STR_CONCISE, len)) {
1649 return complete_confno(word, state);
1651 if (pos == 3 && state == 0) {
1658 strtok_r(myline,
" ", &saved);
1659 strtok_r(NULL,
" ", &saved);
1660 confno = strtok_r(NULL,
" ", &saved);
1662 if (!strcasecmp(confno, STR_CONCISE)) {
1668 if (!strncasecmp(word, STR_CONCISE, len)) {
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";
1694 return complete_meetmecmd_list(a->line, a->word, a->pos, a->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");
1713 ast_free(marked_users);
1717 ast_cli(a->fd, MC_HEADER_FORMAT,
"Conf Num",
"Parties",
"Marked",
"Activity",
"Creation",
"Locked");
1720 hr = (now - cnf->
start) / 3600;
1721 min = ((now - cnf->
start) % 3600) / 60;
1722 sec = (now - cnf->
start) % 60;
1729 ast_cli(a->fd, MC_DATA_FORMAT, cnf->
confno, cnf->
users,
1733 ast_cli(a->fd,
"%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1742 total += cnf->
users;
1746 ast_cli(a->fd,
"* Total number of MeetMe users: %d\n", total);
1748 ast_free(marked_users);
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]);
1778 while((user = ao2_iterator_next(&user_iter))) {
1779 hr = (now - user->
jointime) / 3600;
1780 min = ((now - user->
jointime) % 3600) / 60;
1783 ast_cli(a->fd,
"User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1787 ast_channel_name(user->
chan),
1792 istalking(user->
talking), hr, min, sec);
1794 ast_cli(a->fd,
"%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1798 ast_channel_name(user->
chan),
1809 ast_cli(a->fd,
"%d users in that conference.\n", cnf->
users);
1814 return CLI_SHOWUSAGE;
1828 if (strcasestr(a->argv[1],
"lock")) {
1829 if (strcasecmp(a->argv[1],
"lock") == 0) {
1836 }
else if (strcasestr(a->argv[1],
"mute")) {
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) {
1866 return CLI_SHOWUSAGE;
1881 e->
command =
"meetme {lock|unlock}";
1883 "Usage: meetme lock|unlock <confno>\n"
1884 " Lock or unlock a conference to new users.\n";
1887 return complete_meetmecmd_lock(a->word, a->pos, a->n);
1891 return CLI_SHOWUSAGE;
1894 return meetme_cmd_helper(a);
1903 "Usage: meetme kick <confno> all|<userno>\n"
1904 " Kick a conference or a user in a conference.\n";
1907 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
1911 return CLI_SHOWUSAGE;
1914 return meetme_cmd_helper(a);
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";
1927 return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
1931 return CLI_SHOWUSAGE;
1934 return meetme_cmd_helper(a);
1938 AST_CLI_DEFINE(meetme_kick_cmd,
"Kick a conference or a user in a conference."),
1939 AST_CLI_DEFINE(meetme_show_cmd,
"List all conferences or a specific conference."),
1940 AST_CLI_DEFINE(meetme_lock_cmd,
"Lock or unlock a conference to new users."),
1941 AST_CLI_DEFINE(meetme_mute_cmd,
"Mute or unmute a conference or a user in a conference."),
1944 static void conf_flush(
int fd,
struct ast_channel *chan)
1967 x = DAHDI_FLUSH_ALL;
1968 if (ioctl(fd, DAHDI_FLUSH, &x))
1969 ast_log(LOG_WARNING,
"Error flushing channel\n");
1983 meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
1985 if (conf->
recording == MEETME_RECORD_ACTIVE) {
1986 conf->
recording = MEETME_RECORD_TERMINATE;
1991 if (conf->
recording == MEETME_RECORD_OFF)
1997 for (x = 0; x < AST_FRAME_BITS; x++) {
1998 if (conf->transframe[x])
1999 ast_frfree(conf->transframe[x]);
2000 if (conf->transpath[x])
2003 if (conf->announcethread != AST_PTHREADT_NULL) {
2004 ast_mutex_lock(&conf->announcelistlock);
2005 conf->announcethread_stop = 1;
2007 ast_cond_signal(&conf->announcelist_addition);
2008 ast_mutex_unlock(&conf->announcelistlock);
2009 pthread_join(conf->announcethread, NULL);
2018 ast_mutex_destroy(&conf->announcelistlock);
2021 if (conf->origframe)
2022 ast_frfree(conf->origframe);
2030 if (conf->usercontainer) {
2031 ao2_ref(conf->usercontainer, -1);
2036 ast_mutex_destroy(&conf->
playlock);
2039 ast_mutex_destroy(&conf->announcethreadlock);
2052 while ((user = ao2_iterator_next(&user_iter))) {
2053 if (user == sender) {
2058 ast_log(LOG_WARNING,
"Error writing frame to channel %s\n", ast_channel_name(user->
chan));
2073 if ((sscanf(conf->
confno,
"%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
2074 conf_map[confno_int] = 0;
2084 static int rt_extend_conf(
const char *confno)
2086 char currenttime[32];
2102 var = ast_load_realtime(
"meetme",
"confno",
2103 confno,
"startTime<= ", currenttime,
2104 "endtime>= ", currenttime, NULL);
2110 if (!strcasecmp(var->
name,
"bookid")) {
2113 if (!strcasecmp(var->
name,
"endtime")) {
2124 now.tv_sec += extendby;
2128 strcat(currenttime,
"0");
2130 var = ast_load_realtime(
"meetme",
"confno",
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);
2146 static void conf_start_moh(
struct ast_channel *chan,
const char *musicclass)
2150 ast_channel_lock(chan);
2151 original_moh =
ast_strdupa(ast_channel_musicclass(chan));
2152 ast_channel_musicclass_set(chan, musicclass);
2153 ast_channel_unlock(chan);
2157 ast_channel_lock(chan);
2158 ast_channel_musicclass_set(chan, original_moh);
2159 ast_channel_unlock(chan);
2162 static const char *get_announce_filename(
enum announcetypes type)
2166 return "conf-hasleft";
2169 return "conf-hasjoin";
2176 static void *announce_thread(
void *data)
2181 char filename[PATH_MAX] =
"";
2185 while (!conf->announcethread_stop) {
2186 ast_mutex_lock(&conf->announcelistlock);
2187 if (conf->announcethread_stop) {
2188 ast_mutex_unlock(&conf->announcelistlock);
2192 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2197 ast_mutex_unlock(&conf->announcelistlock);
2198 if (conf->announcethread_stop) {
2206 if ((current->confchan) && (current->confusers > 1) && !
ast_check_hangup(current->confchan)) {
2210 ast_copy_string(filename, get_announce_filename(current->announcetype),
sizeof(filename));
2211 if (!
ast_streamfile(current->confchan, filename, current->language))
2215 if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2225 if (!current->vmrec) {
2245 meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
2250 int last_talking = user->
talking;
2251 if (last_talking == talking)
2258 int was_talking = (last_talking > 0);
2259 int now_talking = (talking > 0);
2260 if (was_talking != now_talking) {
2261 send_talking_event(chan, conf, user, now_talking);
2266 static int user_set_hangup_cb(
void *obj,
void *check_admin_arg,
int flags)
2277 static int user_set_kickme_cb(
void *obj,
void *check_admin_arg,
int flags)
2288 static int user_set_unmuted_cb(
void *obj,
void *check_admin_arg,
int flags)
2299 static int user_set_muted_cb(
void *obj,
void *check_admin_arg,
int flags)
2314 MENU_ADMIN_EXTENDED,
2331 *menu_mode = MENU_DISABLED;
2338 if (!
ast_streamfile(chan,
"conf-muted", ast_channel_language(chan))) {
2342 if (!
ast_streamfile(chan,
"conf-unmuted", ast_channel_language(chan))) {
2349 *menu_mode = MENU_DISABLED;
2362 tweak_listen_volume(user, VOL_DOWN);
2367 rt_extend_conf(conf->
confno);
2369 *menu_mode = MENU_DISABLED;
2373 tweak_listen_volume(user, VOL_UP);
2377 tweak_talk_volume(user, VOL_DOWN);
2381 *menu_mode = MENU_DISABLED;
2385 tweak_talk_volume(user, VOL_UP);
2389 *menu_mode = MENU_DISABLED;
2390 if (!
ast_streamfile(chan,
"conf-errormenu", ast_channel_language(chan))) {
2411 *menu_mode = MENU_DISABLED;
2420 if (!
ast_streamfile(chan,
"conf-muted", ast_channel_language(chan))) {
2424 if (!
ast_streamfile(chan,
"conf-unmuted", ast_channel_language(chan))) {
2431 *menu_mode = MENU_DISABLED;
2434 if (!
ast_streamfile(chan,
"conf-unlockednow", ast_channel_language(chan))) {
2439 if (!
ast_streamfile(chan,
"conf-lockednow", ast_channel_language(chan))) {
2450 *menu_mode = MENU_DISABLED;
2451 usr = ao2_find(conf->usercontainer, &max_no, 0);
2453 if (!
ast_streamfile(chan,
"conf-errormenu", ast_channel_language(chan))) {
2465 tweak_listen_volume(user, VOL_DOWN);
2471 if (!rt_extend_conf(conf->
confno)) {
2472 if (!
ast_streamfile(chan,
"conf-extended", ast_channel_language(chan))) {
2476 if (!
ast_streamfile(chan,
"conf-nonextended", ast_channel_language(chan))) {
2482 *menu_mode = MENU_DISABLED;
2486 tweak_listen_volume(user, VOL_UP);
2490 tweak_talk_volume(user, VOL_DOWN);
2494 if (!
ast_streamfile(chan,
"conf-adminmenu-menu8", ast_channel_language(chan))) {
2499 *menu_mode = MENU_ADMIN_EXTENDED;
2503 tweak_talk_volume(user, VOL_UP);
2506 *menu_mode = MENU_DISABLED;
2508 if (!
ast_streamfile(chan,
"conf-errormenu", ast_channel_language(chan))) {
2528 static void meetme_menu_admin_extended(
enum menu_modes *menu_mode,
int *dtmf,
2530 struct ast_conf_user *user,
char *recordingtmp,
int recordingtmp_size,
2543 if (conf->
users == 1) {
2544 if (keepplaying && !
ast_streamfile(chan,
"conf-onlyperson", ast_channel_language(chan))) {
2551 }
else if (conf->
users == 2) {
2552 if (keepplaying && !
ast_streamfile(chan,
"conf-onlyone", ast_channel_language(chan))) {
2560 if (keepplaying && !
ast_streamfile(chan,
"conf-thereare", ast_channel_language(chan))) {
2568 res =
ast_say_number(chan, conf->
users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (
char *) NULL);
2574 if (keepplaying && !
ast_streamfile(chan,
"conf-otherinparty", ast_channel_language(chan))) {
2583 while((usr = ao2_iterator_next(&user_iter))) {
2597 if (keepplaying && playednamerec && !
ast_streamfile(chan,
"conf-roll-callcomplete", ast_channel_language(chan))) {
2605 *menu_mode = MENU_DISABLED;
2609 if (conf->
users == 1) {
2610 if(!
ast_streamfile(chan,
"conf-errormenu", ast_channel_language(chan))) {
2617 *menu_mode = MENU_DISABLED;
2624 if (!
ast_streamfile(chan,
"conf-now-unmuted", ast_channel_language(chan))) {
2630 if (!
ast_streamfile(chan,
"conf-now-muted", ast_channel_language(chan))) {
2635 *menu_mode = MENU_DISABLED;
2639 if (conf->
recording != MEETME_RECORD_ACTIVE) {
2643 ast_channel_lock(chan);
2650 ast_channel_unlock(chan);
2652 snprintf(recordingtmp, recordingtmp_size,
"meetme-conf-rec-%s-%s", conf->
confno, ast_channel_uniqueid(chan));
2658 ast_verb(4,
"Starting recording of MeetMe Conference %s into file %s.%s.\n",
2664 struct dahdi_confinfo dahdic;
2670 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2671 if (ioctl(ast_channel_fd(conf->
lchan, 0), DAHDI_SETCONF, &dahdic)) {
2672 ast_log(LOG_WARNING,
"Error starting listen channel\n");
2676 ast_pthread_create_detached_background(&conf->
recordthread, NULL, recordthread, conf);
2680 if (!
ast_streamfile(chan,
"conf-now-recording", ast_channel_language(chan))) {
2686 *menu_mode = MENU_DISABLED;
2691 *menu_mode = MENU_DISABLED;
2695 if (!
ast_streamfile(chan,
"conf-errormenu", ast_channel_language(chan))) {
2699 *menu_mode = MENU_DISABLED;
2716 static void meetme_menu(
enum menu_modes *menu_mode,
int *dtmf,
2718 struct ast_conf_user *user,
char *recordingtmp,
int recordingtmp_size,
2721 switch (*menu_mode) {
2725 meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
2728 meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
2730 if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
2733 case MENU_ADMIN_EXTENDED:
2734 meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
2735 recordingtmp, recordingtmp_size, cap_slin);
2744 struct dahdi_confinfo dahdic, dahdic_empty;
2754 int musiconhold = 0, mohtempstopped = 0;
2757 int currentmarked = 0;
2760 enum menu_modes menu_mode = MENU_DISABLED;
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;
2794 char *mailbox, *context;
2798 goto conf_run_cleanup;
2802 if (!(user = ao2_alloc(
sizeof(*user), NULL))) {
2803 goto conf_run_cleanup;
2808 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2809 (sscanf(optargs[OPT_ARG_WAITMARKED],
"%30d", &opt_waitmarked_timeout) == 1) &&
2810 (opt_waitmarked_timeout > 0)) {
2811 timeout = time(NULL) + opt_waitmarked_timeout;
2814 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2815 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2816 ast_verb(3,
"Setting call duration limit to %u seconds.\n", calldurationlimit);
2819 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2820 char *limit_str, *warning_str, *warnfreq_str;
2823 parse = optargs[OPT_ARG_DURATION_LIMIT];
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);
2856 ast_channel_lock(chan);
2860 ast_channel_unlock(chan);
2862 warning_sound = var ? var :
"timeleft";
2864 ast_channel_lock(chan);
2868 ast_channel_unlock(chan);
2870 end_sound = var ? var : NULL;
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");
2890 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2891 exitkeys =
ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2899 ast_channel_lock(chan);
2906 ast_channel_unlock(chan);
2908 snprintf(recordingtmp,
sizeof(recordingtmp),
"meetme-conf-rec-%s-%s", conf->
confno, ast_channel_uniqueid(chan));
2914 ast_verb(4,
"Starting recording of MeetMe Conference %s into file %s.%s.\n",
2921 ((conf->
lchan =
ast_request(
"DAHDI", cap_slin, NULL, chan,
"pseudo", NULL)))) {
2926 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2927 if (ioctl(ast_channel_fd(conf->
lchan, 0), DAHDI_SETCONF, &dahdic)) {
2928 ast_log(LOG_WARNING,
"Error starting listen channel\n");
2932 ast_pthread_create_detached_background(&conf->
recordthread, NULL, recordthread, conf);
2937 ast_mutex_lock(&conf->announcethreadlock);
2938 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags,
CONFFLAG_QUIET) &&
2940 ast_mutex_init(&conf->announcelistlock);
2942 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2944 ast_mutex_unlock(&conf->announcethreadlock);
2954 if (calldurationlimit > 0) {
2970 if (!
ast_streamfile(chan,
"conf-locked", ast_channel_language(chan)))
2977 if (rt_schedule && conf->
maxusers) {
2981 if (!
ast_streamfile(chan,
"conf-full", ast_channel_language(chan)))
2987 ao2_lock(conf->usercontainer);
2990 ao2_link(conf->usercontainer, user);
2991 ao2_unlock(conf->usercontainer);
3004 char destdir[PATH_MAX];
3006 snprintf(destdir,
sizeof(destdir),
"%s/meetme", ast_config_AST_SPOOL_DIR);
3009 ast_log(LOG_WARNING,
"mkdir '%s' failed: %s\n", destdir, strerror(errno));
3013 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3014 context =
ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
3015 mailbox = strsep(&context,
"@");
3017 if (ast_strlen_zero(mailbox)) {
3019 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
3020 ast_log(LOG_WARNING,
"You must specify a mailbox in the v() option\n");
3022 if (ast_strlen_zero(context)) {
3023 context =
"default";
3027 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
3032 "%s/meetme-username-%s-%d", destdir,
3034 ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
3039 "%s/meetme-username-%s-%d", destdir,
3058 if (rt_log_members) {
3060 snprintf(members,
sizeof(members),
"%d", conf->
users);
3062 "confno", strlen(conf->
confno) > 7 ? RQ_UINTEGER4 : strlen(conf->
confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->
confno),
3063 "members", RQ_UINTEGER1, strlen(members),
3070 if (conf->
users == 1)
3079 ast_channel_lock(chan);
3083 ast_copy_string(exitcontext, ast_channel_context(chan),
sizeof(exitcontext));
3085 ast_channel_unlock(chan);
3090 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
3091 if (!
ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
3098 if (!
ast_streamfile(chan,
"conf-onlyperson", ast_channel_language(chan)))
3101 if (!
ast_streamfile(chan,
"conf-waitforleader", ast_channel_language(chan)))
3106 int keepplaying = 1;
3108 if (conf->
users == 2) {
3109 if (!
ast_streamfile(chan,
"conf-onlyone", ast_channel_language(chan))) {
3118 if (!
ast_streamfile(chan,
"conf-thereare", ast_channel_language(chan))) {
3127 res =
ast_say_number(chan, conf->
users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (
char *) NULL);
3133 if (keepplaying && !
ast_streamfile(chan,
"conf-otherinparty", ast_channel_language(chan))) {
3150 ast_log(LOG_WARNING,
"Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
3155 ast_log(LOG_WARNING,
"Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
3164 retrydahdi = (strcasecmp(
ast_channel_tech(chan)->type,
"DAHDI") || (ast_channel_audiohooks(chan)) ? 1 : 0);
3168 origfd = ast_channel_fd(chan, 0);
3171 fd = open(
"/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
3173 ast_log(LOG_WARNING,
"Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
3178 memset(&bi, 0,
sizeof(bi));
3179 bi.bufsize = CONF_SIZE / 2;
3180 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
3181 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
3183 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
3184 ast_log(LOG_WARNING,
"Unable to set buffering information: %s\n", strerror(errno));
3189 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
3190 ast_log(LOG_WARNING,
"Unable to set linear mode: %s\n", strerror(errno));
3197 fd = ast_channel_fd(chan, 0);
3200 memset(&dahdic, 0,
sizeof(dahdic));
3201 memset(&dahdic_empty, 0,
sizeof(dahdic_empty));
3204 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
3205 ast_log(LOG_WARNING,
"Error getting conference\n");
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));
3225 if (!(item = ao2_alloc(
sizeof(*item), NULL)))
3228 ast_copy_string(item->language, ast_channel_language(chan),
sizeof(item->language));
3229 item->confchan = conf->
chan;
3230 item->confusers = conf->
users;
3231 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3234 item->announcetype = CONF_HASJOIN;
3235 ast_mutex_lock(&conf->announcelistlock);
3238 ast_cond_signal(&conf->announcelist_addition);
3239 ast_mutex_unlock(&conf->announcelistlock);
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)) {
3257 ast_log(LOG_WARNING,
"Error setting conference\n");
3261 ast_debug(1,
"Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->
dahdiconf);
3264 meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL);
3274 conf_play(chan, conf, ENTER);
3278 conf_flush(fd, chan);
3284 ast_log(LOG_WARNING,
"Unable to allocate DSP!\n");
3292 ast_channel_lock(chan);
3298 ast_channel_unlock(chan);
3308 ret =
pbx_exec(chan, agi_app, agifile);
3310 ast_log(LOG_WARNING,
"Could not find application (agi)\n");
3319 int lastusers = conf->
users;
3327 int menu_was_active = 0;
3333 if (rt_schedule && conf->
endtime) {
3334 char currenttime[32];
3335 long localendtime = 0;
3341 if (now.tv_sec % 60 == 0) {
3345 var = origvar = ast_load_realtime(
"meetme",
"confno",
3346 conf->
confno,
"starttime <=", currenttime,
3347 "endtime >=", currenttime, NULL);
3349 for ( ; var; var = var->
next) {
3350 if (!strcasecmp(var->
name,
"endtime")) {
3351 struct ast_tm endtime_tm;
3354 localendtime = tmp.tv_sec;
3361 if (localendtime > conf->
endtime){
3367 ast_verbose(
"Quitting time...\n");
3371 if (!announcement_played && conf->
endalert) {
3373 if (!
ast_streamfile(chan,
"conf-will-end-in", ast_channel_language(chan)))
3376 if (!
ast_streamfile(chan,
"minutes", ast_channel_language(chan)))
3379 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3381 announcement_played = 1;
3386 announcement_played = 0;
3407 int minutes = 0, seconds = 0, remain = 0;
3414 if (time_left_ms < to) {
3418 if (time_left_ms <= 0) {
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;
3446 res =
ast_streamfile(chan,
"vm-youhave", ast_channel_language(chan));
3449 res =
ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (
char *) NULL);
3450 res =
ast_streamfile(chan,
"queue-minutes", ast_channel_language(chan));
3454 res =
ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (
char *) NULL);
3455 res =
ast_streamfile(chan,
"queue-seconds", ast_channel_language(chan));
3463 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3476 if (timeout && now.tv_sec >= timeout) {
3488 if (!menu_mode && menu_was_active && user->listen.
desired && !user->listen.
actual) {
3489 set_talk_volume(user, user->listen.
desired);
3492 menu_was_active = menu_mode;
3499 if (currentmarked == 1 && conf->
users > 1) {
3500 ast_say_number(chan, conf->
users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (
char *) NULL);
3501 if (conf->
users - 1 == 1) {
3502 if (!
ast_streamfile(chan,
"conf-userwilljoin", ast_channel_language(chan))) {
3506 if (!
ast_streamfile(chan,
"conf-userswilljoin", ast_channel_language(chan))) {
3512 if (!
ast_streamfile(chan,
"conf-onlyperson", ast_channel_language(chan))) {
3522 if (currentmarked == 0) {
3523 if (lastmarked != 0) {
3525 if (!
ast_streamfile(chan,
"conf-leaderhasleft", ast_channel_language(chan))) {
3535 dahdic.confmode = DAHDI_CONF_CONF;
3536 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3537 ast_log(LOG_WARNING,
"Error setting conference\n");
3543 if (!musiconhold && (ast_test_flag64(confflags,
CONFFLAG_MOH))) {
3544 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
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)) {
3558 ast_log(LOG_WARNING,
"Error setting conference\n");
3562 if (musiconhold && (ast_test_flag64(confflags,
CONFFLAG_MOH))) {
3568 if (!
ast_streamfile(chan,
"conf-placeintoconf", ast_channel_language(chan))) {
3571 conf_play(chan, conf, ENTER);
3578 if (conf->
users == 1) {
3580 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3592 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags,
CONFFLAG_MARKEDEXIT)) {
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)) {
3616 ast_log(LOG_WARNING,
"Error setting conference - Un/Mute \n");
3625 meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
3631 dahdic.confmode |= DAHDI_CONF_TALKER;
3632 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3633 ast_log(LOG_WARNING,
"Error setting conference - Un/Mute \n");
3637 meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
3644 talkreq_manager = 1;
3645 meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
3651 talkreq_manager = 0;
3652 meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
3665 !
ast_streamfile(chan,
"conf-kicked", ast_channel_language(chan))) {
3680 char dtmfstr[2] =
"";
3682 if (ast_channel_fd(c, 0) != origfd || (user->
dahdichannel && ast_channel_audiohooks(c))) {
3688 ast_debug(1,
"Ooh, something swapped out under us, starting over\n");
3689 retrydahdi = (strcasecmp(
ast_channel_tech(c)->type,
"DAHDI") || ast_channel_audiohooks(c) ? 1 : 0);
3717 if (!user->
talking && totalsilence < MEETME_DELAYDETECTTALK) {
3721 if (user->
talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
3744 conf_queue_dtmf(conf, user, f);
3747 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
3748 ast_log(LOG_WARNING,
"Error setting conference\n");
3757 if (!menu_mode && user->talk.
desired && !user->talk.
actual) {
3758 set_talk_volume(user, 0);
3763 }
else if (!menu_mode) {
3766 menu_mode = MENU_ADMIN;
3767 menu_to_play =
"conf-adminmenu-18";
3769 menu_mode = MENU_NORMAL;
3770 menu_to_play =
"conf-usermenu-162";
3773 if (!
ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
3784 meetme_menu(&menu_mode, &dtmf, conf, confflags,
3785 chan, user, recordingtmp,
sizeof(recordingtmp), cap_slin);
3788 if (musiconhold && !menu_mode) {
3789 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3793 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3794 ast_log(LOG_WARNING,
"Error setting conference\n");
3800 conf_flush(fd, chan);
3807 conf_queue_dtmf(conf, user, f);
3811 ast_debug(1,
"Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
3816 ast_debug(2,
"Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
3823 conf_queue_dtmf(conf, user, f);
3830 conf_queue_dtmf(conf, user, f);
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) {
3852 res = read(outfd, buf, CONF_SIZE);
3854 memset(&fr, 0,
sizeof(fr));
3858 fr.samples = res / 2;
3861 if (!user->listen.
actual &&
3867 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
3872 if (idx >= AST_FRAME_BITS) {
3873 goto bailoutandtrynormal;
3876 if (!conf->transframe[idx]) {
3877 if (conf->origframe) {
3881 && confsilence < MEETME_DELAYDETECTTALK) {
3885 if (!conf->transpath[idx]) {
3888 if (conf->transpath[idx]) {
3889 conf->transframe[idx] =
ast_translate(conf->transpath[idx], conf->origframe, 0);
3890 if (!conf->transframe[idx]) {
3896 if (conf->transframe[idx]) {
3898 can_write(chan, confflags)) {
3905 ast_log(LOG_WARNING,
"Unable to write frame to channel %s\n", ast_channel_name(chan));
3909 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
3911 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3916 goto bailoutandtrynormal;
3920 bailoutandtrynormal:
3924 && confsilence < MEETME_DELAYDETECTTALK) {
3928 if (user->listen.
actual) {
3931 if (can_write(chan, confflags) &&
ast_write(chan, &fr) < 0) {
3932 ast_log(LOG_WARNING,
"Unable to write frame to channel %s\n", ast_channel_name(chan));
3934 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
3936 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
3940 ast_log(LOG_WARNING,
"Failed to read frame: %s\n", strerror(errno));
3943 lastmarked = currentmarked;
3957 dahdic.confmode = 0;
3958 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3959 ast_log(LOG_WARNING,
"Error setting conference\n");
3963 reset_volumes(user);
3967 conf_play(chan, conf, LEAVE);
3972 if (!(item = ao2_alloc(
sizeof(*item), NULL)))
3975 ast_copy_string(item->language, ast_channel_language(chan),
sizeof(item->language));
3976 item->confchan = conf->
chan;
3977 item->confusers = conf->
users;
3978 item->announcetype = CONF_HASLEFT;
3979 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
3982 ast_mutex_lock(&conf->announcelistlock);
3984 ast_cond_signal(&conf->announcelist_addition);
3985 ast_mutex_unlock(&conf->announcelistlock);
4003 meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL);
4008 if (rt_log_members) {
4010 snprintf(members,
sizeof(members),
"%d", conf->
users);
4012 "confno", strlen(conf->
confno) > 7 ? RQ_UINTEGER4 : strlen(conf->
confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->
confno),
4013 "members", RQ_UINTEGER1, strlen(members),
4031 ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
4035 snprintf(meetmesecs,
sizeof(meetmesecs),
"%d", (
int) (time(NULL) - user->
jointime));
4048 ao2_cleanup(cap_slin);
4054 char *dynamic_pin,
size_t pin_buf_len,
int refcount,
struct ast_flags64 *confflags,
int *too_early,
char **optargs)
4064 if (!strcmp(confno, cnf->
confno)) {
4074 char *pin = NULL, *pinadmin = NULL;
4077 char recordingfilename[256] =
"";
4078 char recordingformat[11] =
"";
4079 char currenttime[32] =
"";
4080 char eatime[32] =
"";
4081 char bookid[51] =
"";
4083 char useropts[OPTIONS_LEN + 1] =
"";
4084 char adminopts[OPTIONS_LEN + 1] =
"";
4086 struct timeval endtime = { .tv_sec = 0 };
4095 ast_debug(1,
"Looking for conference %s that starts after %s\n", confno, currenttime);
4097 var = ast_load_realtime(
"meetme",
"confno",
4098 confno,
"starttime <= ", currenttime,
"endtime >= ",
4101 if (!var && fuzzystart) {
4103 now.tv_sec += fuzzystart;
4107 var = ast_load_realtime(
"meetme",
"confno",
4108 confno,
"starttime <= ", currenttime,
"endtime >= ",
4112 if (!var && earlyalert) {
4114 now.tv_sec += earlyalert;
4117 var = ast_load_realtime(
"meetme",
"confno",
4118 confno,
"starttime <= ", eatime,
"endtime >= ",
4126 var = ast_load_realtime(
"meetme",
"confno", confno, NULL);
4133 if (rt_schedule && *too_early) {
4135 if (!
ast_streamfile(chan,
"conf-has-not-started", ast_channel_language(chan))) {
4142 for (origvar = var; var; var = var->
next) {
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;
4179 if (!ast_strlen_zero(recordingfilename)) {
4182 if (!ast_strlen_zero(recordingformat)) {
4189 ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
4192 if (ast_strlen_zero(recordingfilename)) {
4193 ast_channel_lock(chan);
4198 ast_channel_unlock(chan);
4200 snprintf(recordingtmp,
sizeof(recordingtmp),
"meetme-conf-rec-%s-%s", cnf->
confno, ast_channel_uniqueid(chan));
4206 ast_channel_lock(chan);
4211 ast_channel_unlock(chan);
4223 if (confflags->flags && !cnf->
chan &&
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");
4241 char *dynamic_pin,
size_t pin_buf_len,
int refcount,
struct ast_flags64 *confflags)
4255 ast_debug(1,
"The requested confno is '%s'?\n", confno);
4259 if (!strcmp(confno, cnf->
confno))
4270 ast_debug(1,
"Building dynamic conference '%s'\n", confno);
4272 if (dynamic_pin[0] ==
'q') {
4274 if (
ast_app_getdata(chan,
"conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
4277 cnf =
build_conf(confno, dynamic_pin,
"", make, dynamic, refcount, chan, NULL);
4279 cnf =
build_conf(confno,
"",
"", make, dynamic, refcount, chan, NULL);
4285 ast_log(LOG_WARNING,
"No %s file :(\n", CONFIG_FILE_NAME);
4287 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
4288 ast_log(LOG_ERROR,
"Config file " CONFIG_FILE_NAME
" is in an invalid format. Aborting.\n");
4292 for (var = ast_variable_browse(cfg,
"rooms"); var; var = var->
next) {
4293 char parse[MAX_SETTINGS];
4295 if (strcasecmp(var->
name,
"conf"))
4301 ast_debug(3,
"Will conf %s match %s?\n", confno, args.confno);
4302 if (!strcasecmp(args.confno, confno)) {
4306 S_OR(args.pinadmin,
""),
4307 make, dynamic, refcount, chan, NULL);
4312 ast_log(LOG_WARNING,
"%s isn't a valid conference\n", confno);
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");
4356 if (ast_strlen_zero(data)) {
4357 ast_log(LOG_WARNING,
"MeetMeCount requires an argument (conference number)\n");
4365 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
4368 count = conf->
users;
4374 if (!ast_strlen_zero(args.varname)) {
4376 snprintf(val,
sizeof(val),
"%d", count);
4382 res =
ast_say_number(chan, count,
"", ast_channel_language(chan), (
char *) NULL);
4392 char confno[MAX_CONFNUM] =
"";
4399 int empty = 0, empty_no_pin = 0;
4400 int always_prompt = 0;
4401 const char *notdata;
4402 char *info, the_pin[MAX_PIN] =
"";
4408 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
4410 if (ast_strlen_zero(data)) {
4426 if (ast_strlen_zero(confno)) {
4436 dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
4437 if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
4438 strcpy(the_pin,
"q");
4440 empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
4441 empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
4442 always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
4455 if ((empty_no_pin) || (!dynamic)) {
4457 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
4458 var = ast_variable_browse(cfg,
"rooms");
4460 char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
4461 if (!strcasecmp(var->
name,
"conf")) {
4464 confno_tmp = strsep(&stringp,
"|,");
4469 if (!strcmp(confno_tmp, cnf->
confno)) {
4479 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
4498 const char *confno_tmp = ast_variable_retrieve(cfg, catg,
"confno");
4499 const char *pin_tmp = ast_variable_retrieve(cfg, catg,
"pin");
4500 if (ast_strlen_zero(confno_tmp)) {
4508 if (!strcmp(confno_tmp, cnf->
confno)) {
4517 if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
4533 if (ast_strlen_zero(confno) && dynamic) {
4535 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
4537 snprintf(confno,
sizeof(confno),
"%d", i);
4546 if (ast_strlen_zero(confno)) {
4547 res =
ast_streamfile(chan,
"conf-noempty", ast_channel_language(chan));
4552 if (sscanf(confno,
"%30d", &confno_int) == 1) {
4554 res =
ast_streamfile(chan,
"conf-enteringno", ast_channel_language(chan));
4557 res =
ast_say_digits(chan, confno_int,
"", ast_channel_language(chan));
4561 ast_log(LOG_ERROR,
"Could not scan confno '%s'\n", confno);
4566 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
4568 res =
ast_app_getdata(chan,
"conf-getconfno", confno,
sizeof(confno) - 1, 0);
4576 if (!ast_strlen_zero(confno)) {
4578 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
4579 sizeof(the_pin), 1, &confflags);
4583 cnf = find_conf_realtime(chan, confno, 1, dynamic,
4584 the_pin,
sizeof(the_pin), 1, &confflags, &too_early, optargs);
4585 if (rt_schedule && too_early)
4592 res =
ast_streamfile(chan,
"conf-invalid", ast_channel_language(chan));
4599 int req_pin = !ast_strlen_zero(cnf->
pin) ||
4600 (!ast_strlen_zero(cnf->
pinadmin) &&
4616 not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
4617 not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->
users);
4618 if (req_pin && not_exempt) {
4619 char pin[MAX_PIN] =
"";
4623 for (j = 0; j < 3; j++) {
4624 if (*the_pin && (always_prompt == 0)) {
4631 ast_channel_name(chan));
4632 res =
ast_app_getdata(chan,
"conf-getpin", pin + strlen(pin),
sizeof(pin) - 1 - strlen(pin), 0);
4635 if ((!strcasecmp(pin, cnf->
pin) &&
4638 (!ast_strlen_zero(cnf->
pinadmin) &&
4639 !strcasecmp(pin, cnf->
pinadmin))) {
4648 if (!ast_strlen_zero(cnf->
useropts)) {
4655 res = conf_run(chan, cnf, &confflags, optargs);
4659 if (!
ast_streamfile(chan,
"conf-invalidpin", ast_channel_language(chan))) {
4663 ast_log(LOG_WARNING,
"Couldn't play invalid pin msg!\n");
4683 if (*the_pin && (always_prompt == 0)) {
4694 if (!ast_strlen_zero(cnf->
useropts)) {
4700 res = conf_run(chan, cnf, &confflags, optargs);
4706 }
while (allowretry);
4719 if (conf && callerident && sscanf(callerident,
"%30d", &cid) == 1) {
4720 user = ao2_find(conf->usercontainer, &cid, 0);
4727 static int user_listen_volup_cb(
void *obj,
void *unused,
int flags)
4730 tweak_listen_volume(user, VOL_UP);
4734 static int user_listen_voldown_cb(
void *obj,
void *unused,
int flags)
4737 tweak_listen_volume(user, VOL_DOWN);
4741 static int user_talk_volup_cb(
void *obj,
void *unused,
int flags)
4744 tweak_talk_volume(user, VOL_UP);
4748 static int user_talk_voldown_cb(
void *obj,
void *unused,
int flags)
4751 tweak_talk_volume(user, VOL_DOWN);
4755 static int user_reset_vol_cb(
void *obj,
void *unused,
int flags)
4758 reset_volumes(user);
4762 static int user_chan_cb(
void *obj,
void *args,
int flags)
4765 const char *channel = args;
4767 if (!strcmp(ast_channel_name(user->
chan), channel)) {
4788 if (ast_strlen_zero(data)) {
4789 ast_log(LOG_WARNING,
"MeetMeAdmin requires an argument!\n");
4799 if (!args.command) {
4800 ast_log(LOG_WARNING,
"MeetmeAdmin requires a command!\n");
4809 if (!strcmp(cnf->
confno, args.confno))
4814 ast_log(LOG_WARNING,
"Conference number '%s' not found!\n", args.confno);
4825 user = find_user(cnf, args.user);
4827 ast_log(LOG_NOTICE,
"Specified User not found!\n");
4833 switch (*args.command) {
4843 ast_log(LOG_NOTICE,
"No user specified!\n");
4850 switch (*args.command) {
4866 eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
4869 ast_log(LOG_NOTICE,
"No last user to kick!\n");
4877 ast_log(LOG_NOTICE,
"Not kicking last user, is an Admin!\n");
4912 reset_volumes(user);
4915 tweak_listen_volume(user, VOL_UP);
4918 tweak_listen_volume(user, VOL_DOWN);
4921 tweak_talk_volume(user, VOL_UP);
4924 tweak_talk_volume(user, VOL_DOWN);
4927 if (rt_extend_conf(args.confno)) {
4959 if (ast_strlen_zero(data)) {
4960 ast_log(LOG_WARNING,
"MeetMeChannelAdmin requires two arguments!\n");
4967 if (!args.channel) {
4968 ast_log(LOG_WARNING,
"MeetMeChannelAdmin requires a channel name!\n");
4972 if (!args.command) {
4973 ast_log(LOG_WARNING,
"MeetMeChannelAdmin requires a command!\n");
4979 if ((user =
ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
4985 ast_log(LOG_NOTICE,
"Specified user (%s) not found\n", args.channel);
4991 switch (*args.command) {
5002 ast_log(LOG_WARNING,
"Unknown MeetMeChannelAdmin command '%s'\n", args.command);
5011 static int meetmemute(
struct mansession *s,
const struct message *m,
int mute)
5019 if (ast_strlen_zero(confid)) {
5024 if (ast_strlen_zero(userid)) {
5029 userno = strtoul(userid, &userid, 10);
5039 if (!strcmp(confid, conf->
confno))
5049 user = ao2_find(conf->usercontainer, &userno, 0);
5064 ast_log(LOG_NOTICE,
"Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ?
"" :
"un", conf->
confno, user->
user_no, ast_channel_name(user->
chan), ast_channel_uniqueid(user->
chan));
5071 static int action_meetmemute(
struct mansession *s,
const struct message *m)
5073 return meetmemute(s, m, 1);
5076 static int action_meetmeunmute(
struct mansession *s,
const struct message *m)
5078 return meetmemute(s, m, 0);
5081 static int action_meetmelist(
struct mansession *s,
const struct message *m)
5085 char idText[80] =
"";
5091 if (!ast_strlen_zero(actionid))
5092 snprintf(idText,
sizeof(idText),
"ActionID: %s\r\n", actionid);
5105 if (!ast_strlen_zero(conference) && strcmp(cnf->
confno, conference))
5110 while ((user = ao2_iterator_next(&user_iter))) {
5113 "Event: MeetmeList\r\n"
5115 "Conference: %s\r\n"
5116 "UserNumber: %d\r\n"
5117 "CallerIDNum: %s\r\n"
5118 "CallerIDName: %s\r\n"
5119 "ConnectedLineNum: %s\r\n"
5120 "ConnectedLineName: %s\r\n"
5124 "MarkedUser: %s\r\n"
5135 ast_channel_name(user->
chan),
5140 user->
talking > 0 ?
"Yes" : user->
talking == 0 ?
"No" :
"Not monitored");
5153 static int action_meetmelistrooms(
struct mansession *s,
const struct message *m)
5156 char idText[80] =
"";
5161 char markedusers[5];
5163 if (!ast_strlen_zero(actionid)) {
5164 snprintf(idText,
sizeof(idText),
"ActionID: %s\r\n", actionid);
5182 strcpy(markedusers,
"N/A");
5186 hr = (now - cnf->
start) / 3600;
5187 min = ((now - cnf->
start) % 3600) / 60;
5188 sec = (now - cnf->
start) % 60;
5191 "Event: MeetmeListRooms\r\n"
5193 "Conference: %s\r\n"
5196 "Activity: %2.2d:%2.2d:%2.2d\r\n"
5206 cnf->
locked ?
"Yes" :
"No");
5222 static void filename_parse(
char *filename,
char *buffer)
5225 if (ast_strlen_zero(filename)) {
5226 ast_log(LOG_WARNING,
"No file name was provided for a file save option.\n");
5227 }
else if (filename[0] !=
'/') {
5228 snprintf(buffer, PATH_MAX,
"%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
5234 if ((slash = strrchr(slash,
'/'))) {
5249 const char *oldrecordingfilename = NULL;
5250 char filename_buffer[PATH_MAX];
5252 if (!cnf || !cnf->
lchan) {
5256 filename_buffer[0] =
'\0';
5260 flags = O_CREAT | O_TRUNC | O_WRONLY;
5265 if (cnf->
recording == MEETME_RECORD_TERMINATE) {
5270 if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
5272 oldrecordingfilename = filename_buffer;
5282 for (x = 0; x < AST_FRAME_BITS; x++) {
5284 if (cnf->transframe[x]) {
5285 ast_frfree(cnf->transframe[x]);
5286 cnf->transframe[x] = NULL;
5290 ast_frfree(cnf->origframe);
5317 if (!strcmp(data, conf->
confno))
5332 static void meetme_set_defaults(
void)
5348 static void load_config_meetme(
int reload)
5355 meetme_set_defaults();
5360 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
5361 ast_log(LOG_ERROR,
"Config file " CONFIG_FILE_NAME
" is in an invalid format. Aborting.\n");
5366 meetme_set_defaults();
5369 if ((val = ast_variable_retrieve(cfg,
"general",
"audiobuffers"))) {
5371 ast_log(LOG_WARNING,
"audiobuffers setting must be a number, not '%s'\n", val);
5374 ast_log(LOG_WARNING,
"audiobuffers setting must be between %d and %d\n",
5375 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
5379 ast_log(LOG_NOTICE,
"Audio buffers per channel set to %d\n",
audio_buffers);
5382 if ((val = ast_variable_retrieve(cfg,
"general",
"schedule")))
5384 if ((val = ast_variable_retrieve(cfg,
"general",
"logmembercount")))
5386 if ((val = ast_variable_retrieve(cfg,
"general",
"fuzzystart"))) {
5387 if ((sscanf(val,
"%30d", &fuzzystart) != 1)) {
5388 ast_log(LOG_WARNING,
"fuzzystart must be a number, not '%s'\n", val);
5392 if ((val = ast_variable_retrieve(cfg,
"general",
"earlyalert"))) {
5393 if ((sscanf(val,
"%30d", &earlyalert) != 1)) {
5394 ast_log(LOG_WARNING,
"earlyalert must be a number, not '%s'\n", val);
5398 if ((val = ast_variable_retrieve(cfg,
"general",
"endalert"))) {
5399 if ((sscanf(val,
"%30d", &endalert) != 1)) {
5400 ast_log(LOG_WARNING,
"endalert must be a number, not '%s'\n", val);
5404 if ((val = ast_variable_retrieve(cfg,
"general",
"extendby"))) {
5405 if ((sscanf(val,
"%30d", &extendby) != 1)) {
5406 ast_log(LOG_WARNING,
"extendby must be a number, not '%s'\n", val);
5414 static int acf_meetme_info_eval(
const char *keyword,
const struct ast_conference *conf)
5416 if (!strcasecmp(
"lock", keyword)) {
5418 }
else if (!strcasecmp(
"parties", keyword)) {
5420 }
else if (!strcasecmp(
"activity", keyword)) {
5423 return (now - conf->
start);
5424 }
else if (!strcasecmp(
"dynamic", keyword)) {
5432 static int acf_meetme_info(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
5442 if (ast_strlen_zero(data)) {
5443 ast_log(LOG_ERROR,
"Syntax: MEETME_INFO() requires two arguments\n");
5450 if (ast_strlen_zero(args.keyword)) {
5451 ast_log(LOG_ERROR,
"Syntax: MEETME_INFO() requires a keyword\n");
5455 if (ast_strlen_zero(args.confno)) {
5456 ast_log(LOG_ERROR,
"Syntax: MEETME_INFO() requires a conference number\n");
5462 if (!strcmp(args.confno, conf->
confno)) {
5463 result = acf_meetme_info_eval(args.keyword, conf);
5470 snprintf(buf, len,
"%d", result);
5471 }
else if (result == -1) {
5472 ast_log(LOG_NOTICE,
"Error: invalid keyword: '%s'\n", args.keyword);
5473 snprintf(buf, len,
"0");
5474 }
else if (result == -2) {
5475 ast_log(LOG_NOTICE,
"Error: conference (%s) not found\n", args.confno);
5476 snprintf(buf, len,
"0");
5483 .
name =
"MEETME_INFO",
5484 .read = acf_meetme_info,
5487 static int load_config(
int reload)
5489 load_config_meetme(reload);
5493 static int unload_module(
void)
5512 meetme_stasis_cleanup();
5531 res |= load_config(0);
5533 res |= meetme_stasis_init();
5553 static int reload(
void)
5556 return load_config(1);
5559 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"MeetMe conference bridge",
5560 .support_level = AST_MODULE_SUPPORT_DEPRECATED,
5562 .unload = unload_module,
5565 .optional_modules =
"func_speex",
struct ast_variable * next
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
static int conf_free(struct ast_conference *conf)
Remove the conference from the list and free it.
const char * warning_sound
Main Channel structure associated with a channel.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
ast_device_state
Device States.
#define ast_frdup(fr)
Copies a frame.
char * str
Subscriber phone number (Malloced)
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Asterisk main include file. File version handling, generic pbx functions.
static int dispose_conf(struct ast_conference *conf)
Decrement reference counts, as incremented by find_conf()
struct timeval start_time
struct ast_channel * chan
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
#define AST_OPTION_TXGAIN
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
struct ast_party_id id
Connected party ID.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Support for translation of data formats. translate.c.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_party_name name
Subscriber name.
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
struct ast_channel_snapshot * snapshot
Convenient Signal Processing routines.
int ast_softhangup(struct ast_channel *chan, int cause)
Softly hangup up a channel.
static int conf_exec(struct ast_channel *chan, const char *data)
The meetme() application.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_json_object_update(struct ast_json *object, struct ast_json *other)
Update object with all of the fields of other.
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Structure for variables, used for configurations and for channel variables.
#define CONFFLAG_DONT_DENOISE
static int count_exec(struct ast_channel *chan, const char *data)
The MeetmeCount application.
static int load_module(void)
Load the module.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
int ast_devstate_prov_del(const char *label)
Remove device state provider.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
ast_channel_state
ast_channel states
char * str
Subscriber name (Malloced)
int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
says digits
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
static struct ast_conference * build_conf(const char *confno, const char *pin, const char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan, struct ast_test *test)
Find or create a conference.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
Add device state provider.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
struct ast_frame * ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume)
translates one or more frames Apply an input frame into the translator and receive zero or one output...
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
#define ast_strdup(str)
A wrapper for strdup()
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
Structure used to handle a large number of boolean flags == used only in app_dial?
int ast_unregister_application(const char *app)
Unregister an application.
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Blob of data associated with a channel.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
#define CONFFLAG_INTROMSG
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
All configuration options for http media cache.
int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
Allow to record message and have a review option.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
struct ast_party_id id
Caller party ID.
Configuration File Parser.
#define CONFFLAG_KILL_LAST_MAN_STANDING
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
u-Law to Signed linear conversion
#define ast_config_load(filename, flags)
Load a config file.
struct ast_trans_pvt * ast_translator_build_path(struct ast_format *dest, struct ast_format *source)
Builds a translator path Build a path (possibly NULL) from source to dest.
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Asterisk file paths, configured in asterisk.conf.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Data structure associated with a custom dialplan function.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_MAX_EXTENSION
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
int ast_atomic_dec_and_test(volatile int *p)
decrement *p by 1 and return true if the variable has reached 0.
static enum ast_device_state meetmestate(const char *data)
Callback for devicestate providers.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Core PBX routines and definitions.
char * ast_strptime(const char *s, const char *format, struct ast_tm *tm)
Special version of strptime(3) which places the answer in the common structure ast_tm. Also, unlike strptime(3), ast_strptime() initializes its memory prior to use.
int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define ast_test_suite_event_notify(s, f,...)
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
struct ast_flags64 userflags
#define DEFAULT_AUDIO_BUFFERS
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
Default structure for translators, with the basic fields and buffers, all allocated as part of the sa...
#define AST_OPTION_RXGAIN
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define ao2_unlink(container, obj)
Remove an object from a container.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
enum recording_state recording
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
char namerecloc[PATH_MAX]
static int rt_log_members
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
struct stasis_message * ast_channel_blob_create(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Creates a ast_channel_blob message.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
union ast_frame::@224 data
char namerecloc[PATH_MAX]
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_closestream(struct ast_filestream *f)
Closes a stream.
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
#define CONFFLAG_NO_AUDIO_UNTIL_UP
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
structure to hold users read from users.conf
Structure used to handle boolean flags.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
struct ast_frame ast_null_frame
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
static const char gain_map[]
Map 'volume' levels from -5 through +5 into decibel (dB) settings for channel drivers.
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_sync_message_type)
A message type used to synchronize with the CDR topic.
void stasis_message_router_unsubscribe(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
struct timeval ast_mktime(struct ast_tm *const tmp, const char *zone)
Timezone-independent version of mktime(3).
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
ast_app: A registered application
static int audio_buffers
The number of audio buffers to be allocated on pseudo channels when in a conference.
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
int ast_answer(struct ast_channel *chan)
Answer a channel.
Data structure associated with a single frame of data.
Internal Asterisk hangup causes.
Abstract JSON element (object, array, string, int, ...).
static int admin_exec(struct ast_channel *chan, const char *data)
The MeetMeAdmin application.
#define AST_OPTION_TONE_VERIFY
struct ast_channel_id uniqueid
struct timeval ast_tvsub(struct timeval a, struct timeval b)
Returns the difference of two timevals a - b.
struct ast_channel * lchan
enum ast_frame_type frametype
int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
Adjusts the volume of the audio samples contained in a frame.
unsigned char valid
TRUE if the name information is valid/present.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
The MeetMe Conference object.
struct ast_format * format
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
int ast_str_append_event_header(struct ast_str **fields_string, const char *header, const char *value)
append an event header to an ast string
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
struct ast_frame * ast_read_noaudio(struct ast_channel *chan)
Reads a frame, returning AST_FRAME_NULL frame if audio.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
unsigned char valid
TRUE if the number information is valid/present.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_channel * chan
#define ast_custom_function_register(acf)
Register a custom function.
static int channel_admin_exec(struct ast_channel *chan, const char *data)
The MeetMeChannelAdmin application MeetMeChannelAdmin(channel, command)
int ast_stopstream(struct ast_channel *c)
Stops a stream.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Structure for mutex and tracking information.
void ast_translator_free_path(struct ast_trans_pvt *tr)
Frees a translator path Frees the given translator path structure.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
ast_mutex_t recordthreadlock
#define AST_APP_ARG(name)
Define an application argument.
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
struct ast_party_number number
Subscriber phone number.
#define ao2_link(container, obj)
Add an object to a container.