3 * Asterisk -- An open source telephony toolkit.
5 * Copyright (C) 1999 - 2006, Digium, Inc.
7 * Mark Spencer <markster@digium.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Meet me conference bridge
24 * \author Mark Spencer <markster@digium.com>
26 * \ingroup applications
34 #include <sys/ioctl.h>
36 #include <linux/zaptel.h>
39 #endif /* __linux__ */
43 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45 #include "asterisk/lock.h"
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/config.h"
52 #include "asterisk/app.h"
53 #include "asterisk/dsp.h"
54 #include "asterisk/musiconhold.h"
55 #include "asterisk/manager.h"
56 #include "asterisk/options.h"
57 #include "asterisk/cli.h"
58 #include "asterisk/say.h"
59 #include "asterisk/utils.h"
60 #include "asterisk/translate.h"
61 #include "asterisk/ulaw.h"
64 static const char *app = "MeetMe";
65 static const char *app2 = "MeetMeCount";
66 static const char *app3 = "MeetMeAdmin";
68 static const char *synopsis = "MeetMe conference bridge";
69 static const char *synopsis2 = "MeetMe participant count";
70 static const char *synopsis3 = "MeetMe conference Administration";
72 static const char *descrip =
73 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
74 "conference. If the conference number is omitted, the user will be prompted\n"
75 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
76 "is specified, by pressing '#'.\n"
77 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
78 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
79 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
80 "The option string may contain zero or more of the following characters:\n"
81 " 'a' -- set admin mode\n"
82 " 'A' -- set marked mode\n"
83 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
84 " Default: conf-background.agi (Note: This does not work with\n"
85 " non-Zap channels in the same conference)\n"
86 " 'c' -- announce user(s) count on joining a conference\n"
87 " 'd' -- dynamically add conference\n"
88 " 'D' -- dynamically add conference, prompting for a PIN\n"
89 " 'e' -- select an empty conference\n"
90 " 'E' -- select an empty pinless conference\n"
91 " 'i' -- announce user join/leave\n"
92 " 'm' -- set monitor only mode (Listen only, no talking)\n"
93 " 'M' -- enable music on hold when the conference has a single caller\n"
94 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
95 " being muted, meaning (a) No encode is done on transmission and\n"
96 " (b) Received audio that is not registered as talking is omitted\n"
97 " causing no buildup in background noise\n"
98 " 'p' -- allow user to exit the conference by pressing '#'\n"
99 " 'P' -- always prompt for the pin even if it is specified\n"
100 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
101 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
102 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
103 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
105 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
106 " 't' -- set talk only mode. (Talk only, no listening)\n"
107 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
108 " 'v' -- video mode\n"
109 " 'w' -- wait until the marked user enters the conference\n"
110 " 'x' -- close the conference when last marked user exits\n"
111 " 'X' -- allow user to exit the conference by entering a valid single\n"
112 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
113 " if that variable is not defined.\n"
114 " '1' -- do not play message when first person enters\n";
116 static const char *descrip2 =
117 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
118 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
119 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
120 "the channel, unless priority n+1 exists, in which case priority progress will\n"
122 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
124 static const char *descrip3 =
125 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
126 " 'e' -- Eject last user that joined\n"
127 " 'k' -- Kick one user out of conference\n"
128 " 'K' -- Kick all users out of conference\n"
129 " 'l' -- Unlock conference\n"
130 " 'L' -- Lock conference\n"
131 " 'm' -- Unmute conference\n"
132 " 'M' -- Mute conference\n"
133 " 'n' -- Unmute entire conference (except admin)\n"
134 " 'N' -- Mute entire conference (except admin)\n"
137 #define CONFIG_FILE_NAME "meetme.conf"
141 struct ast_conference {
142 ast_mutex_t playlock; /* Conference specific lock (players) */
143 ast_mutex_t listenlock; /* Conference specific lock (listeners) */
144 char confno[AST_MAX_EXTENSION]; /* Conference */
145 struct ast_channel *chan; /* Announcements channel */
146 struct ast_channel *lchan; /* Listen/Record channel */
147 int fd; /* Announcements fd */
148 int zapconf; /* Zaptel Conf # */
149 int users; /* Number of active users */
150 int markedusers; /* Number of marked users */
151 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
152 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
153 time_t start; /* Start time (s) */
154 int refcount; /* reference count of usage */
155 unsigned int recording:2; /* recording status */
156 unsigned int isdynamic:1; /* Created on the fly? */
157 unsigned int locked:1; /* Is the conference locked? */
158 pthread_t recordthread; /* thread for recording */
159 pthread_attr_t attr; /* thread attribute */
160 const char *recordingfilename; /* Filename to record the Conference into */
161 const char *recordingformat; /* Format to record the Conference in */
162 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
163 char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */
164 struct ast_frame *transframe[32];
165 struct ast_frame *origframe;
166 struct ast_trans_pvt *transpath[32];
167 AST_LIST_ENTRY(ast_conference) list;
170 static AST_LIST_HEAD_STATIC(confs, ast_conference);
173 int desired; /* Desired volume adjustment */
174 int actual; /* Actual volume adjustment (for channels that can't adjust) */
177 struct ast_conf_user {
178 int user_no; /* User Number */
179 struct ast_conf_user *prevuser; /* Pointer to the previous user */
180 struct ast_conf_user *nextuser; /* Pointer to the next user */
181 int userflags; /* Flags as set in the conference */
182 int adminflags; /* Flags set by the Admin */
183 struct ast_channel *chan; /* Connected channel */
184 int talking; /* Is user talking */
185 int zapchannel; /* Is a Zaptel channel */
186 char usrvalue[50]; /* Custom User Value */
187 char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
188 time_t jointime; /* Time the user joined the conference */
190 struct volume listen;
193 static int audio_buffers; /* The number of audio buffers to be allocated on pseudo channels
197 #define DEFAULT_AUDIO_BUFFERS 32 /* each buffer is 20ms, so this is 640ms total */
199 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
200 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
201 #define MEETME_DELAYDETECTTALK 300
202 #define MEETME_DELAYDETECTENDTALK 1000
204 #define AST_FRAME_BITS 32
211 static int admin_exec(struct ast_channel *chan, void *data);
213 static void *recordthread(void *args);
221 #define MEETME_RECORD_OFF 0
222 #define MEETME_RECORD_STARTED 1
223 #define MEETME_RECORD_ACTIVE 2
224 #define MEETME_RECORD_TERMINATE 3
226 #define CONF_SIZE 320
228 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
229 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
230 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
231 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user when '*' is pressed */
232 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
233 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
234 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
235 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
236 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
237 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
238 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
239 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
240 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
241 #define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
242 #define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
243 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
244 #define CONFFLAG_DYNAMIC (1 << 17)
245 #define CONFFLAG_DYNAMICPIN (1 << 18)
246 #define CONFFLAG_EMPTY (1 << 19)
247 #define CONFFLAG_EMPTYNOPIN (1 << 20)
248 #define CONFFLAG_ALWAYSPROMPT (1 << 21)
249 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22) /* If set, when user joins the conference, they will be told the number of users that are already in */
250 #define CONFFLAG_OPTIMIZETALKER (1 << 23) /* If set, treats talking users as muted users */
251 #define CONFFLAG_NOONLYPERSON (1 << 24) /* If set, won't speak the extra prompt when the first person enters the conference */
254 AST_APP_OPTIONS(meetme_opts, {
255 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
256 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
257 AST_APP_OPTION('b', CONFFLAG_AGI ),
258 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
259 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
260 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
261 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
262 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
263 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
264 AST_APP_OPTION('M', CONFFLAG_MOH ),
265 AST_APP_OPTION('m', CONFFLAG_MONITOR ),
266 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
267 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
268 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
269 AST_APP_OPTION('q', CONFFLAG_QUIET ),
270 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
271 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
272 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
273 AST_APP_OPTION('t', CONFFLAG_TALKER ),
274 AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
275 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
276 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
277 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
280 static char *istalking(int x)
285 return "(unmonitored)";
287 return "(not talking)";
290 static int careful_write(int fd, unsigned char *data, int len, int block)
297 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
298 res = ioctl(fd, ZT_IOMUX, &x);
302 res = write(fd, data, len);
304 if (errno != EAGAIN) {
305 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
317 /* Map 'volume' levels from -5 through +5 into
318 decibel (dB) settings for channel drivers
319 Note: these are not a straight linear-to-dB
320 conversion... the numbers have been modified
321 to give the user a better level of adjustability
323 static signed char gain_map[] = {
337 static int set_talk_volume(struct ast_conf_user *user, int volume)
339 signed char gain_adjust;
341 /* attempt to make the adjustment in the channel driver;
342 if successful, don't adjust in the frame reading routine
344 gain_adjust = gain_map[volume + 5];
346 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
349 static int set_listen_volume(struct ast_conf_user *user, int volume)
351 signed char gain_adjust;
353 /* attempt to make the adjustment in the channel driver;
354 if successful, don't adjust in the frame reading routine
356 gain_adjust = gain_map[volume + 5];
358 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
361 static void tweak_volume(struct volume *vol, enum volume_action action)
365 switch (vol->desired) {
380 switch (vol->desired) {
396 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
398 tweak_volume(&user->talk, action);
399 /* attempt to make the adjustment in the channel driver;
400 if successful, don't adjust in the frame reading routine
402 if (!set_talk_volume(user, user->talk.desired))
403 user->talk.actual = 0;
405 user->talk.actual = user->talk.desired;
408 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
410 tweak_volume(&user->listen, action);
411 /* attempt to make the adjustment in the channel driver;
412 if successful, don't adjust in the frame reading routine
414 if (!set_listen_volume(user, user->listen.desired))
415 user->listen.actual = 0;
417 user->listen.actual = user->listen.desired;
420 static void reset_volumes(struct ast_conf_user *user)
422 signed char zero_volume = 0;
424 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
425 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
428 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
434 if (!chan->_softhangup)
435 res = ast_autoservice_start(chan);
437 AST_LIST_LOCK(&confs);
453 careful_write(conf->fd, data, len, 1);
456 AST_LIST_UNLOCK(&confs);
459 ast_autoservice_stop(chan);
462 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
464 struct ast_conference *cnf;
465 struct zt_confinfo ztc;
467 AST_LIST_LOCK(&confs);
469 AST_LIST_TRAVERSE(&confs, cnf, list) {
470 if (!strcmp(confno, cnf->confno))
474 if (!cnf && (make || dynamic)) {
476 if ((cnf = ast_calloc(1, sizeof(*cnf)))) {
477 ast_mutex_init(&cnf->playlock);
478 ast_mutex_init(&cnf->listenlock);
479 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
480 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
481 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
483 cnf->markedusers = 0;
484 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
486 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
487 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
488 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
490 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
491 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
493 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
499 memset(&ztc, 0, sizeof(ztc));
500 /* Setup a new zap conference */
503 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
504 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
505 ast_log(LOG_WARNING, "Error setting conference\n");
507 ast_hangup(cnf->chan);
514 cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
516 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
517 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
519 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
520 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
521 ast_log(LOG_WARNING, "Error setting conference\n");
522 ast_hangup(cnf->lchan);
526 /* Fill the conference struct */
527 cnf->start = time(NULL);
528 cnf->zapconf = ztc.confno;
529 cnf->isdynamic = dynamic ? 1 : 0;
530 cnf->firstuser = NULL;
531 cnf->lastuser = NULL;
533 if (option_verbose > 2)
534 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
535 AST_LIST_INSERT_HEAD(&confs, cnf, list);
540 cnf->refcount += refcount;
542 AST_LIST_UNLOCK(&confs);
546 static int confs_show(int fd, int argc, char **argv)
548 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
550 return RESULT_SUCCESS;
553 static char show_confs_usage[] =
554 "Deprecated! Please use 'meetme' instead.\n";
556 static struct ast_cli_entry cli_show_confs = {
557 { "show", "conferences", NULL }, confs_show,
558 "Show status of conferences", show_confs_usage, NULL };
560 static int conf_cmd(int fd, int argc, char **argv) {
561 /* Process the command */
562 struct ast_conference *cnf;
563 struct ast_conf_user *user;
565 int i = 0, total = 0;
567 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
568 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
569 char cmdline[1024] = "";
572 ast_cli(fd, "Invalid Arguments.\n");
573 /* Check for length so no buffer will overflow... */
574 for (i = 0; i < argc; i++) {
575 if (strlen(argv[i]) > 100)
576 ast_cli(fd, "Invalid Arguments.\n");
579 /* 'MeetMe': List all the conferences */
581 if (AST_LIST_EMPTY(&confs)) {
582 ast_cli(fd, "No active MeetMe conferences.\n");
583 return RESULT_SUCCESS;
585 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
586 AST_LIST_TRAVERSE(&confs, cnf, list) {
587 if (cnf->markedusers == 0)
588 strcpy(cmdline, "N/A ");
590 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
591 hr = (now - cnf->start) / 3600;
592 min = ((now - cnf->start) % 3600) / 60;
593 sec = (now - cnf->start) % 60;
595 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
599 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
600 return RESULT_SUCCESS;
603 return RESULT_SHOWUSAGE;
604 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
605 if (strstr(argv[1], "lock")) {
606 if (strcmp(argv[1], "lock") == 0) {
608 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
611 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
613 } else if (strstr(argv[1], "mute")) {
615 return RESULT_SHOWUSAGE;
616 if (strcmp(argv[1], "mute") == 0) {
618 if (strcmp(argv[3], "all") == 0) {
619 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
621 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
622 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
626 if (strcmp(argv[3], "all") == 0) {
627 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
629 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
630 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
633 } else if (strcmp(argv[1], "kick") == 0) {
635 return RESULT_SHOWUSAGE;
636 if (strcmp(argv[3], "all") == 0) {
638 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
640 /* Kick a single user */
641 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
642 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
644 } else if(strcmp(argv[1], "list") == 0) {
645 /* List all the users in a conference */
646 if (AST_LIST_EMPTY(&confs)) {
647 ast_cli(fd, "No active conferences.\n");
648 return RESULT_SUCCESS;
650 /* Find the right conference */
651 AST_LIST_TRAVERSE(&confs, cnf, list) {
652 if (strcmp(cnf->confno, argv[2]) == 0)
656 ast_cli(fd, "No such conference: %s.\n",argv[2]);
657 return RESULT_SUCCESS;
659 /* Show all the users */
660 for (user = cnf->firstuser; user; user = user->nextuser){
662 hr = (now - user->jointime) / 3600;
663 min = ((now - user->jointime) % 3600) / 60;
664 sec = (now - user->jointime) % 60;
667 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
669 user->chan->cid.cid_num ? user->chan->cid.cid_num : "<unknown>",
670 user->chan->cid.cid_name ? user->chan->cid.cid_name : "<no name>",
672 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
673 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
674 user->adminflags & ADMINFLAG_MUTED ? "(Admn Muted)" : "",
675 istalking(user->talking), hr, min, sec);
677 ast_cli(fd,"%d users in that conference.\n",cnf->users);
679 return RESULT_SUCCESS;
681 return RESULT_SHOWUSAGE;
682 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
683 admin_exec(NULL, cmdline);
688 static char *complete_confcmd(const char *line, const char *word, int pos, int state)
690 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
692 int len = strlen(word);
694 struct ast_conference *cnf = NULL;
695 struct ast_conf_user *usr = NULL;
698 char *myline, *ret = NULL;
700 if (pos == 1) { /* Command */
701 return ast_cli_complete(word, cmds, state);
702 } else if (pos == 2) { /* Conference Number */
703 AST_LIST_LOCK(&confs);
704 AST_LIST_TRAVERSE(&confs, cnf, list) {
705 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
710 ret = ast_strdup(ret); /* dup before releasing the lock */
711 AST_LIST_UNLOCK(&confs);
713 } else if (pos == 3) {
714 /* User Number || Conf Command option*/
715 if (strstr(line, "mute") || strstr(line, "kick")) {
716 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
717 return strdup("all");
719 AST_LIST_LOCK(&confs);
721 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
722 myline = ast_strdupa(line);
723 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
724 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
728 AST_LIST_TRAVERSE(&confs, cnf, list) {
729 if (!strcmp(confno, cnf->confno))
734 /* Search for the user */
735 for (usr = cnf->firstuser; usr; usr = usr->nextuser) {
736 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
737 if (!strncasecmp(word, usrno, len) && ++which > state)
741 AST_LIST_UNLOCK(&confs);
742 return usr ? strdup(usrno) : NULL;
749 static char conf_usage[] =
750 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
751 " Executes a command for the conference or on a conferee\n";
753 static struct ast_cli_entry cli_conf = {
754 {"meetme", NULL, NULL }, conf_cmd,
755 "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
757 static void conf_flush(int fd, struct ast_channel *chan)
761 /* read any frames that may be waiting on the channel
767 /* when no frames are available, this will wait
768 for 1 millisecond maximum
770 while (ast_waitfor(chan, 1)) {
774 else /* channel was hung up or something else happened */
779 /* flush any data sitting in the pseudo channel */
781 if (ioctl(fd, ZT_FLUSH, &x))
782 ast_log(LOG_WARNING, "Error flushing channel\n");
786 /* Remove the conference from the list and free it.
787 We assume that this was called while holding conflock. */
788 static int conf_free(struct ast_conference *conf)
792 AST_LIST_REMOVE(&confs, conf, list);
794 if (conf->recording == MEETME_RECORD_ACTIVE) {
795 conf->recording = MEETME_RECORD_TERMINATE;
796 AST_LIST_UNLOCK(&confs);
798 AST_LIST_LOCK(&confs);
799 if (conf->recording == MEETME_RECORD_OFF)
801 AST_LIST_UNLOCK(&confs);
805 for (x=0;x<AST_FRAME_BITS;x++) {
806 if (conf->transframe[x])
807 ast_frfree(conf->transframe[x]);
808 if (conf->transpath[x])
809 ast_translator_free_path(conf->transpath[x]);
812 ast_frfree(conf->origframe);
814 ast_hangup(conf->lchan);
816 ast_hangup(conf->chan);
825 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
827 struct ast_conf_user *user = NULL;
828 struct ast_conf_user *usr = NULL;
830 struct zt_confinfo ztc, ztc_empty;
832 struct ast_channel *c;
844 int currentmarked = 0;
848 int using_pseudo = 0;
852 struct ast_dsp *dsp=NULL;
855 const char *agifiledefault = "conf-background.agi";
856 char meetmesecs[30] = "";
857 char exitcontext[AST_MAX_CONTEXT] = "";
858 char recordingtmp[AST_MAX_EXTENSION] = "";
859 char members[10] = "";
862 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
863 char *buf = __buf + AST_FRIENDLY_OFFSET;
865 if (!(user = ast_calloc(1, sizeof(*user)))) {
866 AST_LIST_LOCK(&confs);
868 if (!conf->refcount){
871 AST_LIST_UNLOCK(&confs);
875 if (confflags & CONFFLAG_RECORDCONF) {
876 if (!conf->recordingfilename) {
877 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
878 if (!conf->recordingfilename) {
879 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
880 conf->recordingfilename = ast_strdupa(recordingtmp);
882 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
883 if (!conf->recordingformat) {
884 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
885 conf->recordingformat = ast_strdupa(recordingtmp);
887 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
888 conf->confno, conf->recordingfilename, conf->recordingformat);
892 if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
893 pthread_attr_init(&conf->attr);
894 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
895 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
898 time(&user->jointime);
900 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
901 /* Sorry, but this confernce is locked! */
902 if (!ast_streamfile(chan, "conf-locked", chan->language))
903 ast_waitstream(chan, "");
907 if (confflags & CONFFLAG_MARKEDUSER)
910 ast_mutex_lock(&conf->playlock);
911 if (!conf->firstuser) {
912 /* Fill the first new User struct */
914 conf->firstuser = user;
915 conf->lastuser = user;
917 /* Fill the new user struct */
918 user->user_no = conf->lastuser->user_no + 1;
919 user->prevuser = conf->lastuser;
920 if (conf->lastuser->nextuser) {
921 ast_log(LOG_WARNING, "Error in User Management!\n");
922 ast_mutex_unlock(&conf->playlock);
925 conf->lastuser->nextuser = user;
926 conf->lastuser = user;
931 user->userflags = confflags;
932 user->adminflags = 0;
936 snprintf(members, sizeof(members), "%d", conf->users);
937 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
939 ast_mutex_unlock(&conf->playlock);
941 if (confflags & CONFFLAG_EXIT_CONTEXT) {
942 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
943 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
944 else if (!ast_strlen_zero(chan->macrocontext))
945 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
947 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
950 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
951 snprintf(user->namerecloc, sizeof(user->namerecloc),
952 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
953 conf->confno, user->user_no);
954 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
959 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
960 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
961 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
962 ast_waitstream(chan, "");
963 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
964 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
965 ast_waitstream(chan, "");
968 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
971 if (conf->users == 2) {
972 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
973 res = ast_waitstream(chan, AST_DIGIT_ANY);
980 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
981 res = ast_waitstream(chan, AST_DIGIT_ANY);
988 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
994 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
995 res = ast_waitstream(chan, AST_DIGIT_ANY);
1004 ast_indicate(chan, -1);
1006 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1007 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1011 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1012 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1016 retryzap = strcasecmp(chan->tech->type, "Zap");
1017 user->zapchannel = !retryzap;
1020 origfd = chan->fds[0];
1022 fd = open("/dev/zap/pseudo", O_RDWR);
1024 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1028 /* Make non-blocking */
1029 flags = fcntl(fd, F_GETFL);
1031 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1035 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1036 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1040 /* Setup buffering information */
1041 memset(&bi, 0, sizeof(bi));
1042 bi.bufsize = CONF_SIZE/2;
1043 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1044 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1045 bi.numbufs = audio_buffers;
1046 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1047 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1052 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1053 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1059 /* XXX Make sure we're not running on a pseudo channel XXX */
1063 memset(&ztc, 0, sizeof(ztc));
1064 memset(&ztc_empty, 0, sizeof(ztc_empty));
1065 /* Check to see if we're in a conference... */
1067 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1068 ast_log(LOG_WARNING, "Error getting conference\n");
1073 /* Whoa, already in a conference... Retry... */
1075 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1080 memset(&ztc, 0, sizeof(ztc));
1081 /* Add us to the conference */
1083 ztc.confno = conf->zapconf;
1085 ast_mutex_lock(&conf->playlock);
1087 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
1088 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1089 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1090 ast_waitstream(conf->chan, "");
1091 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1092 ast_waitstream(conf->chan, "");
1096 if (confflags & CONFFLAG_MONITOR)
1097 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1098 else if (confflags & CONFFLAG_TALKER)
1099 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1101 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1103 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1104 ast_log(LOG_WARNING, "Error setting conference\n");
1106 ast_mutex_unlock(&conf->playlock);
1109 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1111 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1116 chan->name, chan->uniqueid, conf->confno, user->user_no);
1118 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1120 if (!(confflags & CONFFLAG_QUIET))
1121 if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
1122 conf_play(chan, conf, ENTER);
1125 ast_mutex_unlock(&conf->playlock);
1127 conf_flush(fd, chan);
1129 if (confflags & CONFFLAG_AGI) {
1130 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1131 or use default filename of conf-background.agi */
1133 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1135 agifile = agifiledefault;
1137 if (user->zapchannel) {
1138 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1140 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1142 /* Find a pointer to the agi app and execute the script */
1143 app = pbx_findapp("agi");
1145 char *s = ast_strdupa(agifile);
1146 ret = pbx_exec(chan, app, s);
1148 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1151 if (user->zapchannel) {
1152 /* Remove CONFMUTE mode on Zap channel */
1154 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1157 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1158 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1160 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1162 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1163 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1167 int menu_was_active = 0;
1172 /* if we have just exited from the menu, and the user had a channel-driver
1173 volume adjustment, restore it
1175 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1176 set_talk_volume(user, user->listen.desired);
1178 menu_was_active = menu_active;
1180 currentmarked = conf->markedusers;
1181 if (!(confflags & CONFFLAG_QUIET) &&
1182 (confflags & CONFFLAG_MARKEDUSER) &&
1183 (confflags & CONFFLAG_WAITMARKED) &&
1185 if (currentmarked == 1 && conf->users > 1) {
1186 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1187 if (conf->users - 1 == 1) {
1188 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1189 ast_waitstream(chan, "");
1191 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1192 ast_waitstream(chan, "");
1195 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1196 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1197 ast_waitstream(chan, "");
1200 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1203 /* Update the struct with the actual confflags */
1204 user->userflags = confflags;
1206 if (confflags & CONFFLAG_WAITMARKED) {
1207 if(currentmarked == 0) {
1208 if (lastmarked != 0) {
1209 if (!(confflags & CONFFLAG_QUIET))
1210 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1211 ast_waitstream(chan, "");
1212 if(confflags & CONFFLAG_MARKEDEXIT)
1215 ztc.confmode = ZT_CONF_CONF;
1216 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1217 ast_log(LOG_WARNING, "Error setting conference\n");
1223 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1224 ast_moh_start(chan, NULL);
1227 ztc.confmode = ZT_CONF_CONF;
1228 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1229 ast_log(LOG_WARNING, "Error setting conference\n");
1234 } else if(currentmarked >= 1 && lastmarked == 0) {
1235 if (confflags & CONFFLAG_MONITOR)
1236 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1237 else if (confflags & CONFFLAG_TALKER)
1238 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1240 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1241 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1242 ast_log(LOG_WARNING, "Error setting conference\n");
1246 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1250 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1251 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1252 ast_waitstream(chan, "");
1253 conf_play(chan, conf, ENTER);
1258 /* trying to add moh for single person conf */
1259 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1260 if (conf->users == 1) {
1261 if (musiconhold == 0) {
1262 ast_moh_start(chan, NULL);
1273 /* Leave if the last marked user left */
1274 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1279 /* Check if the admin changed my modes */
1280 if (user->adminflags) {
1281 /* Set the new modes */
1282 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
1283 ztc.confmode ^= ZT_CONF_TALKER;
1284 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1285 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1290 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1291 ztc.confmode |= ZT_CONF_TALKER;
1292 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1293 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1298 if (user->adminflags & ADMINFLAG_KICKME) {
1299 /* You have been kicked. */
1300 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1301 ast_waitstream(chan, "");
1305 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1306 ztc.confmode |= ZT_CONF_TALKER;
1307 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1308 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1315 if (c->fds[0] != origfd) {
1317 /* Kill old pseudo */
1321 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1322 retryzap = strcasecmp(c->tech->type, "Zap");
1323 user->zapchannel = !retryzap;
1326 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & ADMINFLAG_MUTED))
1327 f = ast_read_noaudio(c);
1332 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1333 if (user->talk.actual)
1334 ast_frame_adjust_volume(f, user->talk.actual);
1336 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1339 if (user->talking == -1)
1342 res = ast_dsp_silence(dsp, f, &totalsilence);
1343 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1345 if (confflags & CONFFLAG_MONITORTALKER)
1346 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1351 chan->name, chan->uniqueid, conf->confno, user->user_no);
1353 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1355 if (confflags & CONFFLAG_MONITORTALKER)
1356 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
1361 chan->name, chan->uniqueid, conf->confno, user->user_no);
1365 /* Absolutely do _not_ use careful_write here...
1366 it is important that we read data from the channel
1367 as fast as it arrives, and feed it into the conference.
1368 The buffering in the pseudo channel will take care of any
1369 timing differences, unless they are so drastic as to lose
1370 audio frames (in which case carefully writing would only
1371 have delayed the audio even further).
1373 /* As it turns out, we do want to use careful write. We just
1374 don't want to block, but we do want to at least *try*
1375 to write out all the samples.
1377 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1378 careful_write(fd, f->data, f->datalen, 0);
1380 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1383 tmp[0] = f->subclass;
1385 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1386 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1389 } else if (option_debug > 1)
1390 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1391 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1394 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1395 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1396 ast_log(LOG_WARNING, "Error setting conference\n");
1401 /* if we are entering the menu, and the user has a channel-driver
1402 volume adjustment, clear it
1404 if (!menu_active && user->talk.desired && !user->talk.actual)
1405 set_talk_volume(user, 0);
1410 if ((confflags & CONFFLAG_ADMIN)) {
1414 /* Record this sound! */
1415 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1416 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1417 ast_stopstream(chan);
1424 case '1': /* Un/Mute */
1426 if (ztc.confmode & ZT_CONF_TALKER) {
1427 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1428 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1430 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1431 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1433 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1434 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1438 if (ztc.confmode & ZT_CONF_TALKER) {
1439 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1440 ast_waitstream(chan, "");
1442 if (!ast_streamfile(chan, "conf-muted", chan->language))
1443 ast_waitstream(chan, "");
1446 case '2': /* Un/Lock the Conference */
1450 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1451 ast_waitstream(chan, "");
1454 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1455 ast_waitstream(chan, "");
1458 case '3': /* Eject last user */
1460 usr = conf->lastuser;
1461 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1462 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1463 ast_waitstream(chan, "");
1465 usr->adminflags |= ADMINFLAG_KICKME;
1466 ast_stopstream(chan);
1469 tweak_listen_volume(user, VOL_DOWN);
1472 tweak_listen_volume(user, VOL_UP);
1475 tweak_talk_volume(user, VOL_DOWN);
1481 tweak_talk_volume(user, VOL_UP);
1485 /* Play an error message! */
1486 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1487 ast_waitstream(chan, "");
1495 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1496 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1497 ast_stopstream(chan);
1504 case '1': /* Un/Mute */
1506 if (ztc.confmode & ZT_CONF_TALKER) {
1507 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1508 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1509 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1510 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1511 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1513 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1514 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1518 if (ztc.confmode & ZT_CONF_TALKER) {
1519 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1520 ast_waitstream(chan, "");
1522 if (!ast_streamfile(chan, "conf-muted", chan->language))
1523 ast_waitstream(chan, "");
1527 tweak_listen_volume(user, VOL_DOWN);
1530 tweak_listen_volume(user, VOL_UP);
1533 tweak_talk_volume(user, VOL_DOWN);
1539 tweak_talk_volume(user, VOL_UP);
1543 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1544 ast_waitstream(chan, "");
1550 ast_moh_start(chan, NULL);
1552 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1553 ast_log(LOG_WARNING, "Error setting conference\n");
1555 AST_LIST_UNLOCK(&confs);
1559 conf_flush(fd, chan);
1560 } else if (option_debug) {
1562 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1563 chan->name, f->frametype, f->subclass);
1566 } else if (outfd > -1) {
1567 res = read(outfd, buf, CONF_SIZE);
1569 memset(&fr, 0, sizeof(fr));
1570 fr.frametype = AST_FRAME_VOICE;
1571 fr.subclass = AST_FORMAT_SLINEAR;
1575 fr.offset = AST_FRIENDLY_OFFSET;
1576 if (!user->listen.actual &&
1577 ((confflags & CONFFLAG_MONITOR) ||
1578 (user->adminflags & ADMINFLAG_MUTED) ||
1579 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1582 for (index=0;index<AST_FRAME_BITS;index++)
1583 if (chan->rawwriteformat & (1 << index))
1585 if (index >= AST_FRAME_BITS)
1586 goto bailoutandtrynormal;
1587 ast_mutex_lock(&conf->listenlock);
1588 if (!conf->transframe[index]) {
1589 if (conf->origframe) {
1590 if (!conf->transpath[index])
1591 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1592 if (conf->transpath[index]) {
1593 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1594 if (!conf->transframe[index])
1595 conf->transframe[index] = &ast_null_frame;
1599 if (conf->transframe[index]) {
1600 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1601 if (ast_write(chan, conf->transframe[index]))
1602 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1605 ast_mutex_unlock(&conf->listenlock);
1606 goto bailoutandtrynormal;
1608 ast_mutex_unlock(&conf->listenlock);
1610 bailoutandtrynormal:
1611 if (user->listen.actual)
1612 ast_frame_adjust_volume(&fr, user->listen.actual);
1613 if (ast_write(chan, &fr) < 0) {
1614 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1618 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1620 lastmarked = currentmarked;
1630 /* Take out of conference */
1634 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1635 ast_log(LOG_WARNING, "Error setting conference\n");
1639 reset_volumes(user);
1641 AST_LIST_LOCK(&confs);
1642 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1643 conf_play(chan, conf, LEAVE);
1645 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1646 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1647 if ((conf->chan) && (conf->users > 1)) {
1648 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1649 ast_waitstream(conf->chan, "");
1650 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1651 ast_waitstream(conf->chan, "");
1653 ast_filedelete(user->namerecloc, NULL);
1656 AST_LIST_UNLOCK(&confs);
1659 AST_LIST_LOCK(&confs);
1664 if (user->user_no) { /* Only cleanup users who really joined! */
1666 hr = (now - user->jointime) / 3600;
1667 min = ((now - user->jointime) % 3600) / 60;
1668 sec = (now - user->jointime) % 60;
1670 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1677 "Duration: %02d:%02d:%02d\r\n",
1678 chan->name, chan->uniqueid, conf->confno,
1680 user->chan->cid.cid_num ? user->chan->cid.cid_num :
1682 user->chan->cid.cid_name ? user->chan->cid.cid_name :
1683 "<no name>", hr, min, sec);
1688 snprintf(members, sizeof(members), "%d", conf->users);
1689 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1690 if (confflags & CONFFLAG_MARKEDUSER)
1691 conf->markedusers--;
1693 /* close this one when no more users and no references*/
1694 if (!conf->refcount){
1698 /* Remove the user struct */
1699 if (user == conf->firstuser) {
1700 if (user->nextuser) {
1701 /* There is another entry */
1702 user->nextuser->prevuser = NULL;
1704 /* We are the only entry */
1705 conf->lastuser = NULL;
1707 /* In either case */
1708 conf->firstuser = user->nextuser;
1709 } else if (user == conf->lastuser){
1711 user->prevuser->nextuser = NULL;
1713 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1714 conf->lastuser = user->prevuser;
1717 user->nextuser->prevuser = user->prevuser;
1719 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1721 user->prevuser->nextuser = user->nextuser;
1723 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1726 /* Return the number of seconds the user was in the conf */
1727 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1728 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1731 AST_LIST_UNLOCK(&confs);
1736 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1737 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1739 struct ast_variable *var;
1740 struct ast_conference *cnf;
1742 /* Check first in the conference list */
1743 AST_LIST_LOCK(&confs);
1744 AST_LIST_TRAVERSE(&confs, cnf, list) {
1745 if (!strcmp(confno, cnf->confno))
1749 cnf->refcount += refcount;
1751 AST_LIST_UNLOCK(&confs);
1754 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1756 cnf = ast_calloc(1, sizeof(struct ast_conference));
1758 ast_log(LOG_ERROR, "Out of memory\n");
1762 var = ast_load_realtime("meetme", "confno", confno, NULL);
1764 if (!strcasecmp(var->name, "confno")) {
1765 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1766 } else if (!strcasecmp(var->name, "pin")) {
1767 pin = ast_strdupa(var->value);
1768 } else if (!strcasecmp(var->name, "adminpin")) {
1769 pinadmin = ast_strdupa(var->value);
1773 ast_variables_destroy(var);
1775 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1779 if (confflags && !cnf->chan &&
1780 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1781 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1782 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1783 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1786 if (confflags && !cnf->chan &&
1787 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1788 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1789 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1797 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
1798 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1800 struct ast_config *cfg;
1801 struct ast_variable *var;
1802 struct ast_conference *cnf;
1804 AST_DECLARE_APP_ARGS(args,
1805 AST_APP_ARG(confno);
1807 AST_APP_ARG(pinadmin);
1810 /* Check first in the conference list */
1811 AST_LIST_LOCK(&confs);
1812 AST_LIST_TRAVERSE(&confs, cnf, list) {
1813 if (!strcmp(confno, cnf->confno))
1817 cnf->refcount += refcount;
1819 AST_LIST_UNLOCK(&confs);
1823 /* No need to parse meetme.conf */
1824 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1826 if (dynamic_pin[0] == 'q') {
1827 /* Query the user to enter a PIN */
1828 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
1831 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
1833 cnf = build_conf(confno, "", "", make, dynamic, refcount);
1836 /* Check the config */
1837 cfg = ast_config_load(CONFIG_FILE_NAME);
1839 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
1842 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
1843 if (strcasecmp(var->name, "conf"))
1846 if (!(parse = ast_strdupa(var->value)))
1849 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
1850 if (!strcasecmp(args.confno, confno)) {
1851 /* Bingo it's a valid conference */
1852 cnf = build_conf(args.confno,
1854 S_OR(args.pinadmin, ""),
1855 make, dynamic, refcount);
1860 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1862 ast_config_destroy(cfg);
1864 } else if (dynamic_pin) {
1865 /* Correct for the user selecting 'D' instead of 'd' to have
1866 someone join into a conference that has already been created
1868 if (dynamic_pin[0] == 'q')
1869 dynamic_pin[0] = '\0';
1873 if (confflags && !cnf->chan &&
1874 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1875 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1876 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1877 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1880 if (confflags && !cnf->chan &&
1881 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1882 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1883 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1890 /*! \brief The MeetmeCount application */
1891 static int count_exec(struct ast_channel *chan, void *data)
1893 struct localuser *u;
1895 struct ast_conference *conf;
1899 AST_DECLARE_APP_ARGS(args,
1900 AST_APP_ARG(confno);
1901 AST_APP_ARG(varname);
1904 if (ast_strlen_zero(data)) {
1905 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1911 if (!(localdata = ast_strdupa(data))) {
1912 LOCAL_USER_REMOVE(u);
1916 AST_STANDARD_APP_ARGS(args, localdata);
1918 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
1921 count = conf->users;
1925 if (!ast_strlen_zero(args.varname)){
1926 /* have var so load it and exit */
1927 snprintf(val, sizeof(val), "%d",count);
1928 pbx_builtin_setvar_helper(chan, args.varname, val);
1930 if (chan->_state != AST_STATE_UP)
1932 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1934 LOCAL_USER_REMOVE(u);
1939 /*! \brief The meetme() application */
1940 static int conf_exec(struct ast_channel *chan, void *data)
1943 struct localuser *u;
1944 char confno[AST_MAX_EXTENSION] = "";
1947 struct ast_conference *cnf;
1948 struct ast_flags confflags = {0};
1950 int empty = 0, empty_no_pin = 0;
1951 int always_prompt = 0;
1952 char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
1953 AST_DECLARE_APP_ARGS(args,
1954 AST_APP_ARG(confno);
1955 AST_APP_ARG(options);
1961 if (ast_strlen_zero(data)) {
1968 if (chan->_state != AST_STATE_UP)
1971 info = ast_strdupa(notdata);
1973 AST_STANDARD_APP_ARGS(args, info);
1976 ast_copy_string(confno, args.confno, sizeof(confno));
1977 if (ast_strlen_zero(confno)) {
1983 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
1986 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
1987 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
1988 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
1989 strcpy(the_pin, "q");
1991 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
1992 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
1993 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2000 int i, map[1024] = { 0, };
2001 struct ast_config *cfg;
2002 struct ast_variable *var;
2005 AST_LIST_LOCK(&confs);
2006 AST_LIST_TRAVERSE(&confs, cnf, list) {
2007 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2008 /* Disqualify in use conference */
2009 if (confno_int >= 0 && confno_int < 1024)
2013 AST_LIST_UNLOCK(&confs);
2015 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2016 if ((empty_no_pin) || (!dynamic)) {
2017 cfg = ast_config_load(CONFIG_FILE_NAME);
2019 var = ast_variable_browse(cfg, "rooms");
2021 if (!strcasecmp(var->name, "conf")) {
2022 char *stringp = ast_strdupa(var->value);
2024 char *confno_tmp = strsep(&stringp, "|,");
2026 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2027 if ((confno_int >= 0) && (confno_int < 1024)) {
2028 if (stringp && empty_no_pin) {
2034 /* For static: run through the list and see if this conference is empty */
2035 AST_LIST_LOCK(&confs);
2036 AST_LIST_TRAVERSE(&confs, cnf, list) {
2037 if (!strcmp(confno_tmp, cnf->confno)) {
2038 /* The conference exists, therefore it's not empty */
2043 AST_LIST_UNLOCK(&confs);
2045 /* At this point, we have a confno_tmp (static conference) that is empty */
2046 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2047 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2048 * Case 2: empty_no_pin and pin is blank (but not NULL)
2049 * Case 3: not empty_no_pin
2051 ast_copy_string(confno, confno_tmp, sizeof(confno));
2053 /* XXX the map is not complete (but we do have a confno) */
2061 ast_config_destroy(cfg);
2065 /* Select first conference number not in use */
2066 if (ast_strlen_zero(confno) && dynamic) {
2067 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2069 snprintf(confno, sizeof(confno), "%d", i);
2076 if (ast_strlen_zero(confno)) {
2077 res = ast_streamfile(chan, "conf-noempty", chan->language);
2079 ast_waitstream(chan, "");
2081 if (sscanf(confno, "%d", &confno_int) == 1) {
2082 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2084 ast_waitstream(chan, "");
2085 res = ast_say_digits(chan, confno_int, "", chan->language);
2088 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2093 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2094 /* Prompt user for conference number */
2095 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2097 /* Don't try to validate when we catch an error */
2103 if (!ast_strlen_zero(confno)) {
2104 /* Check the validity of the conference */
2105 cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2107 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2110 res = ast_streamfile(chan, "conf-invalid", chan->language);
2112 ast_waitstream(chan, "");
2117 if ((!ast_strlen_zero(cnf->pin) &&
2118 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2119 (!ast_strlen_zero(cnf->pinadmin) &&
2120 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2121 char pin[AST_MAX_EXTENSION]="";
2124 /* Allow the pin to be retried up to 3 times */
2125 for (j = 0; j < 3; j++) {
2126 if (*the_pin && (always_prompt == 0)) {
2127 ast_copy_string(pin, the_pin, sizeof(pin));
2130 /* Prompt user for pin if pin is required */
2131 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2134 if (!strcasecmp(pin, cnf->pin) ||
2135 (!ast_strlen_zero(cnf->pinadmin) &&
2136 !strcasecmp(pin, cnf->pinadmin))) {
2139 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2140 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2141 /* Run the conference */
2142 res = conf_run(chan, cnf, confflags.flags);
2146 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
2148 ast_waitstream(chan, AST_DIGIT_ANY);
2150 AST_LIST_LOCK(&confs);
2152 if (!cnf->refcount){
2155 AST_LIST_UNLOCK(&confs);
2165 /* failed when getting the pin */
2168 /* see if we need to get rid of the conference */
2169 AST_LIST_LOCK(&confs);
2171 if (!cnf->refcount) {
2174 AST_LIST_UNLOCK(&confs);
2178 /* Don't retry pin with a static pin */
2179 if (*the_pin && (always_prompt==0)) {
2184 /* No pin required */
2187 /* Run the conference */
2188 res = conf_run(chan, cnf, confflags.flags);
2192 } while (allowretry);
2194 LOCAL_USER_REMOVE(u);
2199 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident)
2201 struct ast_conf_user *user = NULL;
2204 sscanf(callerident, "%i", &cid);
2205 if (conf && callerident) {
2206 user = conf->firstuser;
2208 if (cid == user->user_no)
2210 user = user->nextuser;
2216 /*! \brief The MeetMeadmin application */
2217 /* MeetMeAdmin(confno, command, caller) */
2218 static int admin_exec(struct ast_channel *chan, void *data) {
2220 struct ast_conference *cnf;
2221 struct ast_conf_user *user = NULL;
2222 struct localuser *u;
2223 AST_DECLARE_APP_ARGS(args,
2224 AST_APP_ARG(confno);
2225 AST_APP_ARG(command);
2231 AST_LIST_LOCK(&confs);
2232 /* The param has the conference number the user and the command to execute */
2233 if (!ast_strlen_zero(data)) {
2234 params = ast_strdupa((char *) data);
2236 AST_STANDARD_APP_ARGS(args, params);
2238 if (!args.command) {
2239 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2240 AST_LIST_UNLOCK(&confs);
2241 LOCAL_USER_REMOVE(u);
2244 AST_LIST_TRAVERSE(&confs, cnf, list) {
2245 if (!strcmp(cnf->confno, args.confno))
2250 user = find_user(cnf, args.user);
2253 switch((int) (*args.command)) {
2254 case 76: /* L: Lock */
2257 case 108: /* l: Unlock */
2260 case 75: /* K: kick all users*/
2261 user = cnf->firstuser;
2263 user->adminflags |= ADMINFLAG_KICKME;
2264 if (user->nextuser) {
2265 user = user->nextuser;
2271 case 101: /* e: Eject last user*/
2272 user = cnf->lastuser;
2273 if (!(user->userflags & CONFFLAG_ADMIN)) {
2274 user->adminflags |= ADMINFLAG_KICKME;
2277 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2279 case 77: /* M: Mute */
2281 user->adminflags |= ADMINFLAG_MUTED;
2283 ast_log(LOG_NOTICE, "Specified User not found!\n");
2286 case 78: /* N: Mute all users */
2287 user = cnf->firstuser;
2289 if (user && !(user->userflags & CONFFLAG_ADMIN))
2290 user->adminflags |= ADMINFLAG_MUTED;
2291 if (user->nextuser) {
2292 user = user->nextuser;
2298 case 109: /* m: Unmute */
2299 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
2300 user->adminflags ^= ADMINFLAG_MUTED;
2302 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!\n");
2305 case 110: /* n: Unmute all users */
2306 user = cnf->firstuser;
2308 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
2309 user->adminflags ^= ADMINFLAG_MUTED;
2311 if (user->nextuser) {
2312 user = user->nextuser;
2318 case 107: /* k: Kick user */
2320 user->adminflags |= ADMINFLAG_KICKME;
2322 ast_log(LOG_NOTICE, "Specified User not found!");
2327 ast_log(LOG_NOTICE, "Conference Number not found\n");
2330 AST_LIST_UNLOCK(&confs);
2332 LOCAL_USER_REMOVE(u);
2337 static void *recordthread(void *args)
2339 struct ast_conference *cnf = args;
2340 struct ast_frame *f=NULL;
2342 struct ast_filestream *s=NULL;
2345 const char *oldrecordingfilename = NULL;
2347 if (!cnf || !cnf->lchan) {
2351 ast_stopstream(cnf->lchan);
2352 flags = O_CREAT|O_TRUNC|O_WRONLY;
2355 cnf->recording = MEETME_RECORD_ACTIVE;
2356 while (ast_waitfor(cnf->lchan, -1) > -1) {
2357 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2358 AST_LIST_LOCK(&confs);
2359 AST_LIST_UNLOCK(&confs);
2362 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2363 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
2364 oldrecordingfilename = cnf->recordingfilename;
2367 f = ast_read(cnf->lchan);
2372 if (f->frametype == AST_FRAME_VOICE) {
2373 ast_mutex_lock(&cnf->listenlock);
2374 for (x=0;x<AST_FRAME_BITS;x++) {
2375 /* Free any translations that have occured */
2376 if (cnf->transframe[x]) {
2377 ast_frfree(cnf->transframe[x]);
2378 cnf->transframe[x] = NULL;
2382 ast_frfree(cnf->origframe);
2384 ast_mutex_unlock(&cnf->listenlock);
2386 res = ast_writestream(s, f);
2394 cnf->recording = MEETME_RECORD_OFF;
2401 static void load_config(void)
2403 struct ast_config *cfg;
2406 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2408 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2411 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2412 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2413 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2414 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2415 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2416 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2417 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2418 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2420 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2421 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2424 ast_config_destroy(cfg);
2427 static int unload_module(void *mod)
2431 res = ast_cli_unregister(&cli_show_confs);
2432 res |= ast_cli_unregister(&cli_conf);
2433 res |= ast_unregister_application(app3);
2434 res |= ast_unregister_application(app2);
2435 res |= ast_unregister_application(app);
2437 STANDARD_HANGUP_LOCALUSERS;
2442 static int load_module(void *mod)
2448 res = ast_cli_register(&cli_show_confs);
2449 res |= ast_cli_register(&cli_conf);
2450 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
2451 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
2452 res |= ast_register_application(app, conf_exec, synopsis, descrip);
2457 static int reload(void *mod)
2464 static const char *description(void)
2466 return "MeetMe conference bridge";
2469 static const char *key(void)
2471 return ASTERISK_GPL_KEY;
2474 STD_MOD(MOD_1, reload, NULL, NULL);