2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Meet me conference bridge
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <depend>zaptel</depend>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include <sys/ioctl.h>
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/config.h"
51 #include "asterisk/app.h"
52 #include "asterisk/dsp.h"
53 #include "asterisk/musiconhold.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/options.h"
56 #include "asterisk/cli.h"
57 #include "asterisk/say.h"
58 #include "asterisk/utils.h"
59 #include "asterisk/translate.h"
60 #include "asterisk/ulaw.h"
61 #include "asterisk/astobj.h"
62 #include "asterisk/devicestate.h"
69 #define CONFIG_FILE_NAME "meetme.conf"
71 /*! each buffer is 20ms, so this is 640ms total */
72 #define DEFAULT_AUDIO_BUFFERS 32
75 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
76 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
77 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
80 #define MEETME_DELAYDETECTTALK 300
81 #define MEETME_DELAYDETECTENDTALK 1000
83 #define AST_FRAME_BITS 32
95 enum recording_state {
97 MEETME_RECORD_STARTED,
99 MEETME_RECORD_TERMINATE
102 #define CONF_SIZE 320
105 /*! user has admin access on the conference */
106 CONFFLAG_ADMIN = (1 << 0),
107 /*! If set the user can only receive audio from the conference */
108 CONFFLAG_MONITOR = (1 << 1),
109 /*! If set asterisk will exit conference when '#' is pressed */
110 CONFFLAG_POUNDEXIT = (1 << 2),
111 /*! If set asterisk will provide a menu to the user when '*' is pressed */
112 CONFFLAG_STARMENU = (1 << 3),
113 /*! If set the use can only send audio to the conference */
114 CONFFLAG_TALKER = (1 << 4),
115 /*! If set there will be no enter or leave sounds */
116 CONFFLAG_QUIET = (1 << 5),
117 /*! If set, when user joins the conference, they will be told the number
118 * of users that are already in */
119 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
120 /*! Set to run AGI Script in Background */
121 CONFFLAG_AGI = (1 << 7),
122 /*! Set to have music on hold when user is alone in conference */
123 CONFFLAG_MOH = (1 << 8),
124 /*! If set the MeetMe will return if all marked with this flag left */
125 CONFFLAG_MARKEDEXIT = (1 << 9),
126 /*! If set, the MeetMe will wait until a marked user enters */
127 CONFFLAG_WAITMARKED = (1 << 10),
128 /*! If set, the MeetMe will exit to the specified context */
129 CONFFLAG_EXIT_CONTEXT = (1 << 11),
130 /*! If set, the user will be marked */
131 CONFFLAG_MARKEDUSER = (1 << 12),
132 /*! If set, user will be ask record name on entry of conference */
133 CONFFLAG_INTROUSER = (1 << 13),
134 /*! If set, the MeetMe will be recorded */
135 CONFFLAG_RECORDCONF = (1<< 14),
136 /*! If set, the user will be monitored if the user is talking or not */
137 CONFFLAG_MONITORTALKER = (1 << 15),
138 CONFFLAG_DYNAMIC = (1 << 16),
139 CONFFLAG_DYNAMICPIN = (1 << 17),
140 CONFFLAG_EMPTY = (1 << 18),
141 CONFFLAG_EMPTYNOPIN = (1 << 19),
142 CONFFLAG_ALWAYSPROMPT = (1 << 20),
143 /*! If set, treats talking users as muted users */
144 CONFFLAG_OPTIMIZETALKER = (1 << 21),
145 /*! If set, won't speak the extra prompt when the first person
146 * enters the conference */
147 CONFFLAG_NOONLYPERSON = (1 << 22),
148 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
149 /*! If set, user will be asked to record name on entry of conference
151 CONFFLAG_STARTMUTED = (1 << 24)
152 /*! If set, the user will be initially self-muted */
155 AST_APP_OPTIONS(meetme_opts, {
156 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
157 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
158 AST_APP_OPTION('b', CONFFLAG_AGI ),
159 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
160 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
161 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
162 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
163 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
164 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
165 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
166 AST_APP_OPTION('M', CONFFLAG_MOH ),
167 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
168 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
169 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
170 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
171 AST_APP_OPTION('q', CONFFLAG_QUIET ),
172 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
173 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
174 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
175 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
176 AST_APP_OPTION('t', CONFFLAG_TALKER ),
177 AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
178 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
179 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
180 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
183 AST_APP_OPTIONS(sla_opts, {
184 /* Just a placeholder for now */
186 static const char *app = "MeetMe";
187 static const char *app2 = "MeetMeCount";
188 static const char *app3 = "MeetMeAdmin";
189 static const char *appslas = "SLAS";
190 static const char *appslat = "SLAT";
192 static const char *synopsis = "MeetMe conference bridge";
193 static const char *synopsis2 = "MeetMe participant count";
194 static const char *synopsis3 = "MeetMe conference Administration";
195 static const char *synopslas = "Shared Line Appearance - Station";
196 static const char *synopslat = "Shared Line Appearance - Trunk";
198 static const char *descrip =
199 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
200 "conference. If the conference number is omitted, the user will be prompted\n"
201 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
202 "is specified, by pressing '#'.\n"
203 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
204 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
205 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
206 "The option string may contain zero or more of the following characters:\n"
207 " 'a' -- set admin mode\n"
208 " 'A' -- set marked mode\n"
209 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
210 " Default: conf-background.agi (Note: This does not work with\n"
211 " non-Zap channels in the same conference)\n"
212 " 'c' -- announce user(s) count on joining a conference\n"
213 " 'd' -- dynamically add conference\n"
214 " 'D' -- dynamically add conference, prompting for a PIN\n"
215 " 'e' -- select an empty conference\n"
216 " 'E' -- select an empty pinless conference\n"
217 " 'i' -- announce user join/leave with review\n"
218 " 'I' -- announce user join/leave without review\n"
219 " 'l' -- set listen only mode (Listen only, no talking)\n"
220 " 'm' -- set initially muted\n"
221 " 'M' -- enable music on hold when the conference has a single caller\n"
222 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
223 " being muted, meaning (a) No encode is done on transmission and\n"
224 " (b) Received audio that is not registered as talking is omitted\n"
225 " causing no buildup in background noise\n"
226 " 'p' -- allow user to exit the conference by pressing '#'\n"
227 " 'P' -- always prompt for the pin even if it is specified\n"
228 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
229 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
230 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
231 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
233 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
234 " 't' -- set talk only mode. (Talk only, no listening)\n"
235 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
236 " 'w' -- wait until the marked user enters the conference\n"
237 " 'x' -- close the conference when last marked user exits\n"
238 " 'X' -- allow user to exit the conference by entering a valid single\n"
239 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
240 " if that variable is not defined.\n"
241 " '1' -- do not play message when first person enters\n";
243 static const char *descrip2 =
244 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
245 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
246 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
247 "the channel, unless priority n+1 exists, in which case priority progress will\n"
249 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
251 static const char *descrip3 =
252 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
253 " 'e' -- Eject last user that joined\n"
254 " 'k' -- Kick one user out of conference\n"
255 " 'K' -- Kick all users out of conference\n"
256 " 'l' -- Unlock conference\n"
257 " 'L' -- Lock conference\n"
258 " 'm' -- Unmute one user\n"
259 " 'M' -- Mute one user\n"
260 " 'n' -- Unmute all users in the conference\n"
261 " 'N' -- Mute all non-admin users in the conference\n"
262 " 'r' -- Reset one user's volume settings\n"
263 " 'R' -- Reset all users volume settings\n"
264 " 's' -- Lower entire conference speaking volume\n"
265 " 'S' -- Raise entire conference speaking volume\n"
266 " 't' -- Lower one user's talk volume\n"
267 " 'T' -- Lower all users talk volume\n"
268 " 'u' -- Lower one user's listen volume\n"
269 " 'U' -- Lower all users listen volume\n"
270 " 'v' -- Lower entire conference listening volume\n"
271 " 'V' -- Raise entire conference listening volume\n"
274 static const char *descripslas =
275 " SLAS(sla[,options]): Run Shared Line Appearance for station\n"
276 "Runs the share line appearance for a station calling in. If there are no\n"
277 "other participants in the conference, the trunk is called and is dumped into\n"
280 static const char *descripslat =
281 " SLAT(sla[,options]): Run Shared Line Appearance for trunk\n"
282 "Runs the share line appearance for a trunk calling in. If there are no\n"
283 "other participants in the conference, all member stations are invited into\n"
286 #define CONFIG_FILE_NAME "meetme.conf"
287 #define CONFIG_FILE_NAME_SLA "sla.conf"
291 struct ast_conference {
292 ast_mutex_t playlock; /*!< Conference specific lock (players) */
293 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
294 char confno[AST_MAX_EXTENSION]; /*!< Conference */
295 struct ast_channel *chan; /*!< Announcements channel */
296 struct ast_channel *lchan; /*!< Listen/Record channel */
297 int fd; /*!< Announcements fd */
298 int zapconf; /*!< Zaptel Conf # */
299 int users; /*!< Number of active users */
300 int markedusers; /*!< Number of marked users */
301 time_t start; /*!< Start time (s) */
302 int refcount; /*!< reference count of usage */
303 enum recording_state recording:2; /*!< recording status */
304 unsigned int isdynamic:1; /*!< Created on the fly? */
305 unsigned int locked:1; /*!< Is the conference locked? */
306 pthread_t recordthread; /*!< thread for recording */
307 pthread_attr_t attr; /*!< thread attribute */
308 const char *recordingfilename; /*!< Filename to record the Conference into */
309 const char *recordingformat; /*!< Format to record the Conference in */
310 char pin[AST_MAX_EXTENSION]; /*!< If protected by a PIN */
311 char pinadmin[AST_MAX_EXTENSION]; /*!< If protected by a admin PIN */
312 struct ast_frame *transframe[32];
313 struct ast_frame *origframe;
314 struct ast_trans_pvt *transpath[32];
315 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
316 AST_LIST_ENTRY(ast_conference) list;
319 static AST_LIST_HEAD_STATIC(confs, ast_conference);
322 int desired; /*!< Desired volume adjustment */
323 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
326 struct ast_conf_user {
327 int user_no; /*!< User Number */
328 int userflags; /*!< Flags as set in the conference */
329 int adminflags; /*!< Flags set by the Admin */
330 struct ast_channel *chan; /*!< Connected channel */
331 int talking; /*!< Is user talking */
332 int zapchannel; /*!< Is a Zaptel channel */
333 char usrvalue[50]; /*!< Custom User Value */
334 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
335 time_t jointime; /*!< Time the user joined the conference */
337 struct volume listen;
338 AST_LIST_ENTRY(ast_conf_user) list;
341 struct ast_sla_station {
342 ASTOBJ_COMPONENTS(struct ast_sla_station);
347 struct ast_sla_station_box {
348 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla_station);
352 ASTOBJ_COMPONENTS (struct ast_sla);
353 struct ast_sla_station_box stations;
360 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla);
363 static int audio_buffers; /* The number of audio buffers to be allocated on pseudo channels
366 /*! The number of audio buffers to be allocated on pseudo channels
367 * when in a conference */
368 static int audio_buffers;
370 /*! Map 'volume' levels from -5 through +5 into
371 * decibel (dB) settings for channel drivers
372 * Note: these are not a straight linear-to-dB
373 * conversion... the numbers have been modified
374 * to give the user a better level of adjustability
376 static signed char gain_map[] = {
391 static int admin_exec(struct ast_channel *chan, void *data);
392 static void *recordthread(void *args);
394 static char *istalking(int x)
399 return "(unmonitored)";
401 return "(not talking)";
404 static int careful_write(int fd, unsigned char *data, int len, int block)
411 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
412 res = ioctl(fd, ZT_IOMUX, &x);
416 res = write(fd, data, len);
418 if (errno != EAGAIN) {
419 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
431 static int set_talk_volume(struct ast_conf_user *user, int volume)
433 signed char gain_adjust;
435 /* attempt to make the adjustment in the channel driver;
436 if successful, don't adjust in the frame reading routine
438 gain_adjust = gain_map[volume + 5];
440 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
443 static int set_listen_volume(struct ast_conf_user *user, int volume)
445 signed char gain_adjust;
447 /* attempt to make the adjustment in the channel driver;
448 if successful, don't adjust in the frame reading routine
450 gain_adjust = gain_map[volume + 5];
452 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
455 static void tweak_volume(struct volume *vol, enum volume_action action)
459 switch (vol->desired) {
474 switch (vol->desired) {
490 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
492 tweak_volume(&user->talk, action);
493 /* attempt to make the adjustment in the channel driver;
494 if successful, don't adjust in the frame reading routine
496 if (!set_talk_volume(user, user->talk.desired))
497 user->talk.actual = 0;
499 user->talk.actual = user->talk.desired;
502 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
504 tweak_volume(&user->listen, action);
505 /* attempt to make the adjustment in the channel driver;
506 if successful, don't adjust in the frame reading routine
508 if (!set_listen_volume(user, user->listen.desired))
509 user->listen.actual = 0;
511 user->listen.actual = user->listen.desired;
514 static void reset_volumes(struct ast_conf_user *user)
516 signed char zero_volume = 0;
518 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
519 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
522 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
528 if (!chan->_softhangup)
529 res = ast_autoservice_start(chan);
531 AST_LIST_LOCK(&confs);
547 careful_write(conf->fd, data, len, 1);
550 AST_LIST_UNLOCK(&confs);
553 ast_autoservice_stop(chan);
556 static void station_destroy(struct ast_sla_station *station)
561 static void sla_destroy(struct ast_sla *sla)
563 ASTOBJ_CONTAINER_DESTROYALL(&sla->stations, station_destroy);
564 ASTOBJ_CONTAINER_DESTROY(&sla->stations);
568 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
570 struct ast_conference *cnf;
571 struct zt_confinfo ztc;
573 AST_LIST_LOCK(&confs);
575 AST_LIST_TRAVERSE(&confs, cnf, list) {
576 if (!strcmp(confno, cnf->confno))
580 if (!cnf && (make || dynamic)) {
582 if ((cnf = ast_calloc(1, sizeof(*cnf)))) {
583 ast_mutex_init(&cnf->playlock);
584 ast_mutex_init(&cnf->listenlock);
585 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
586 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
587 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
589 cnf->markedusers = 0;
590 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
592 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
593 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
594 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
596 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
597 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
599 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
605 memset(&ztc, 0, sizeof(ztc));
606 /* Setup a new zap conference */
609 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
610 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
611 ast_log(LOG_WARNING, "Error setting conference\n");
613 ast_hangup(cnf->chan);
620 cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
622 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
623 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
625 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
626 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
627 ast_log(LOG_WARNING, "Error setting conference\n");
628 ast_hangup(cnf->lchan);
632 /* Fill the conference struct */
633 cnf->start = time(NULL);
634 cnf->zapconf = ztc.confno;
635 cnf->isdynamic = dynamic ? 1 : 0;
636 if (option_verbose > 2)
637 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
638 AST_LIST_INSERT_HEAD(&confs, cnf, list);
643 cnf->refcount += refcount;
645 AST_LIST_UNLOCK(&confs);
649 static int confs_show(int fd, int argc, char **argv)
651 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
653 return RESULT_SUCCESS;
656 static int sla_show(int fd, int argc, char *argv[])
660 return RESULT_SHOWUSAGE;
662 ast_cli(fd, "Shared line appearances:\n");
663 ASTOBJ_CONTAINER_TRAVERSE(&slas, 1, {
664 ASTOBJ_RDLOCK(iterator);
665 ast_cli(fd, "SLA %s\n", iterator->name);
666 if (ast_strlen_zero(iterator->trunkdest) || ast_strlen_zero(iterator->trunktech))
667 ast_cli(fd, "Trunk => <unspecified>\n");
669 ast_cli(fd, "Trunk => %s/%s\n", iterator->trunktech, iterator->trunkdest);
671 ASTOBJ_CONTAINER_TRAVERSE(&sla->stations, 1, {
672 ast_cli(fd, "Station: %s/%s\n", iterator->tech, iterator->dest);
674 ASTOBJ_UNLOCK(iterator);
677 return RESULT_SUCCESS;
680 static char show_confs_usage[] =
681 "Deprecated! Please use 'meetme' instead.\n";
683 static struct ast_cli_entry cli_show_confs = {
684 { "show", "conferences", NULL }, confs_show,
685 "Show status of conferences", show_confs_usage, NULL };
688 static char sla_show_usage[] =
690 " Lists status of all shared line appearances\n";
692 static struct ast_cli_entry cli_sla_show = {
693 { "sla", "show", NULL }, sla_show,
694 "Show status of Shared Line Appearances", sla_show_usage, NULL };
696 static int conf_cmd(int fd, int argc, char **argv)
698 /* Process the command */
699 struct ast_conference *cnf;
700 struct ast_conf_user *user;
702 int i = 0, total = 0;
704 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
705 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
706 char cmdline[1024] = "";
709 ast_cli(fd, "Invalid Arguments.\n");
710 /* Check for length so no buffer will overflow... */
711 for (i = 0; i < argc; i++) {
712 if (strlen(argv[i]) > 100)
713 ast_cli(fd, "Invalid Arguments.\n");
716 /* 'MeetMe': List all the conferences */
718 if (AST_LIST_EMPTY(&confs)) {
719 ast_cli(fd, "No active MeetMe conferences.\n");
720 return RESULT_SUCCESS;
722 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
723 AST_LIST_TRAVERSE(&confs, cnf, list) {
724 if (cnf->markedusers == 0)
725 strcpy(cmdline, "N/A ");
727 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
728 hr = (now - cnf->start) / 3600;
729 min = ((now - cnf->start) % 3600) / 60;
730 sec = (now - cnf->start) % 60;
732 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
736 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
737 return RESULT_SUCCESS;
740 return RESULT_SHOWUSAGE;
741 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
742 if (strstr(argv[1], "lock")) {
743 if (strcmp(argv[1], "lock") == 0) {
745 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
748 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
750 } else if (strstr(argv[1], "mute")) {
752 return RESULT_SHOWUSAGE;
753 if (strcmp(argv[1], "mute") == 0) {
755 if (strcmp(argv[3], "all") == 0) {
756 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
758 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
759 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
763 if (strcmp(argv[3], "all") == 0) {
764 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
766 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
767 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
770 } else if (strcmp(argv[1], "kick") == 0) {
772 return RESULT_SHOWUSAGE;
773 if (strcmp(argv[3], "all") == 0) {
775 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
777 /* Kick a single user */
778 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
779 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
781 } else if(strcmp(argv[1], "list") == 0) {
782 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
783 /* List all the users in a conference */
784 if (AST_LIST_EMPTY(&confs)) {
786 ast_cli(fd, "No active conferences.\n");
787 return RESULT_SUCCESS;
789 /* Find the right conference */
790 AST_LIST_TRAVERSE(&confs, cnf, list) {
791 if (strcmp(cnf->confno, argv[2]) == 0)
796 ast_cli(fd, "No such conference: %s.\n",argv[2]);
797 return RESULT_SUCCESS;
799 /* Show all the users */
800 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
802 hr = (now - user->jointime) / 3600;
803 min = ((now - user->jointime) % 3600) / 60;
804 sec = (now - user->jointime) % 60;
806 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
808 S_OR(user->chan->cid.cid_num, "<unknown>"),
809 S_OR(user->chan->cid.cid_name, "<no name>"),
811 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
812 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
813 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
814 istalking(user->talking), hr, min, sec);
816 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
818 S_OR(user->chan->cid.cid_num, ""),
819 S_OR(user->chan->cid.cid_name, ""),
821 user->userflags & CONFFLAG_ADMIN ? "1" : "",
822 user->userflags & CONFFLAG_MONITOR ? "1" : "",
823 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
824 user->talking, hr, min, sec);
828 ast_cli(fd,"%d users in that conference.\n",cnf->users);
830 return RESULT_SUCCESS;
832 return RESULT_SHOWUSAGE;
833 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
834 admin_exec(NULL, cmdline);
839 static char *complete_confcmd(const char *line, const char *word, int pos, int state)
841 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
843 int len = strlen(word);
845 struct ast_conference *cnf = NULL;
846 struct ast_conf_user *usr = NULL;
849 char *myline, *ret = NULL;
851 if (pos == 1) { /* Command */
852 return ast_cli_complete(word, cmds, state);
853 } else if (pos == 2) { /* Conference Number */
854 AST_LIST_LOCK(&confs);
855 AST_LIST_TRAVERSE(&confs, cnf, list) {
856 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
861 ret = ast_strdup(ret); /* dup before releasing the lock */
862 AST_LIST_UNLOCK(&confs);
864 } else if (pos == 3) {
865 /* User Number || Conf Command option*/
866 if (strstr(line, "mute") || strstr(line, "kick")) {
867 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
868 return strdup("all");
870 AST_LIST_LOCK(&confs);
872 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
873 myline = ast_strdupa(line);
874 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
875 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
879 AST_LIST_TRAVERSE(&confs, cnf, list) {
880 if (!strcmp(confno, cnf->confno))
885 /* Search for the user */
886 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
887 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
888 if (!strncasecmp(word, usrno, len) && ++which > state)
892 AST_LIST_UNLOCK(&confs);
893 return usr ? strdup(usrno) : NULL;
894 } else if ( strstr(line, "list") && ( 0 == state ) )
895 return strdup("concise");
901 static char conf_usage[] =
902 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
903 " Executes a command for the conference or on a conferee\n";
905 static struct ast_cli_entry cli_conf = {
906 {"meetme", NULL, NULL }, conf_cmd,
907 "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
909 static void conf_flush(int fd, struct ast_channel *chan)
913 /* read any frames that may be waiting on the channel
919 /* when no frames are available, this will wait
920 for 1 millisecond maximum
922 while (ast_waitfor(chan, 1)) {
926 else /* channel was hung up or something else happened */
931 /* flush any data sitting in the pseudo channel */
933 if (ioctl(fd, ZT_FLUSH, &x))
934 ast_log(LOG_WARNING, "Error flushing channel\n");
938 /* Remove the conference from the list and free it.
939 We assume that this was called while holding conflock. */
940 static int conf_free(struct ast_conference *conf)
944 AST_LIST_REMOVE(&confs, conf, list);
946 if (conf->recording == MEETME_RECORD_ACTIVE) {
947 conf->recording = MEETME_RECORD_TERMINATE;
948 AST_LIST_UNLOCK(&confs);
950 AST_LIST_LOCK(&confs);
951 if (conf->recording == MEETME_RECORD_OFF)
953 AST_LIST_UNLOCK(&confs);
957 for (x=0;x<AST_FRAME_BITS;x++) {
958 if (conf->transframe[x])
959 ast_frfree(conf->transframe[x]);
960 if (conf->transpath[x])
961 ast_translator_free_path(conf->transpath[x]);
964 ast_frfree(conf->origframe);
966 ast_hangup(conf->lchan);
968 ast_hangup(conf->chan);
977 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
979 struct ast_conf_user *user = NULL;
980 struct ast_conf_user *usr = NULL;
982 struct zt_confinfo ztc, ztc_empty;
984 struct ast_channel *c;
996 int currentmarked = 0;
1000 int using_pseudo = 0;
1005 struct ast_dsp *dsp=NULL;
1006 struct ast_app *app;
1007 const char *agifile;
1008 const char *agifiledefault = "conf-background.agi";
1009 char meetmesecs[30] = "";
1010 char exitcontext[AST_MAX_CONTEXT] = "";
1011 char recordingtmp[AST_MAX_EXTENSION] = "";
1012 char members[10] = "";
1015 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1016 char *buf = __buf + AST_FRIENDLY_OFFSET;
1018 if (!(user = ast_calloc(1, sizeof(*user)))) {
1019 AST_LIST_LOCK(&confs);
1021 if (!conf->refcount){
1024 AST_LIST_UNLOCK(&confs);
1028 if (confflags & CONFFLAG_RECORDCONF) {
1029 if (!conf->recordingfilename) {
1030 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1031 if (!conf->recordingfilename) {
1032 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1033 conf->recordingfilename = ast_strdupa(recordingtmp);
1035 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1036 if (!conf->recordingformat) {
1037 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1038 conf->recordingformat = ast_strdupa(recordingtmp);
1040 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1041 conf->confno, conf->recordingfilename, conf->recordingformat);
1045 if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
1046 pthread_attr_init(&conf->attr);
1047 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1048 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
1051 time(&user->jointime);
1053 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1054 /* Sorry, but this confernce is locked! */
1055 if (!ast_streamfile(chan, "conf-locked", chan->language))
1056 ast_waitstream(chan, "");
1060 if (confflags & CONFFLAG_MARKEDUSER)
1061 conf->markedusers++;
1063 ast_mutex_lock(&conf->playlock);
1065 if (AST_LIST_EMPTY(&conf->userlist))
1068 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1070 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1073 user->userflags = confflags;
1074 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1078 snprintf(members, sizeof(members), "%d", conf->users);
1079 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1081 /* This device changed state now - if this is the first user */
1082 if (conf->users == 1)
1083 ast_device_state_changed("meetme:%s", conf->confno);
1085 ast_mutex_unlock(&conf->playlock);
1087 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1088 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1089 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1090 else if (!ast_strlen_zero(chan->macrocontext))
1091 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1093 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1096 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1097 snprintf(user->namerecloc, sizeof(user->namerecloc),
1098 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1099 conf->confno, user->user_no);
1100 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1101 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1103 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1108 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1109 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1110 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1111 ast_waitstream(chan, "");
1112 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1113 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1114 ast_waitstream(chan, "");
1117 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1118 int keepplaying = 1;
1120 if (conf->users == 2) {
1121 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1122 res = ast_waitstream(chan, AST_DIGIT_ANY);
1129 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1130 res = ast_waitstream(chan, AST_DIGIT_ANY);
1137 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1143 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1144 res = ast_waitstream(chan, AST_DIGIT_ANY);
1153 ast_indicate(chan, -1);
1155 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1156 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1160 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1161 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1165 retryzap = strcasecmp(chan->tech->type, "Zap");
1166 user->zapchannel = !retryzap;
1169 origfd = chan->fds[0];
1171 fd = open("/dev/zap/pseudo", O_RDWR);
1173 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1177 /* Make non-blocking */
1178 flags = fcntl(fd, F_GETFL);
1180 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1184 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1185 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1189 /* Setup buffering information */
1190 memset(&bi, 0, sizeof(bi));
1191 bi.bufsize = CONF_SIZE/2;
1192 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1193 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1194 bi.numbufs = audio_buffers;
1195 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1196 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1201 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1202 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1208 /* XXX Make sure we're not running on a pseudo channel XXX */
1212 memset(&ztc, 0, sizeof(ztc));
1213 memset(&ztc_empty, 0, sizeof(ztc_empty));
1214 /* Check to see if we're in a conference... */
1216 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1217 ast_log(LOG_WARNING, "Error getting conference\n");
1222 /* Whoa, already in a conference... Retry... */
1224 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1229 memset(&ztc, 0, sizeof(ztc));
1230 /* Add us to the conference */
1232 ztc.confno = conf->zapconf;
1234 ast_mutex_lock(&conf->playlock);
1236 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1237 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1238 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1239 ast_waitstream(conf->chan, "");
1240 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1241 ast_waitstream(conf->chan, "");
1245 if (confflags & CONFFLAG_MONITOR)
1246 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1247 else if (confflags & CONFFLAG_TALKER)
1248 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1250 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1252 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1253 ast_log(LOG_WARNING, "Error setting conference\n");
1255 ast_mutex_unlock(&conf->playlock);
1258 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1261 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1266 chan->name, chan->uniqueid, conf->confno, user->user_no);
1270 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1272 if (!(confflags & CONFFLAG_QUIET))
1273 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1274 conf_play(chan, conf, ENTER);
1277 ast_mutex_unlock(&conf->playlock);
1279 conf_flush(fd, chan);
1281 if (confflags & CONFFLAG_AGI) {
1282 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1283 or use default filename of conf-background.agi */
1285 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1287 agifile = agifiledefault;
1289 if (user->zapchannel) {
1290 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1292 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1294 /* Find a pointer to the agi app and execute the script */
1295 app = pbx_findapp("agi");
1297 char *s = ast_strdupa(agifile);
1298 ret = pbx_exec(chan, app, s);
1300 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1303 if (user->zapchannel) {
1304 /* Remove CONFMUTE mode on Zap channel */
1306 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1309 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1310 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1312 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1314 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1315 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1319 int menu_was_active = 0;
1324 /* if we have just exited from the menu, and the user had a channel-driver
1325 volume adjustment, restore it
1327 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1328 set_talk_volume(user, user->listen.desired);
1330 menu_was_active = menu_active;
1332 currentmarked = conf->markedusers;
1333 if (!(confflags & CONFFLAG_QUIET) &&
1334 (confflags & CONFFLAG_MARKEDUSER) &&
1335 (confflags & CONFFLAG_WAITMARKED) &&
1337 if (currentmarked == 1 && conf->users > 1) {
1338 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1339 if (conf->users - 1 == 1) {
1340 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1341 ast_waitstream(chan, "");
1343 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1344 ast_waitstream(chan, "");
1347 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1348 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1349 ast_waitstream(chan, "");
1352 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1355 /* Update the struct with the actual confflags */
1356 user->userflags = confflags;
1358 if (confflags & CONFFLAG_WAITMARKED) {
1359 if(currentmarked == 0) {
1360 if (lastmarked != 0) {
1361 if (!(confflags & CONFFLAG_QUIET))
1362 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1363 ast_waitstream(chan, "");
1364 if(confflags & CONFFLAG_MARKEDEXIT)
1367 ztc.confmode = ZT_CONF_CONF;
1368 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1369 ast_log(LOG_WARNING, "Error setting conference\n");
1375 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1376 ast_moh_start(chan, NULL);
1379 ztc.confmode = ZT_CONF_CONF;
1380 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1381 ast_log(LOG_WARNING, "Error setting conference\n");
1386 } else if(currentmarked >= 1 && lastmarked == 0) {
1387 if (confflags & CONFFLAG_MONITOR)
1388 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1389 else if (confflags & CONFFLAG_TALKER)
1390 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1392 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1393 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1394 ast_log(LOG_WARNING, "Error setting conference\n");
1398 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1402 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1403 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1404 ast_waitstream(chan, "");
1405 conf_play(chan, conf, ENTER);
1410 /* trying to add moh for single person conf */
1411 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1412 if (conf->users == 1) {
1413 if (musiconhold == 0) {
1414 ast_moh_start(chan, NULL);
1425 /* Leave if the last marked user left */
1426 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1431 /* Check if my modes have changed */
1433 /* If I should be muted but am still talker, mute me */
1434 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1435 ztc.confmode ^= ZT_CONF_TALKER;
1436 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1437 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1442 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1448 chan->name, chan->uniqueid, conf->confno, user->user_no);
1451 /* If I should be un-muted but am not talker, un-mute me */
1452 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1453 ztc.confmode |= ZT_CONF_TALKER;
1454 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1455 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1460 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1466 chan->name, chan->uniqueid, conf->confno, user->user_no);
1469 /* If I have been kicked, exit the conference */
1470 if (user->adminflags & ADMINFLAG_KICKME) {
1471 //You have been kicked.
1472 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1473 ast_waitstream(chan, "");
1479 if (c->fds[0] != origfd) {
1481 /* Kill old pseudo */
1485 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1486 retryzap = strcasecmp(c->tech->type, "Zap");
1487 user->zapchannel = !retryzap;
1490 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1491 f = ast_read_noaudio(c);
1496 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1497 if (user->talk.actual)
1498 ast_frame_adjust_volume(f, user->talk.actual);
1500 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1503 if (user->talking == -1)
1506 res = ast_dsp_silence(dsp, f, &totalsilence);
1507 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1509 if (confflags & CONFFLAG_MONITORTALKER)
1510 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1516 chan->name, chan->uniqueid, conf->confno, user->user_no);
1518 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1520 if (confflags & CONFFLAG_MONITORTALKER)
1521 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1527 chan->name, chan->uniqueid, conf->confno, user->user_no);
1531 /* Absolutely do _not_ use careful_write here...
1532 it is important that we read data from the channel
1533 as fast as it arrives, and feed it into the conference.
1534 The buffering in the pseudo channel will take care of any
1535 timing differences, unless they are so drastic as to lose
1536 audio frames (in which case carefully writing would only
1537 have delayed the audio even further).
1539 /* As it turns out, we do want to use careful write. We just
1540 don't want to block, but we do want to at least *try*
1541 to write out all the samples.
1543 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1544 careful_write(fd, f->data, f->datalen, 0);
1546 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1549 tmp[0] = f->subclass;
1551 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1552 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1556 } else if (option_debug > 1)
1557 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1558 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1562 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1563 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1564 ast_log(LOG_WARNING, "Error setting conference\n");
1570 /* if we are entering the menu, and the user has a channel-driver
1571 volume adjustment, clear it
1573 if (!menu_active && user->talk.desired && !user->talk.actual)
1574 set_talk_volume(user, 0);
1579 if ((confflags & CONFFLAG_ADMIN)) {
1583 /* Record this sound! */
1584 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1585 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1586 ast_stopstream(chan);
1593 case '1': /* Un/Mute */
1596 /* for admin, change both admin and use flags */
1597 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1598 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1600 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1602 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1603 if (!ast_streamfile(chan, "conf-muted", chan->language))
1604 ast_waitstream(chan, "");
1606 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1607 ast_waitstream(chan, "");
1610 case '2': /* Un/Lock the Conference */
1614 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1615 ast_waitstream(chan, "");
1618 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1619 ast_waitstream(chan, "");
1622 case '3': /* Eject last user */
1624 usr = AST_LIST_LAST(&conf->userlist);
1625 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1626 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1627 ast_waitstream(chan, "");
1629 usr->adminflags |= ADMINFLAG_KICKME;
1630 ast_stopstream(chan);
1633 tweak_listen_volume(user, VOL_DOWN);
1636 tweak_listen_volume(user, VOL_UP);
1639 tweak_talk_volume(user, VOL_DOWN);
1645 tweak_talk_volume(user, VOL_UP);
1649 /* Play an error message! */
1650 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1651 ast_waitstream(chan, "");
1659 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1660 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1661 ast_stopstream(chan);
1668 case '1': /* Un/Mute */
1671 /* user can only toggle the self-muted state */
1672 user->adminflags ^= ADMINFLAG_SELFMUTED;
1674 /* they can't override the admin mute state */
1675 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1676 if (!ast_streamfile(chan, "conf-muted", chan->language))
1677 ast_waitstream(chan, "");
1679 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1680 ast_waitstream(chan, "");
1684 tweak_listen_volume(user, VOL_DOWN);
1687 tweak_listen_volume(user, VOL_UP);
1690 tweak_talk_volume(user, VOL_DOWN);
1696 tweak_talk_volume(user, VOL_UP);
1700 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1701 ast_waitstream(chan, "");
1707 ast_moh_start(chan, NULL);
1709 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1710 ast_log(LOG_WARNING, "Error setting conference\n");
1716 conf_flush(fd, chan);
1717 } else if (option_debug) {
1719 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1720 chan->name, f->frametype, f->subclass);
1723 } else if (outfd > -1) {
1724 res = read(outfd, buf, CONF_SIZE);
1726 memset(&fr, 0, sizeof(fr));
1727 fr.frametype = AST_FRAME_VOICE;
1728 fr.subclass = AST_FORMAT_SLINEAR;
1732 fr.offset = AST_FRIENDLY_OFFSET;
1733 if (!user->listen.actual &&
1734 ((confflags & CONFFLAG_MONITOR) ||
1735 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1736 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1739 for (index=0;index<AST_FRAME_BITS;index++)
1740 if (chan->rawwriteformat & (1 << index))
1742 if (index >= AST_FRAME_BITS)
1743 goto bailoutandtrynormal;
1744 ast_mutex_lock(&conf->listenlock);
1745 if (!conf->transframe[index]) {
1746 if (conf->origframe) {
1747 if (!conf->transpath[index])
1748 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1749 if (conf->transpath[index]) {
1750 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1751 if (!conf->transframe[index])
1752 conf->transframe[index] = &ast_null_frame;
1756 if (conf->transframe[index]) {
1757 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1758 if (ast_write(chan, conf->transframe[index]))
1759 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1762 ast_mutex_unlock(&conf->listenlock);
1763 goto bailoutandtrynormal;
1765 ast_mutex_unlock(&conf->listenlock);
1767 bailoutandtrynormal:
1768 if (user->listen.actual)
1769 ast_frame_adjust_volume(&fr, user->listen.actual);
1770 if (ast_write(chan, &fr) < 0) {
1771 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1775 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1777 lastmarked = currentmarked;
1787 /* Take out of conference */
1791 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1792 ast_log(LOG_WARNING, "Error setting conference\n");
1796 reset_volumes(user);
1798 AST_LIST_LOCK(&confs);
1799 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1800 conf_play(chan, conf, LEAVE);
1802 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1803 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1804 if ((conf->chan) && (conf->users > 1)) {
1805 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1806 ast_waitstream(conf->chan, "");
1807 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1808 ast_waitstream(conf->chan, "");
1810 ast_filedelete(user->namerecloc, NULL);
1813 AST_LIST_UNLOCK(&confs);
1816 AST_LIST_LOCK(&confs);
1821 if (user->user_no) { /* Only cleanup users who really joined! */
1823 hr = (now - user->jointime) / 3600;
1824 min = ((now - user->jointime) % 3600) / 60;
1825 sec = (now - user->jointime) % 60;
1828 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1833 "CallerIDnum: %s\r\n"
1834 "CallerIDname: %s\r\n"
1835 "Duration: %ld\r\n",
1836 chan->name, chan->uniqueid, conf->confno,
1838 S_OR(user->chan->cid.cid_num, "<unknown>"),
1839 S_OR(user->chan->cid.cid_name, "<unknown>"),
1840 (now - user->jointime));
1846 snprintf(members, sizeof(members), "%d", conf->users);
1847 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1848 if (confflags & CONFFLAG_MARKEDUSER)
1849 conf->markedusers--;
1850 /* Remove ourselves from the list */
1851 AST_LIST_REMOVE(&conf->userlist, user, list);
1852 if (AST_LIST_EMPTY(&conf->userlist)) {
1853 /* close this one when no more users and no references*/
1854 if (!conf->refcount)
1857 /* Return the number of seconds the user was in the conf */
1858 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1859 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1861 /* This device changed state now */
1862 if (!conf->users) /* If there are no more members */
1863 ast_device_state_changed("meetme:%s", conf->confno);
1866 AST_LIST_UNLOCK(&confs);
1871 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1872 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1874 struct ast_variable *var;
1875 struct ast_conference *cnf;
1877 /* Check first in the conference list */
1878 AST_LIST_LOCK(&confs);
1879 AST_LIST_TRAVERSE(&confs, cnf, list) {
1880 if (!strcmp(confno, cnf->confno))
1884 cnf->refcount += refcount;
1886 AST_LIST_UNLOCK(&confs);
1889 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1891 cnf = ast_calloc(1, sizeof(struct ast_conference));
1893 ast_log(LOG_ERROR, "Out of memory\n");
1897 var = ast_load_realtime("meetme", "confno", confno, NULL);
1899 if (!strcasecmp(var->name, "confno")) {
1900 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1901 } else if (!strcasecmp(var->name, "pin")) {
1902 pin = ast_strdupa(var->value);
1903 } else if (!strcasecmp(var->name, "adminpin")) {
1904 pinadmin = ast_strdupa(var->value);
1908 ast_variables_destroy(var);
1910 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1914 if (confflags && !cnf->chan &&
1915 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1916 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1917 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1918 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1921 if (confflags && !cnf->chan &&
1922 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1923 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1924 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1932 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
1933 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1935 struct ast_config *cfg;
1936 struct ast_variable *var;
1937 struct ast_conference *cnf;
1939 AST_DECLARE_APP_ARGS(args,
1940 AST_APP_ARG(confno);
1942 AST_APP_ARG(pinadmin);
1945 /* Check first in the conference list */
1946 AST_LIST_LOCK(&confs);
1947 AST_LIST_TRAVERSE(&confs, cnf, list) {
1948 if (!strcmp(confno, cnf->confno))
1952 cnf->refcount += refcount;
1954 AST_LIST_UNLOCK(&confs);
1958 /* No need to parse meetme.conf */
1959 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1961 if (dynamic_pin[0] == 'q') {
1962 /* Query the user to enter a PIN */
1963 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
1966 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
1968 cnf = build_conf(confno, "", "", make, dynamic, refcount);
1971 /* Check the config */
1972 cfg = ast_config_load(CONFIG_FILE_NAME);
1974 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
1977 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
1978 if (strcasecmp(var->name, "conf"))
1981 if (!(parse = ast_strdupa(var->value)))
1984 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
1985 if (!strcasecmp(args.confno, confno)) {
1986 /* Bingo it's a valid conference */
1987 cnf = build_conf(args.confno,
1989 S_OR(args.pinadmin, ""),
1990 make, dynamic, refcount);
1995 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1997 ast_config_destroy(cfg);
1999 } else if (dynamic_pin) {
2000 /* Correct for the user selecting 'D' instead of 'd' to have
2001 someone join into a conference that has already been created
2003 if (dynamic_pin[0] == 'q')
2004 dynamic_pin[0] = '\0';
2008 if (confflags && !cnf->chan &&
2009 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2010 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2011 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2012 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2015 if (confflags && !cnf->chan &&
2016 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2017 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2018 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2025 /*! \brief The MeetmeCount application */
2026 static int count_exec(struct ast_channel *chan, void *data)
2028 struct localuser *u;
2030 struct ast_conference *conf;
2034 AST_DECLARE_APP_ARGS(args,
2035 AST_APP_ARG(confno);
2036 AST_APP_ARG(varname);
2039 if (ast_strlen_zero(data)) {
2040 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2046 if (!(localdata = ast_strdupa(data))) {
2047 LOCAL_USER_REMOVE(u);
2051 AST_STANDARD_APP_ARGS(args, localdata);
2053 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
2056 count = conf->users;
2060 if (!ast_strlen_zero(args.varname)){
2061 /* have var so load it and exit */
2062 snprintf(val, sizeof(val), "%d",count);
2063 pbx_builtin_setvar_helper(chan, args.varname, val);
2065 if (chan->_state != AST_STATE_UP)
2067 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2069 LOCAL_USER_REMOVE(u);
2074 /*! \brief The meetme() application */
2075 static int conf_exec(struct ast_channel *chan, void *data)
2078 struct localuser *u;
2079 char confno[AST_MAX_EXTENSION] = "";
2082 struct ast_conference *cnf;
2083 struct ast_flags confflags = {0};
2085 int empty = 0, empty_no_pin = 0;
2086 int always_prompt = 0;
2087 char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
2088 AST_DECLARE_APP_ARGS(args,
2089 AST_APP_ARG(confno);
2090 AST_APP_ARG(options);
2096 if (ast_strlen_zero(data)) {
2103 if (chan->_state != AST_STATE_UP)
2106 info = ast_strdupa(notdata);
2108 AST_STANDARD_APP_ARGS(args, info);
2111 ast_copy_string(confno, args.confno, sizeof(confno));
2112 if (ast_strlen_zero(confno)) {
2118 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2121 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
2122 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2123 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2124 strcpy(the_pin, "q");
2126 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2127 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2128 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2135 int i, map[1024] = { 0, };
2136 struct ast_config *cfg;
2137 struct ast_variable *var;
2140 AST_LIST_LOCK(&confs);
2141 AST_LIST_TRAVERSE(&confs, cnf, list) {
2142 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2143 /* Disqualify in use conference */
2144 if (confno_int >= 0 && confno_int < 1024)
2148 AST_LIST_UNLOCK(&confs);
2150 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2151 if ((empty_no_pin) || (!dynamic)) {
2152 cfg = ast_config_load(CONFIG_FILE_NAME);
2154 var = ast_variable_browse(cfg, "rooms");
2156 if (!strcasecmp(var->name, "conf")) {
2157 char *stringp = ast_strdupa(var->value);
2159 char *confno_tmp = strsep(&stringp, "|,");
2161 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2162 if ((confno_int >= 0) && (confno_int < 1024)) {
2163 if (stringp && empty_no_pin) {
2169 /* For static: run through the list and see if this conference is empty */
2170 AST_LIST_LOCK(&confs);
2171 AST_LIST_TRAVERSE(&confs, cnf, list) {
2172 if (!strcmp(confno_tmp, cnf->confno)) {
2173 /* The conference exists, therefore it's not empty */
2178 AST_LIST_UNLOCK(&confs);
2180 /* At this point, we have a confno_tmp (static conference) that is empty */
2181 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2182 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2183 * Case 2: empty_no_pin and pin is blank (but not NULL)
2184 * Case 3: not empty_no_pin
2186 ast_copy_string(confno, confno_tmp, sizeof(confno));
2188 /* XXX the map is not complete (but we do have a confno) */
2196 ast_config_destroy(cfg);
2200 /* Select first conference number not in use */
2201 if (ast_strlen_zero(confno) && dynamic) {
2202 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2204 snprintf(confno, sizeof(confno), "%d", i);
2211 if (ast_strlen_zero(confno)) {
2212 res = ast_streamfile(chan, "conf-noempty", chan->language);
2214 ast_waitstream(chan, "");
2216 if (sscanf(confno, "%d", &confno_int) == 1) {
2217 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2219 ast_waitstream(chan, "");
2220 res = ast_say_digits(chan, confno_int, "", chan->language);
2223 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2228 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2229 /* Prompt user for conference number */
2230 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2232 /* Don't try to validate when we catch an error */
2238 if (!ast_strlen_zero(confno)) {
2239 /* Check the validity of the conference */
2240 cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2242 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2245 res = ast_streamfile(chan, "conf-invalid", chan->language);
2247 ast_waitstream(chan, "");
2252 if ((!ast_strlen_zero(cnf->pin) &&
2253 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2254 (!ast_strlen_zero(cnf->pinadmin) &&
2255 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2256 char pin[AST_MAX_EXTENSION]="";
2259 /* Allow the pin to be retried up to 3 times */
2260 for (j = 0; j < 3; j++) {
2261 if (*the_pin && (always_prompt == 0)) {
2262 ast_copy_string(pin, the_pin, sizeof(pin));
2265 /* Prompt user for pin if pin is required */
2266 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2269 if (!strcasecmp(pin, cnf->pin) ||
2270 (!ast_strlen_zero(cnf->pinadmin) &&
2271 !strcasecmp(pin, cnf->pinadmin))) {
2274 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2275 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2276 /* Run the conference */
2277 res = conf_run(chan, cnf, confflags.flags);
2281 if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
2282 res = ast_waitstream(chan, AST_DIGIT_ANY);
2284 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2288 AST_LIST_LOCK(&confs);
2290 if (!cnf->refcount){
2293 AST_LIST_UNLOCK(&confs);
2303 /* failed when getting the pin */
2306 /* see if we need to get rid of the conference */
2307 AST_LIST_LOCK(&confs);
2309 if (!cnf->refcount) {
2312 AST_LIST_UNLOCK(&confs);
2316 /* Don't retry pin with a static pin */
2317 if (*the_pin && (always_prompt==0)) {
2322 /* No pin required */
2325 /* Run the conference */
2326 res = conf_run(chan, cnf, confflags.flags);
2330 } while (allowretry);
2332 LOCAL_USER_REMOVE(u);
2337 struct sla_originate_helper {
2346 static void *sla_originate(void *data)
2348 struct sla_originate_helper *in = data;
2351 struct ast_channel *chan = NULL;
2353 ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, 99999, in->app, in->appdata, &reason, 1,
2354 S_OR(in->cid_num, NULL),
2355 S_OR(in->cid_name, NULL),
2357 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2359 ast_channel_unlock(chan);
2364 static void do_invite(struct ast_channel *orig, struct ast_sla *sla, const char *tech, const char *dest, const char *app)
2366 struct sla_originate_helper *slal = malloc(sizeof(struct sla_originate_helper));
2367 pthread_attr_t attr;
2370 memset(slal, 0, sizeof(struct sla_originate_helper));
2371 ast_copy_string(slal->tech, tech, sizeof(slal->tech));
2372 ast_copy_string(slal->data, dest, sizeof(slal->data));
2373 ast_copy_string(slal->app, app, sizeof(slal->app));
2374 ast_copy_string(slal->appdata, sla->name, sizeof(slal->appdata));
2375 if (orig->cid.cid_num)
2376 ast_copy_string(slal->cid_num, orig->cid.cid_num, sizeof(slal->cid_num));
2377 if (orig->cid.cid_name)
2378 ast_copy_string(slal->cid_name, orig->cid.cid_name, sizeof(slal->cid_name));
2379 pthread_attr_init(&attr);
2380 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2381 ast_pthread_create(&th, &attr, sla_originate, slal);
2385 static void invite_stations(struct ast_channel *orig, struct ast_sla *sla)
2387 ASTOBJ_CONTAINER_TRAVERSE(&sla->stations, 1, {
2388 do_invite(orig, sla,iterator->tech, iterator->dest, "SLAS");
2392 static void invite_trunk(struct ast_channel *orig, struct ast_sla *sla)
2394 do_invite(orig, sla,sla->trunktech, sla->trunkdest, "SLAT");
2399 /*! \brief The slas()/slat() application */
2400 static int sla_exec(struct ast_channel *chan, void *data, int trunk)
2403 struct localuser *u;
2404 char confno[AST_MAX_EXTENSION] = "";
2405 struct ast_sla *sla;
2406 struct ast_conference *cnf;
2408 struct ast_flags confflags = {0};
2410 AST_DECLARE_APP_ARGS(args,
2411 AST_APP_ARG(confno);
2412 AST_APP_ARG(options);
2415 if (ast_strlen_zero(data)) {
2416 ast_log(LOG_WARNING, "SLA%c requires an argument (line)\n", trunk ? 'T' : 'S');
2420 info = ast_strdupa(data);
2422 AST_STANDARD_APP_ARGS(args, info);
2424 if (ast_strlen_zero(args.confno)) {
2425 ast_log(LOG_WARNING, "SLA%c requires an SLA line number\n", trunk ? 'T' : 'S');
2431 if (chan->_state != AST_STATE_UP)
2434 info = ast_strdupa(data);
2438 ast_app_parse_options(sla_opts, &confflags, NULL, args.options);
2440 ast_set_flag(&confflags, CONFFLAG_QUIET|CONFFLAG_DYNAMIC);
2442 ast_set_flag(&confflags, CONFFLAG_WAITMARKED|CONFFLAG_MARKEDEXIT);
2444 ast_set_flag(&confflags, CONFFLAG_MARKEDUSER);
2446 sla = ASTOBJ_CONTAINER_FIND(&slas, args.confno);
2448 snprintf(confno, sizeof(confno), "sla-%s", args.confno);
2449 cnf = find_conf(chan, confno, 1, dynamic, "", 1, &confflags);
2453 invite_stations(chan, sla);
2455 invite_trunk(chan, sla);
2457 /* Run the conference */
2458 res = conf_run(chan, cnf, confflags.flags);
2460 ast_log(LOG_WARNING, "SLA%c: Found SLA '%s' but unable to build conference!\n", trunk ? 'T' : 'S', args.confno);
2461 ASTOBJ_UNREF(sla, sla_destroy);
2463 ast_log(LOG_WARNING, "SLA%c: SLA '%s' not found!\n", trunk ? 'T' : 'S', args.confno);
2466 LOCAL_USER_REMOVE(u);
2471 /*! \brief The slas() wrapper */
2472 static int slas_exec(struct ast_channel *chan, void *data)
2474 return sla_exec(chan, data, 0);
2477 /*! \brief The slat() wrapper */
2478 static int slat_exec(struct ast_channel *chan, void *data)
2480 return sla_exec(chan, data, 1);
2483 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2485 struct ast_conf_user *user = NULL;
2488 sscanf(callerident, "%i", &cid);
2489 if (conf && callerident) {
2490 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2491 if (cid == user->user_no)
2498 /*! \brief The MeetMeadmin application */
2499 /* MeetMeAdmin(confno, command, caller) */
2500 static int admin_exec(struct ast_channel *chan, void *data) {
2502 struct ast_conference *cnf;
2503 struct ast_conf_user *user = NULL;
2504 struct localuser *u;
2505 AST_DECLARE_APP_ARGS(args,
2506 AST_APP_ARG(confno);
2507 AST_APP_ARG(command);
2513 AST_LIST_LOCK(&confs);
2514 /* The param has the conference number the user and the command to execute */
2515 if (!ast_strlen_zero(data)) {
2516 params = ast_strdupa((char *) data);
2518 AST_STANDARD_APP_ARGS(args, params);
2520 if (!args.command) {
2521 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2522 AST_LIST_UNLOCK(&confs);
2523 LOCAL_USER_REMOVE(u);
2526 AST_LIST_TRAVERSE(&confs, cnf, list) {
2527 if (!strcmp(cnf->confno, args.confno))
2532 user = find_user(cnf, args.user);
2535 switch((int) (*args.command)) {
2536 case 76: /* L: Lock */
2539 case 108: /* l: Unlock */
2542 case 75: /* K: kick all users */
2543 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2544 user->adminflags |= ADMINFLAG_KICKME;
2546 case 101: /* e: Eject last user*/
2547 user = AST_LIST_LAST(&cnf->userlist);
2548 if (!(user->userflags & CONFFLAG_ADMIN))
2549 user->adminflags |= ADMINFLAG_KICKME;
2551 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2553 case 77: /* M: Mute */
2555 user->adminflags |= ADMINFLAG_MUTED;
2557 ast_log(LOG_NOTICE, "Specified User not found!\n");
2559 case 78: /* N: Mute all (non-admin) users */
2560 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2561 if (!(user->userflags & CONFFLAG_ADMIN))
2562 user->adminflags |= ADMINFLAG_MUTED;
2565 case 109: /* m: Unmute */
2567 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2569 ast_log(LOG_NOTICE, "Specified User not found!\n");
2571 case 110: /* n: Unmute all users */
2572 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2573 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2575 case 107: /* k: Kick user */
2577 user->adminflags |= ADMINFLAG_KICKME;
2579 ast_log(LOG_NOTICE, "Specified User not found!\n");
2581 case 118: /* v: Lower all users listen volume */
2582 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2583 tweak_listen_volume(user, VOL_DOWN);
2585 case 86: /* V: Raise all users listen volume */
2586 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2587 tweak_listen_volume(user, VOL_UP);
2589 case 115: /* s: Lower all users speaking volume */
2590 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2591 tweak_talk_volume(user, VOL_DOWN);
2593 case 83: /* S: Raise all users speaking volume */
2594 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2595 tweak_talk_volume(user, VOL_UP);
2597 case 82: /* R: Reset all volume levels */
2598 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2599 reset_volumes(user);
2601 case 114: /* r: Reset user's volume level */
2603 reset_volumes(user);
2605 ast_log(LOG_NOTICE, "Specified User not found!\n");
2607 case 85: /* U: Raise user's listen volume */
2609 tweak_listen_volume(user, VOL_UP);
2611 ast_log(LOG_NOTICE, "Specified User not found!\n");
2613 case 117: /* u: Lower user's listen volume */
2615 tweak_listen_volume(user, VOL_DOWN);
2617 ast_log(LOG_NOTICE, "Specified User not found!\n");
2619 case 84: /* T: Raise user's talk volume */
2621 tweak_talk_volume(user, VOL_UP);
2623 ast_log(LOG_NOTICE, "Specified User not found!\n");
2625 case 116: /* t: Lower user's talk volume */
2627 tweak_talk_volume(user, VOL_DOWN);
2629 ast_log(LOG_NOTICE, "Specified User not found!\n");
2633 ast_log(LOG_NOTICE, "Conference Number not found\n");
2636 AST_LIST_UNLOCK(&confs);
2638 LOCAL_USER_REMOVE(u);
2643 static int meetmemute(struct mansession *s, struct message *m, int mute)
2645 struct ast_conference *conf;
2646 struct ast_conf_user *user;
2647 char *confid = astman_get_header(m, "Meetme");
2648 char *userid = astman_get_header(m, "Usernum");
2651 if (ast_strlen_zero(confid)) {
2652 astman_send_error(s, m, "Meetme conference not specified");
2656 if (ast_strlen_zero(userid)) {
2657 astman_send_error(s, m, "Meetme user number not specified");
2661 userno = strtoul(userid, &userid, 10);
2664 astman_send_error(s, m, "Invalid user number");
2668 /* Look in the conference list */
2669 AST_LIST_LOCK(&confs);
2670 AST_LIST_TRAVERSE(&confs, conf, list) {
2671 if (!strcmp(confid, conf->confno))
2676 AST_LIST_UNLOCK(&confs);
2677 astman_send_error(s, m, "Meetme conference does not exist");
2681 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2682 if (user->user_no == userno)
2686 AST_LIST_UNLOCK(&confs);
2687 astman_send_error(s, m, "User number not found");
2692 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2694 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2696 AST_LIST_UNLOCK(&confs);
2698 ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
2700 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2704 static int action_meetmemute(struct mansession *s, struct message *m)
2706 return meetmemute(s, m, 1);
2709 static int action_meetmeunmute(struct mansession *s, struct message *m)
2711 return meetmemute(s, m, 0);
2714 static void *recordthread(void *args)
2716 struct ast_conference *cnf = args;
2717 struct ast_frame *f=NULL;
2719 struct ast_filestream *s=NULL;
2722 const char *oldrecordingfilename = NULL;
2724 if (!cnf || !cnf->lchan) {
2728 ast_stopstream(cnf->lchan);
2729 flags = O_CREAT|O_TRUNC|O_WRONLY;
2732 cnf->recording = MEETME_RECORD_ACTIVE;
2733 while (ast_waitfor(cnf->lchan, -1) > -1) {
2734 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2735 AST_LIST_LOCK(&confs);
2736 AST_LIST_UNLOCK(&confs);
2739 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2740 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
2741 oldrecordingfilename = cnf->recordingfilename;
2744 f = ast_read(cnf->lchan);
2749 if (f->frametype == AST_FRAME_VOICE) {
2750 ast_mutex_lock(&cnf->listenlock);
2751 for (x=0;x<AST_FRAME_BITS;x++) {
2752 /* Free any translations that have occured */
2753 if (cnf->transframe[x]) {
2754 ast_frfree(cnf->transframe[x]);
2755 cnf->transframe[x] = NULL;
2759 ast_frfree(cnf->origframe);
2761 ast_mutex_unlock(&cnf->listenlock);
2763 res = ast_writestream(s, f);
2771 cnf->recording = MEETME_RECORD_OFF;
2778 /*! \brief Callback for devicestate providers */
2779 static int meetmestate(const char *data)
2781 struct ast_conference *conf;
2783 /* Find conference */
2784 AST_LIST_LOCK(&confs);
2785 AST_LIST_TRAVERSE(&confs, conf, list) {
2786 if (!strcmp(data, conf->confno))
2789 AST_LIST_UNLOCK(&confs);
2791 return AST_DEVICE_INVALID;
2796 return AST_DEVICE_NOT_INUSE;
2798 return AST_DEVICE_INUSE;
2801 static void load_config_meetme(void)
2803 struct ast_config *cfg;
2806 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2808 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2811 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2812 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2813 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2814 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2815 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2816 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2817 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2818 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2820 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2821 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2824 ast_config_destroy(cfg);
2827 static void append_station(struct ast_sla *sla, const char *station)
2829 struct ast_sla_station *s;
2831 s = ast_calloc(1, sizeof(struct ast_sla_station) + strlen(station) + 2);
2834 strcpy(s->tech, station);
2835 c = strchr(s->tech, '/');
2839 ASTOBJ_CONTAINER_LINK(&sla->stations, s);
2841 ast_log(LOG_WARNING, "station '%s' should be in tech/destination format! Ignoring!\n", station);
2847 static void parse_sla(const char *cat, struct ast_variable *v)
2849 struct ast_sla *sla;
2850 sla = ASTOBJ_CONTAINER_FIND(&slas, cat);
2852 sla = ast_calloc(1, sizeof(struct ast_sla));
2855 ast_copy_string(sla->name, cat, sizeof(sla->name));
2856 snprintf(sla->confname, sizeof(sla->confname), "sla-%s", sla->name);
2857 ASTOBJ_CONTAINER_LINK(&slas, sla);
2864 if (!strcasecmp(v->name, "trunk")) {
2866 c = strchr(v->value, '/');
2868 ast_copy_string(sla->trunktech, v->value, (c - v->value) + 1);
2869 ast_copy_string(sla->trunkdest, c + 1, sizeof(sla->trunkdest));
2871 } else if (!strcasecmp(v->name, "station")) {
2872 append_station(sla, v->value);
2880 static void load_config_sla(void)
2883 struct ast_config *cfg;
2884 if (!(cfg = ast_config_load(CONFIG_FILE_NAME_SLA)))
2887 ASTOBJ_CONTAINER_MARKALL(&slas);
2888 cat = ast_category_browse(cfg, NULL);
2890 if (strcasecmp(cat, "general"))
2891 parse_sla(cat, ast_variable_browse(cfg, cat));
2892 cat = ast_category_browse(cfg, cat);
2894 ast_config_destroy(cfg);
2895 ASTOBJ_CONTAINER_PRUNE_MARKED(&slas, sla_destroy);
2898 static void load_config(void)
2900 load_config_meetme();
2904 static int unload_module(void *mod)
2908 res = ast_cli_unregister(&cli_show_confs);
2909 res |= ast_cli_unregister(&cli_sla_show);
2910 res |= ast_cli_unregister(&cli_conf);
2911 res |= ast_manager_unregister("MeetmeMute");
2912 res |= ast_manager_unregister("MeetmeUnmute");
2913 res |= ast_unregister_application(app3);
2914 res |= ast_unregister_application(app2);
2915 res |= ast_unregister_application(app);
2917 ast_devstate_prov_del("Meetme");
2918 STANDARD_HANGUP_LOCALUSERS;
2923 static int load_module(void *mod)
2928 ASTOBJ_CONTAINER_INIT(&slas);
2929 res = ast_cli_register(&cli_show_confs);
2930 res |= ast_cli_register(&cli_sla_show);
2931 res |= ast_cli_register(&cli_conf);
2932 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute, "Mute a Meetme user");
2933 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute, "Unmute a Meetme user");
2934 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
2935 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
2936 res |= ast_register_application(app, conf_exec, synopsis, descrip);
2937 res |= ast_register_application(appslas, slas_exec, synopslas, descripslas);
2938 res |= ast_register_application(appslat, slat_exec, synopslat, descripslat);
2940 res |= ast_devstate_prov_add("Meetme", meetmestate);
2944 static int reload(void *mod)
2951 static const char *description(void)
2953 return "MeetMe conference bridge";
2956 static const char *key(void)
2958 return ASTERISK_GPL_KEY;
2961 STD_MOD(MOD_1, reload, NULL, NULL);