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>
42 #include <zaptel/zaptel.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 /*! If set, user will be asked to record name on entry of conference
150 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
151 /*! If set, the user will be initially self-muted */
152 CONFFLAG_STARTMUTED = (1 << 24),
153 /*! If set, the user is a shared line appearance station */
154 CONFFLAG_SLA_STATION = (1 << 25),
155 /*! If set, the user is a shared line appearance trunk */
156 CONFFLAG_SLA_TRUNK = (1 << 26),
157 /*! If set, the user has put us on hold */
158 CONFFLAG_HOLD = (1 << 27)
161 AST_APP_OPTIONS(meetme_opts, {
162 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
163 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
164 AST_APP_OPTION('b', CONFFLAG_AGI ),
165 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
166 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
167 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
168 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
169 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
170 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
171 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
172 AST_APP_OPTION('M', CONFFLAG_MOH ),
173 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
174 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
175 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
176 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
177 AST_APP_OPTION('q', CONFFLAG_QUIET ),
178 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
179 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
180 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
181 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
182 AST_APP_OPTION('t', CONFFLAG_TALKER ),
183 AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
184 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
185 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
186 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
189 AST_APP_OPTIONS(sla_opts, {
190 /* Just a placeholder for now */
192 static const char *app = "MeetMe";
193 static const char *app2 = "MeetMeCount";
194 static const char *app3 = "MeetMeAdmin";
195 static const char *appslas = "SLAS";
196 static const char *appslat = "SLAT";
198 static const char *synopsis = "MeetMe conference bridge";
199 static const char *synopsis2 = "MeetMe participant count";
200 static const char *synopsis3 = "MeetMe conference Administration";
201 static const char *synopslas = "Shared Line Appearance - Station";
202 static const char *synopslat = "Shared Line Appearance - Trunk";
204 static const char *descrip =
205 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
206 "conference. If the conference number is omitted, the user will be prompted\n"
207 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
208 "is specified, by pressing '#'.\n"
209 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
210 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
211 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
212 "The option string may contain zero or more of the following characters:\n"
213 " 'a' -- set admin mode\n"
214 " 'A' -- set marked mode\n"
215 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
216 " Default: conf-background.agi (Note: This does not work with\n"
217 " non-Zap channels in the same conference)\n"
218 " 'c' -- announce user(s) count on joining a conference\n"
219 " 'd' -- dynamically add conference\n"
220 " 'D' -- dynamically add conference, prompting for a PIN\n"
221 " 'e' -- select an empty conference\n"
222 " 'E' -- select an empty pinless conference\n"
223 " 'i' -- announce user join/leave with review\n"
224 " 'I' -- announce user join/leave without review\n"
225 " 'l' -- set listen only mode (Listen only, no talking)\n"
226 " 'm' -- set initially muted\n"
227 " 'M' -- enable music on hold when the conference has a single caller\n"
228 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
229 " being muted, meaning (a) No encode is done on transmission and\n"
230 " (b) Received audio that is not registered as talking is omitted\n"
231 " causing no buildup in background noise\n"
232 " 'p' -- allow user to exit the conference by pressing '#'\n"
233 " 'P' -- always prompt for the pin even if it is specified\n"
234 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
235 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
236 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
237 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
239 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
240 " 't' -- set talk only mode. (Talk only, no listening)\n"
241 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
242 " 'w' -- wait until the marked user enters the conference\n"
243 " 'x' -- close the conference when last marked user exits\n"
244 " 'X' -- allow user to exit the conference by entering a valid single\n"
245 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
246 " if that variable is not defined.\n"
247 " '1' -- do not play message when first person enters\n";
249 static const char *descrip2 =
250 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
251 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
252 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
253 "the channel, unless priority n+1 exists, in which case priority progress will\n"
255 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
257 static const char *descrip3 =
258 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
259 " 'e' -- Eject last user that joined\n"
260 " 'k' -- Kick one user out of conference\n"
261 " 'K' -- Kick all users out of conference\n"
262 " 'l' -- Unlock conference\n"
263 " 'L' -- Lock conference\n"
264 " 'm' -- Unmute one user\n"
265 " 'M' -- Mute one user\n"
266 " 'n' -- Unmute all users in the conference\n"
267 " 'N' -- Mute all non-admin users in the conference\n"
268 " 'r' -- Reset one user's volume settings\n"
269 " 'R' -- Reset all users volume settings\n"
270 " 's' -- Lower entire conference speaking volume\n"
271 " 'S' -- Raise entire conference speaking volume\n"
272 " 't' -- Lower one user's talk volume\n"
273 " 'T' -- Lower all users talk volume\n"
274 " 'u' -- Lower one user's listen volume\n"
275 " 'U' -- Lower all users listen volume\n"
276 " 'v' -- Lower entire conference listening volume\n"
277 " 'V' -- Raise entire conference listening volume\n"
280 static const char *descripslas =
281 " SLAS(sla[,options]): Run Shared Line Appearance for station\n"
282 "Runs the share line appearance for a station calling in. If there are no\n"
283 "other participants in the conference, the trunk is called and is dumped into\n"
286 static const char *descripslat =
287 " SLAT(sla[,options]): Run Shared Line Appearance for trunk\n"
288 "Runs the share line appearance for a trunk calling in. If there are no\n"
289 "other participants in the conference, all member stations are invited into\n"
292 #define CONFIG_FILE_NAME "meetme.conf"
293 #define CONFIG_FILE_NAME_SLA "sla.conf"
297 /*! \brief The MeetMe Conference object */
298 struct ast_conference {
299 ast_mutex_t playlock; /*!< Conference specific lock (players) */
300 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
301 char confno[AST_MAX_EXTENSION]; /*!< Conference */
302 struct ast_channel *chan; /*!< Announcements channel */
303 struct ast_channel *lchan; /*!< Listen/Record channel */
304 int fd; /*!< Announcements fd */
305 int zapconf; /*!< Zaptel Conf # */
306 int users; /*!< Number of active users */
307 int markedusers; /*!< Number of marked users */
308 time_t start; /*!< Start time (s) */
309 int refcount; /*!< reference count of usage */
310 enum recording_state recording:2; /*!< recording status */
311 unsigned int isdynamic:1; /*!< Created on the fly? */
312 unsigned int locked:1; /*!< Is the conference locked? */
313 pthread_t recordthread; /*!< thread for recording */
314 pthread_attr_t attr; /*!< thread attribute */
315 const char *recordingfilename; /*!< Filename to record the Conference into */
316 const char *recordingformat; /*!< Format to record the Conference in */
317 char pin[AST_MAX_EXTENSION]; /*!< If protected by a PIN */
318 char pinadmin[AST_MAX_EXTENSION]; /*!< If protected by a admin PIN */
319 struct ast_frame *transframe[32];
320 struct ast_frame *origframe;
321 struct ast_trans_pvt *transpath[32];
322 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
323 AST_LIST_ENTRY(ast_conference) list;
326 static AST_LIST_HEAD_STATIC(confs, ast_conference);
329 int desired; /*!< Desired volume adjustment */
330 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
333 struct ast_conf_user {
334 int user_no; /*!< User Number */
335 int userflags; /*!< Flags as set in the conference */
336 int adminflags; /*!< Flags set by the Admin */
337 struct ast_channel *chan; /*!< Connected channel */
338 int talking; /*!< Is user talking */
339 int zapchannel; /*!< Is a Zaptel channel */
340 char usrvalue[50]; /*!< Custom User Value */
341 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
342 int control; /*! Queue Control for transmission */
343 int dtmf; /*! Queue DTMF for transmission */
344 time_t jointime; /*!< Time the user joined the conference */
346 struct volume listen;
347 AST_LIST_ENTRY(ast_conf_user) list;
350 /*! SLA station - one device in an SLA configuration */
351 struct ast_sla_station {
352 ASTOBJ_COMPONENTS(struct ast_sla_station);
357 struct ast_sla_station_box {
358 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla_station);
361 /*! SLA - Shared Line Apperance object. These consist of one trunk (outbound line)
362 and stations that receive incoming calls and place outbound calls over the trunk
365 ASTOBJ_COMPONENTS (struct ast_sla);
366 struct ast_sla_station_box stations; /*!< Stations connected to this SLA */
367 char confname[80]; /*!< Name for this SLA bridge */
368 char trunkdest[256]; /*!< Device (channel) identifier for the trunk line */
369 char trunktech[20]; /*!< Technology used for the trunk (channel driver) */
373 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla);
376 static int audio_buffers; /*!< The number of audio buffers to be allocated on pseudo channels
379 /*! The number of audio buffers to be allocated on pseudo channels
380 * when in a conference */
381 static int audio_buffers;
383 /*! Map 'volume' levels from -5 through +5 into
384 * decibel (dB) settings for channel drivers
385 * Note: these are not a straight linear-to-dB
386 * conversion... the numbers have been modified
387 * to give the user a better level of adjustability
389 static signed char gain_map[] = {
404 static int admin_exec(struct ast_channel *chan, void *data);
405 static void *recordthread(void *args);
407 static char *istalking(int x)
412 return "(unmonitored)";
414 return "(not talking)";
417 static int careful_write(int fd, unsigned char *data, int len, int block)
424 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
425 res = ioctl(fd, ZT_IOMUX, &x);
429 res = write(fd, data, len);
431 if (errno != EAGAIN) {
432 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
444 static int set_talk_volume(struct ast_conf_user *user, int volume)
446 signed char gain_adjust;
448 /* attempt to make the adjustment in the channel driver;
449 if successful, don't adjust in the frame reading routine
451 gain_adjust = gain_map[volume + 5];
453 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
456 static int set_listen_volume(struct ast_conf_user *user, int volume)
458 signed char gain_adjust;
460 /* attempt to make the adjustment in the channel driver;
461 if successful, don't adjust in the frame reading routine
463 gain_adjust = gain_map[volume + 5];
465 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
468 static void tweak_volume(struct volume *vol, enum volume_action action)
472 switch (vol->desired) {
487 switch (vol->desired) {
503 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
505 tweak_volume(&user->talk, action);
506 /* attempt to make the adjustment in the channel driver;
507 if successful, don't adjust in the frame reading routine
509 if (!set_talk_volume(user, user->talk.desired))
510 user->talk.actual = 0;
512 user->talk.actual = user->talk.desired;
515 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
517 tweak_volume(&user->listen, action);
518 /* attempt to make the adjustment in the channel driver;
519 if successful, don't adjust in the frame reading routine
521 if (!set_listen_volume(user, user->listen.desired))
522 user->listen.actual = 0;
524 user->listen.actual = user->listen.desired;
527 static void reset_volumes(struct ast_conf_user *user)
529 signed char zero_volume = 0;
531 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
532 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
535 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
541 if (!chan->_softhangup)
542 res = ast_autoservice_start(chan);
544 AST_LIST_LOCK(&confs);
560 careful_write(conf->fd, data, len, 1);
563 AST_LIST_UNLOCK(&confs);
566 ast_autoservice_stop(chan);
569 static void station_destroy(struct ast_sla_station *station)
574 static void sla_destroy(struct ast_sla *sla)
576 ASTOBJ_CONTAINER_DESTROYALL(&sla->stations, station_destroy);
577 ASTOBJ_CONTAINER_DESTROY(&sla->stations);
581 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
583 struct ast_conference *cnf;
584 struct zt_confinfo ztc;
586 AST_LIST_LOCK(&confs);
588 AST_LIST_TRAVERSE(&confs, cnf, list) {
589 if (!strcmp(confno, cnf->confno))
593 if (!cnf && (make || dynamic)) {
595 if ((cnf = ast_calloc(1, sizeof(*cnf)))) {
596 ast_mutex_init(&cnf->playlock);
597 ast_mutex_init(&cnf->listenlock);
598 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
599 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
600 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
602 cnf->markedusers = 0;
603 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
605 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
606 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
607 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
609 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
610 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
612 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
618 memset(&ztc, 0, sizeof(ztc));
619 /* Setup a new zap conference */
622 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
623 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
624 ast_log(LOG_WARNING, "Error setting conference\n");
626 ast_hangup(cnf->chan);
633 cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
635 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
636 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
638 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
639 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
640 ast_log(LOG_WARNING, "Error setting conference\n");
641 ast_hangup(cnf->lchan);
645 /* Fill the conference struct */
646 cnf->start = time(NULL);
647 cnf->zapconf = ztc.confno;
648 cnf->isdynamic = dynamic ? 1 : 0;
649 if (option_verbose > 2)
650 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
651 AST_LIST_INSERT_HEAD(&confs, cnf, list);
656 cnf->refcount += refcount;
658 AST_LIST_UNLOCK(&confs);
662 static int confs_show(int fd, int argc, char **argv)
664 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
666 return RESULT_SUCCESS;
669 /*! \brief CLI command for showing SLAs */
670 static int sla_show(int fd, int argc, char *argv[])
674 return RESULT_SHOWUSAGE;
676 ast_cli(fd, "Shared line appearances:\n");
677 ASTOBJ_CONTAINER_TRAVERSE(&slas, 1, {
678 ASTOBJ_RDLOCK(iterator);
679 ast_cli(fd, "SLA %s\n", iterator->name);
680 if (ast_strlen_zero(iterator->trunkdest) || ast_strlen_zero(iterator->trunktech))
681 ast_cli(fd, " Trunk => <unspecified>\n");
683 ast_cli(fd, " Trunk => %s/%s\n", iterator->trunktech, iterator->trunkdest);
685 ASTOBJ_CONTAINER_TRAVERSE(&sla->stations, 1, {
686 ast_cli(fd, " Station: %s/%s\n", iterator->tech, iterator->dest);
688 ASTOBJ_UNLOCK(iterator);
691 return RESULT_SUCCESS;
694 static char show_confs_usage[] =
695 "Deprecated! Please use 'meetme' instead.\n";
697 static struct ast_cli_entry cli_show_confs = {
698 { "show", "conferences", NULL }, confs_show,
699 "Show status of conferences", show_confs_usage, NULL };
702 static char sla_show_usage[] =
704 " Lists status of all shared line appearances\n";
706 static struct ast_cli_entry cli_sla_show = {
707 { "sla", "show", NULL }, sla_show,
708 "Show status of Shared Line Appearances", sla_show_usage, NULL };
710 static int conf_cmd(int fd, int argc, char **argv)
712 /* Process the command */
713 struct ast_conference *cnf;
714 struct ast_conf_user *user;
716 int i = 0, total = 0;
718 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
719 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
720 char cmdline[1024] = "";
723 ast_cli(fd, "Invalid Arguments.\n");
724 /* Check for length so no buffer will overflow... */
725 for (i = 0; i < argc; i++) {
726 if (strlen(argv[i]) > 100)
727 ast_cli(fd, "Invalid Arguments.\n");
730 /* 'MeetMe': List all the conferences */
732 if (AST_LIST_EMPTY(&confs)) {
733 ast_cli(fd, "No active MeetMe conferences.\n");
734 return RESULT_SUCCESS;
736 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
737 AST_LIST_TRAVERSE(&confs, cnf, list) {
738 if (cnf->markedusers == 0)
739 strcpy(cmdline, "N/A ");
741 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
742 hr = (now - cnf->start) / 3600;
743 min = ((now - cnf->start) % 3600) / 60;
744 sec = (now - cnf->start) % 60;
746 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
750 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
751 return RESULT_SUCCESS;
754 return RESULT_SHOWUSAGE;
755 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
756 if (strstr(argv[1], "lock")) {
757 if (strcmp(argv[1], "lock") == 0) {
759 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
762 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
764 } else if (strstr(argv[1], "mute")) {
766 return RESULT_SHOWUSAGE;
767 if (strcmp(argv[1], "mute") == 0) {
769 if (strcmp(argv[3], "all") == 0) {
770 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
772 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
773 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
777 if (strcmp(argv[3], "all") == 0) {
778 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
780 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
781 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
784 } else if (strcmp(argv[1], "kick") == 0) {
786 return RESULT_SHOWUSAGE;
787 if (strcmp(argv[3], "all") == 0) {
789 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
791 /* Kick a single user */
792 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
793 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
795 } else if(strcmp(argv[1], "list") == 0) {
796 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
797 /* List all the users in a conference */
798 if (AST_LIST_EMPTY(&confs)) {
800 ast_cli(fd, "No active conferences.\n");
801 return RESULT_SUCCESS;
803 /* Find the right conference */
804 AST_LIST_TRAVERSE(&confs, cnf, list) {
805 if (strcmp(cnf->confno, argv[2]) == 0)
810 ast_cli(fd, "No such conference: %s.\n",argv[2]);
811 return RESULT_SUCCESS;
813 /* Show all the users */
814 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
816 hr = (now - user->jointime) / 3600;
817 min = ((now - user->jointime) % 3600) / 60;
818 sec = (now - user->jointime) % 60;
820 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
822 S_OR(user->chan->cid.cid_num, "<unknown>"),
823 S_OR(user->chan->cid.cid_name, "<no name>"),
825 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
826 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
827 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
828 istalking(user->talking),
829 user->userflags & CONFFLAG_HOLD ? " (On Hold) " : "", hr, min, sec);
831 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
833 S_OR(user->chan->cid.cid_num, ""),
834 S_OR(user->chan->cid.cid_name, ""),
836 user->userflags & CONFFLAG_ADMIN ? "1" : "",
837 user->userflags & CONFFLAG_MONITOR ? "1" : "",
838 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
839 user->talking, hr, min, sec);
843 ast_cli(fd,"%d users in that conference.\n",cnf->users);
845 return RESULT_SUCCESS;
847 return RESULT_SHOWUSAGE;
848 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
849 admin_exec(NULL, cmdline);
854 static char *complete_confcmd(const char *line, const char *word, int pos, int state)
856 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
858 int len = strlen(word);
860 struct ast_conference *cnf = NULL;
861 struct ast_conf_user *usr = NULL;
864 char *myline, *ret = NULL;
866 if (pos == 1) { /* Command */
867 return ast_cli_complete(word, cmds, state);
868 } else if (pos == 2) { /* Conference Number */
869 AST_LIST_LOCK(&confs);
870 AST_LIST_TRAVERSE(&confs, cnf, list) {
871 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
876 ret = ast_strdup(ret); /* dup before releasing the lock */
877 AST_LIST_UNLOCK(&confs);
879 } else if (pos == 3) {
880 /* User Number || Conf Command option*/
881 if (strstr(line, "mute") || strstr(line, "kick")) {
882 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
883 return strdup("all");
885 AST_LIST_LOCK(&confs);
887 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
888 myline = ast_strdupa(line);
889 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
890 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
894 AST_LIST_TRAVERSE(&confs, cnf, list) {
895 if (!strcmp(confno, cnf->confno))
900 /* Search for the user */
901 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
902 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
903 if (!strncasecmp(word, usrno, len) && ++which > state)
907 AST_LIST_UNLOCK(&confs);
908 return usr ? strdup(usrno) : NULL;
909 } else if ( strstr(line, "list") && ( 0 == state ) )
910 return strdup("concise");
916 static char conf_usage[] =
917 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
918 " Executes a command for the conference or on a conferee\n";
920 static struct ast_cli_entry cli_conf = {
921 {"meetme", NULL, NULL }, conf_cmd,
922 "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
924 static void conf_flush(int fd, struct ast_channel *chan)
928 /* read any frames that may be waiting on the channel
934 /* when no frames are available, this will wait
935 for 1 millisecond maximum
937 while (ast_waitfor(chan, 1)) {
941 else /* channel was hung up or something else happened */
946 /* flush any data sitting in the pseudo channel */
948 if (ioctl(fd, ZT_FLUSH, &x))
949 ast_log(LOG_WARNING, "Error flushing channel\n");
953 /* Remove the conference from the list and free it.
954 We assume that this was called while holding conflock. */
955 static int conf_free(struct ast_conference *conf)
959 AST_LIST_REMOVE(&confs, conf, list);
961 if (conf->recording == MEETME_RECORD_ACTIVE) {
962 conf->recording = MEETME_RECORD_TERMINATE;
963 AST_LIST_UNLOCK(&confs);
965 AST_LIST_LOCK(&confs);
966 if (conf->recording == MEETME_RECORD_OFF)
968 AST_LIST_UNLOCK(&confs);
972 for (x=0;x<AST_FRAME_BITS;x++) {
973 if (conf->transframe[x])
974 ast_frfree(conf->transframe[x]);
975 if (conf->transpath[x])
976 ast_translator_free_path(conf->transpath[x]);
979 ast_frfree(conf->origframe);
981 ast_hangup(conf->lchan);
983 ast_hangup(conf->chan);
992 static void conf_queue_dtmf(struct ast_conference *conf, int digit)
994 struct ast_conf_user *user;
995 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1000 static void conf_queue_control(struct ast_conference *conf, int control)
1002 struct ast_conf_user *user;
1003 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1004 user->control = control;
1009 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
1011 struct ast_conf_user *user = NULL;
1012 struct ast_conf_user *usr = NULL;
1014 struct zt_confinfo ztc, ztc_empty;
1015 struct ast_frame *f;
1016 struct ast_channel *c;
1017 struct ast_frame fr;
1025 int musiconhold = 0;
1028 int currentmarked = 0;
1031 int menu_active = 0;
1032 int using_pseudo = 0;
1037 struct ast_dsp *dsp=NULL;
1038 struct ast_app *app;
1039 const char *agifile;
1040 const char *agifiledefault = "conf-background.agi";
1041 char meetmesecs[30] = "";
1042 char exitcontext[AST_MAX_CONTEXT] = "";
1043 char recordingtmp[AST_MAX_EXTENSION] = "";
1044 char members[10] = "";
1047 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1048 char *buf = __buf + AST_FRIENDLY_OFFSET;
1050 if (!(user = ast_calloc(1, sizeof(*user)))) {
1051 AST_LIST_LOCK(&confs);
1053 if (!conf->refcount){
1056 AST_LIST_UNLOCK(&confs);
1060 if (confflags & CONFFLAG_RECORDCONF) {
1061 if (!conf->recordingfilename) {
1062 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1063 if (!conf->recordingfilename) {
1064 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1065 conf->recordingfilename = ast_strdupa(recordingtmp);
1067 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1068 if (!conf->recordingformat) {
1069 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1070 conf->recordingformat = ast_strdupa(recordingtmp);
1072 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1073 conf->confno, conf->recordingfilename, conf->recordingformat);
1077 if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
1078 pthread_attr_init(&conf->attr);
1079 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1080 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
1083 time(&user->jointime);
1085 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1086 /* Sorry, but this confernce is locked! */
1087 if (!ast_streamfile(chan, "conf-locked", chan->language))
1088 ast_waitstream(chan, "");
1092 if (confflags & CONFFLAG_MARKEDUSER)
1093 conf->markedusers++;
1095 ast_mutex_lock(&conf->playlock);
1097 if (AST_LIST_EMPTY(&conf->userlist))
1100 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1102 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1105 user->userflags = confflags;
1106 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1110 snprintf(members, sizeof(members), "%d", conf->users);
1111 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1113 /* This device changed state now - if this is the first user */
1114 if (conf->users == 1)
1115 ast_device_state_changed("meetme:%s", conf->confno);
1116 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1117 ast_device_state_changed("SLA:%s", conf->confno + 4);
1119 ast_mutex_unlock(&conf->playlock);
1121 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1122 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1123 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1124 else if (!ast_strlen_zero(chan->macrocontext))
1125 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1127 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1130 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1131 snprintf(user->namerecloc, sizeof(user->namerecloc),
1132 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1133 conf->confno, user->user_no);
1134 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1135 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1137 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1142 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1143 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1144 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1145 ast_waitstream(chan, "");
1146 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1147 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1148 ast_waitstream(chan, "");
1151 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1152 int keepplaying = 1;
1154 if (conf->users == 2) {
1155 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1156 res = ast_waitstream(chan, AST_DIGIT_ANY);
1163 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1164 res = ast_waitstream(chan, AST_DIGIT_ANY);
1171 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1177 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1178 res = ast_waitstream(chan, AST_DIGIT_ANY);
1187 ast_indicate(chan, -1);
1189 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1190 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1194 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1195 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1199 retryzap = strcasecmp(chan->tech->type, "Zap");
1200 user->zapchannel = !retryzap;
1203 origfd = chan->fds[0];
1205 fd = open("/dev/zap/pseudo", O_RDWR);
1207 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1211 /* Make non-blocking */
1212 flags = fcntl(fd, F_GETFL);
1214 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1218 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1219 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1223 /* Setup buffering information */
1224 memset(&bi, 0, sizeof(bi));
1225 bi.bufsize = CONF_SIZE/2;
1226 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1227 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1228 bi.numbufs = audio_buffers;
1229 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1230 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1235 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1236 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1242 /* XXX Make sure we're not running on a pseudo channel XXX */
1246 memset(&ztc, 0, sizeof(ztc));
1247 memset(&ztc_empty, 0, sizeof(ztc_empty));
1248 /* Check to see if we're in a conference... */
1250 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1251 ast_log(LOG_WARNING, "Error getting conference\n");
1256 /* Whoa, already in a conference... Retry... */
1258 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1263 memset(&ztc, 0, sizeof(ztc));
1264 /* Add us to the conference */
1266 ztc.confno = conf->zapconf;
1268 ast_mutex_lock(&conf->playlock);
1270 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1271 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1272 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1273 ast_waitstream(conf->chan, "");
1274 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1275 ast_waitstream(conf->chan, "");
1279 if (confflags & CONFFLAG_MONITOR)
1280 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1281 else if (confflags & CONFFLAG_TALKER)
1282 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1284 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1286 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1287 ast_log(LOG_WARNING, "Error setting conference\n");
1289 ast_mutex_unlock(&conf->playlock);
1292 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1295 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1300 chan->name, chan->uniqueid, conf->confno, user->user_no);
1304 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1306 if (!(confflags & CONFFLAG_QUIET))
1307 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1308 conf_play(chan, conf, ENTER);
1311 ast_mutex_unlock(&conf->playlock);
1313 conf_flush(fd, chan);
1315 if (confflags & CONFFLAG_AGI) {
1316 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1317 or use default filename of conf-background.agi */
1319 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1321 agifile = agifiledefault;
1323 if (user->zapchannel) {
1324 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1326 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1328 /* Find a pointer to the agi app and execute the script */
1329 app = pbx_findapp("agi");
1331 char *s = ast_strdupa(agifile);
1332 ret = pbx_exec(chan, app, s);
1334 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1337 if (user->zapchannel) {
1338 /* Remove CONFMUTE mode on Zap channel */
1340 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1343 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1344 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1346 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1348 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1349 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1353 int menu_was_active = 0;
1358 /* if we have just exited from the menu, and the user had a channel-driver
1359 volume adjustment, restore it
1361 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1362 set_talk_volume(user, user->listen.desired);
1364 menu_was_active = menu_active;
1366 currentmarked = conf->markedusers;
1367 if (!(confflags & CONFFLAG_QUIET) &&
1368 (confflags & CONFFLAG_MARKEDUSER) &&
1369 (confflags & CONFFLAG_WAITMARKED) &&
1371 if (currentmarked == 1 && conf->users > 1) {
1372 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1373 if (conf->users - 1 == 1) {
1374 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1375 ast_waitstream(chan, "");
1377 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1378 ast_waitstream(chan, "");
1381 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1382 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1383 ast_waitstream(chan, "");
1386 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1389 /* Update the struct with the actual confflags */
1390 user->userflags = confflags;
1392 if (confflags & CONFFLAG_WAITMARKED) {
1393 if(currentmarked == 0) {
1394 if (lastmarked != 0) {
1395 if (!(confflags & CONFFLAG_QUIET))
1396 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1397 ast_waitstream(chan, "");
1398 if(confflags & CONFFLAG_MARKEDEXIT)
1401 ztc.confmode = ZT_CONF_CONF;
1402 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1403 ast_log(LOG_WARNING, "Error setting conference\n");
1409 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1410 ast_moh_start(chan, NULL);
1413 ztc.confmode = ZT_CONF_CONF;
1414 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1415 ast_log(LOG_WARNING, "Error setting conference\n");
1420 } else if(currentmarked >= 1 && lastmarked == 0) {
1421 if (confflags & CONFFLAG_MONITOR)
1422 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1423 else if (confflags & CONFFLAG_TALKER)
1424 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1426 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1427 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1428 ast_log(LOG_WARNING, "Error setting conference\n");
1432 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1436 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1437 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1438 ast_waitstream(chan, "");
1439 conf_play(chan, conf, ENTER);
1444 /* trying to add moh for single person conf */
1445 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1446 if (conf->users == 1) {
1447 if (musiconhold == 0) {
1448 ast_moh_start(chan, NULL);
1459 /* Leave if the last marked user left */
1460 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1465 /* Check if my modes have changed */
1467 /* If I should be muted but am still talker, mute me */
1468 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1469 ztc.confmode ^= ZT_CONF_TALKER;
1470 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1471 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1476 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1482 chan->name, chan->uniqueid, conf->confno, user->user_no);
1485 /* If I should be un-muted but am not talker, un-mute me */
1486 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1487 ztc.confmode |= ZT_CONF_TALKER;
1488 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1489 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1494 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1500 chan->name, chan->uniqueid, conf->confno, user->user_no);
1503 /* If I have been kicked, exit the conference */
1504 if (user->adminflags & ADMINFLAG_KICKME) {
1505 //You have been kicked.
1506 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1507 ast_waitstream(chan, "");
1513 if (c->fds[0] != origfd) {
1515 /* Kill old pseudo */
1519 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1520 retryzap = strcasecmp(c->tech->type, "Zap");
1521 user->zapchannel = !retryzap;
1524 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1525 f = ast_read_noaudio(c);
1530 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1531 if (user->talk.actual)
1532 ast_frame_adjust_volume(f, user->talk.actual);
1534 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1537 if (user->talking == -1)
1540 res = ast_dsp_silence(dsp, f, &totalsilence);
1541 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1543 if (confflags & CONFFLAG_MONITORTALKER)
1544 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1550 chan->name, chan->uniqueid, conf->confno, user->user_no);
1552 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1554 if (confflags & CONFFLAG_MONITORTALKER)
1555 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1561 chan->name, chan->uniqueid, conf->confno, user->user_no);
1565 /* Absolutely do _not_ use careful_write here...
1566 it is important that we read data from the channel
1567 as fast as it arrives, and feed it into the conference.
1568 The buffering in the pseudo channel will take care of any
1569 timing differences, unless they are so drastic as to lose
1570 audio frames (in which case carefully writing would only
1571 have delayed the audio even further).
1573 /* As it turns out, we do want to use careful write. We just
1574 don't want to block, but we do want to at least *try*
1575 to write out all the samples.
1577 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1578 careful_write(fd, f->data, f->datalen, 0);
1580 } else if ((f->frametype == AST_FRAME_DTMF) &&
1581 (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) {
1582 conf_queue_dtmf(conf, f->subclass);
1583 } else if ((f->frametype == AST_FRAME_CONTROL) &&
1584 (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) {
1585 conf_queue_control(conf, f->subclass);
1586 if (f->subclass == AST_CONTROL_HOLD)
1587 confflags |= CONFFLAG_HOLD;
1588 else if (f->subclass == AST_CONTROL_UNHOLD)
1589 confflags &= ~CONFFLAG_HOLD;
1590 user->userflags = confflags;
1591 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1594 tmp[0] = f->subclass;
1596 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1597 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1601 } else if (option_debug > 1)
1602 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1603 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1607 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1608 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1609 ast_log(LOG_WARNING, "Error setting conference\n");
1615 /* if we are entering the menu, and the user has a channel-driver
1616 volume adjustment, clear it
1618 if (!menu_active && user->talk.desired && !user->talk.actual)
1619 set_talk_volume(user, 0);
1624 if ((confflags & CONFFLAG_ADMIN)) {
1628 /* Record this sound! */
1629 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1630 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1631 ast_stopstream(chan);
1638 case '1': /* Un/Mute */
1641 /* for admin, change both admin and use flags */
1642 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1643 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1645 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1647 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1648 if (!ast_streamfile(chan, "conf-muted", chan->language))
1649 ast_waitstream(chan, "");
1651 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1652 ast_waitstream(chan, "");
1655 case '2': /* Un/Lock the Conference */
1659 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1660 ast_waitstream(chan, "");
1663 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1664 ast_waitstream(chan, "");
1667 case '3': /* Eject last user */
1669 usr = AST_LIST_LAST(&conf->userlist);
1670 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1671 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1672 ast_waitstream(chan, "");
1674 usr->adminflags |= ADMINFLAG_KICKME;
1675 ast_stopstream(chan);
1678 tweak_listen_volume(user, VOL_DOWN);
1681 tweak_listen_volume(user, VOL_UP);
1684 tweak_talk_volume(user, VOL_DOWN);
1690 tweak_talk_volume(user, VOL_UP);
1694 /* Play an error message! */
1695 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1696 ast_waitstream(chan, "");
1704 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1705 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1706 ast_stopstream(chan);
1713 case '1': /* Un/Mute */
1716 /* user can only toggle the self-muted state */
1717 user->adminflags ^= ADMINFLAG_SELFMUTED;
1719 /* they can't override the admin mute state */
1720 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1721 if (!ast_streamfile(chan, "conf-muted", chan->language))
1722 ast_waitstream(chan, "");
1724 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1725 ast_waitstream(chan, "");
1729 tweak_listen_volume(user, VOL_DOWN);
1732 tweak_listen_volume(user, VOL_UP);
1735 tweak_talk_volume(user, VOL_DOWN);
1741 tweak_talk_volume(user, VOL_UP);
1745 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1746 ast_waitstream(chan, "");
1752 ast_moh_start(chan, NULL);
1754 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1755 ast_log(LOG_WARNING, "Error setting conference\n");
1761 conf_flush(fd, chan);
1762 } else if (option_debug) {
1764 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1765 chan->name, f->frametype, f->subclass);
1768 } else if (outfd > -1) {
1769 if (user->control) {
1770 switch(user->control) {
1771 case AST_CONTROL_RINGING:
1772 case AST_CONTROL_PROGRESS:
1773 case AST_CONTROL_PROCEEDING:
1774 ast_indicate(chan, user->control);
1776 case AST_CONTROL_ANSWER:
1777 if (chan->_state != AST_STATE_UP)
1782 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1783 ast_device_state_changed("SLA:%s", conf->confno + 4);
1787 memset(&fr, 0, sizeof(fr));
1788 fr.frametype = AST_FRAME_DTMF;
1789 fr.subclass = user->dtmf;
1790 if (ast_write(chan, &fr) < 0) {
1791 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1796 res = read(outfd, buf, CONF_SIZE);
1798 memset(&fr, 0, sizeof(fr));
1799 fr.frametype = AST_FRAME_VOICE;
1800 fr.subclass = AST_FORMAT_SLINEAR;
1804 fr.offset = AST_FRIENDLY_OFFSET;
1805 if (!user->listen.actual &&
1806 ((confflags & CONFFLAG_MONITOR) ||
1807 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1808 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1811 for (index=0;index<AST_FRAME_BITS;index++)
1812 if (chan->rawwriteformat & (1 << index))
1814 if (index >= AST_FRAME_BITS)
1815 goto bailoutandtrynormal;
1816 ast_mutex_lock(&conf->listenlock);
1817 if (!conf->transframe[index]) {
1818 if (conf->origframe) {
1819 if (!conf->transpath[index])
1820 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1821 if (conf->transpath[index]) {
1822 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1823 if (!conf->transframe[index])
1824 conf->transframe[index] = &ast_null_frame;
1828 if (conf->transframe[index]) {
1829 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1830 if (ast_write(chan, conf->transframe[index]))
1831 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1834 ast_mutex_unlock(&conf->listenlock);
1835 goto bailoutandtrynormal;
1837 ast_mutex_unlock(&conf->listenlock);
1839 bailoutandtrynormal:
1840 if (user->listen.actual)
1841 ast_frame_adjust_volume(&fr, user->listen.actual);
1842 if (ast_write(chan, &fr) < 0) {
1843 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1847 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1849 lastmarked = currentmarked;
1859 /* Take out of conference */
1863 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1864 ast_log(LOG_WARNING, "Error setting conference\n");
1868 reset_volumes(user);
1870 AST_LIST_LOCK(&confs);
1871 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1872 conf_play(chan, conf, LEAVE);
1874 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1875 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1876 if ((conf->chan) && (conf->users > 1)) {
1877 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1878 ast_waitstream(conf->chan, "");
1879 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1880 ast_waitstream(conf->chan, "");
1882 ast_filedelete(user->namerecloc, NULL);
1885 AST_LIST_UNLOCK(&confs);
1888 AST_LIST_LOCK(&confs);
1893 if (user->user_no) { /* Only cleanup users who really joined! */
1895 hr = (now - user->jointime) / 3600;
1896 min = ((now - user->jointime) % 3600) / 60;
1897 sec = (now - user->jointime) % 60;
1900 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1905 "CallerIDnum: %s\r\n"
1906 "CallerIDname: %s\r\n"
1907 "Duration: %ld\r\n",
1908 chan->name, chan->uniqueid, conf->confno,
1910 S_OR(user->chan->cid.cid_num, "<unknown>"),
1911 S_OR(user->chan->cid.cid_name, "<unknown>"),
1912 (now - user->jointime));
1918 snprintf(members, sizeof(members), "%d", conf->users);
1919 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1920 if (confflags & CONFFLAG_MARKEDUSER)
1921 conf->markedusers--;
1922 /* Remove ourselves from the list */
1923 AST_LIST_REMOVE(&conf->userlist, user, list);
1924 if (AST_LIST_EMPTY(&conf->userlist)) {
1925 /* close this one when no more users and no references*/
1926 if (!conf->refcount)
1929 /* Return the number of seconds the user was in the conf */
1930 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1931 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1933 /* This device changed state now */
1934 if (!conf->users) /* If there are no more members */
1935 ast_device_state_changed("meetme:%s", conf->confno);
1936 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1937 ast_device_state_changed("SLA:%s", conf->confno + 4);
1940 AST_LIST_UNLOCK(&confs);
1945 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1946 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1948 struct ast_variable *var;
1949 struct ast_conference *cnf;
1951 /* Check first in the conference list */
1952 AST_LIST_LOCK(&confs);
1953 AST_LIST_TRAVERSE(&confs, cnf, list) {
1954 if (!strcmp(confno, cnf->confno))
1958 cnf->refcount += refcount;
1960 AST_LIST_UNLOCK(&confs);
1963 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1965 cnf = ast_calloc(1, sizeof(struct ast_conference));
1967 ast_log(LOG_ERROR, "Out of memory\n");
1971 var = ast_load_realtime("meetme", "confno", confno, NULL);
1973 if (!strcasecmp(var->name, "confno")) {
1974 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1975 } else if (!strcasecmp(var->name, "pin")) {
1976 pin = ast_strdupa(var->value);
1977 } else if (!strcasecmp(var->name, "adminpin")) {
1978 pinadmin = ast_strdupa(var->value);
1982 ast_variables_destroy(var);
1984 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1988 if (confflags && !cnf->chan &&
1989 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1990 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1991 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1992 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1995 if (confflags && !cnf->chan &&
1996 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1997 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1998 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2006 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2007 char *dynamic_pin, int refcount, struct ast_flags *confflags)
2009 struct ast_config *cfg;
2010 struct ast_variable *var;
2011 struct ast_conference *cnf;
2013 AST_DECLARE_APP_ARGS(args,
2014 AST_APP_ARG(confno);
2016 AST_APP_ARG(pinadmin);
2019 /* Check first in the conference list */
2020 AST_LIST_LOCK(&confs);
2021 AST_LIST_TRAVERSE(&confs, cnf, list) {
2022 if (!strcmp(confno, cnf->confno))
2026 cnf->refcount += refcount;
2028 AST_LIST_UNLOCK(&confs);
2032 /* No need to parse meetme.conf */
2033 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2035 if (dynamic_pin[0] == 'q') {
2036 /* Query the user to enter a PIN */
2037 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
2040 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2042 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2045 /* Check the config */
2046 cfg = ast_config_load(CONFIG_FILE_NAME);
2048 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2051 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2052 if (strcasecmp(var->name, "conf"))
2055 if (!(parse = ast_strdupa(var->value)))
2058 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2059 if (!strcasecmp(args.confno, confno)) {
2060 /* Bingo it's a valid conference */
2061 cnf = build_conf(args.confno,
2063 S_OR(args.pinadmin, ""),
2064 make, dynamic, refcount);
2069 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2071 ast_config_destroy(cfg);
2073 } else if (dynamic_pin) {
2074 /* Correct for the user selecting 'D' instead of 'd' to have
2075 someone join into a conference that has already been created
2077 if (dynamic_pin[0] == 'q')
2078 dynamic_pin[0] = '\0';
2082 if (confflags && !cnf->chan &&
2083 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2084 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2085 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2086 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2089 if (confflags && !cnf->chan &&
2090 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2091 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2092 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2099 /*! \brief The MeetmeCount application */
2100 static int count_exec(struct ast_channel *chan, void *data)
2102 struct localuser *u;
2104 struct ast_conference *conf;
2108 AST_DECLARE_APP_ARGS(args,
2109 AST_APP_ARG(confno);
2110 AST_APP_ARG(varname);
2113 if (ast_strlen_zero(data)) {
2114 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2120 if (!(localdata = ast_strdupa(data))) {
2121 LOCAL_USER_REMOVE(u);
2125 AST_STANDARD_APP_ARGS(args, localdata);
2127 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
2130 count = conf->users;
2134 if (!ast_strlen_zero(args.varname)){
2135 /* have var so load it and exit */
2136 snprintf(val, sizeof(val), "%d",count);
2137 pbx_builtin_setvar_helper(chan, args.varname, val);
2139 if (chan->_state != AST_STATE_UP)
2141 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2143 LOCAL_USER_REMOVE(u);
2148 /*! \brief The meetme() application */
2149 static int conf_exec(struct ast_channel *chan, void *data)
2152 struct localuser *u;
2153 char confno[AST_MAX_EXTENSION] = "";
2156 struct ast_conference *cnf;
2157 struct ast_flags confflags = {0};
2159 int empty = 0, empty_no_pin = 0;
2160 int always_prompt = 0;
2161 char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
2162 AST_DECLARE_APP_ARGS(args,
2163 AST_APP_ARG(confno);
2164 AST_APP_ARG(options);
2170 if (ast_strlen_zero(data)) {
2177 if (chan->_state != AST_STATE_UP)
2180 info = ast_strdupa(notdata);
2182 AST_STANDARD_APP_ARGS(args, info);
2185 ast_copy_string(confno, args.confno, sizeof(confno));
2186 if (ast_strlen_zero(confno)) {
2192 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2195 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
2196 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2197 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2198 strcpy(the_pin, "q");
2200 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2201 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2202 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2209 int i, map[1024] = { 0, };
2210 struct ast_config *cfg;
2211 struct ast_variable *var;
2214 AST_LIST_LOCK(&confs);
2215 AST_LIST_TRAVERSE(&confs, cnf, list) {
2216 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2217 /* Disqualify in use conference */
2218 if (confno_int >= 0 && confno_int < 1024)
2222 AST_LIST_UNLOCK(&confs);
2224 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2225 if ((empty_no_pin) || (!dynamic)) {
2226 cfg = ast_config_load(CONFIG_FILE_NAME);
2228 var = ast_variable_browse(cfg, "rooms");
2230 if (!strcasecmp(var->name, "conf")) {
2231 char *stringp = ast_strdupa(var->value);
2233 char *confno_tmp = strsep(&stringp, "|,");
2235 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2236 if ((confno_int >= 0) && (confno_int < 1024)) {
2237 if (stringp && empty_no_pin) {
2243 /* For static: run through the list and see if this conference is empty */
2244 AST_LIST_LOCK(&confs);
2245 AST_LIST_TRAVERSE(&confs, cnf, list) {
2246 if (!strcmp(confno_tmp, cnf->confno)) {
2247 /* The conference exists, therefore it's not empty */
2252 AST_LIST_UNLOCK(&confs);
2254 /* At this point, we have a confno_tmp (static conference) that is empty */
2255 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2256 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2257 * Case 2: empty_no_pin and pin is blank (but not NULL)
2258 * Case 3: not empty_no_pin
2260 ast_copy_string(confno, confno_tmp, sizeof(confno));
2262 /* XXX the map is not complete (but we do have a confno) */
2270 ast_config_destroy(cfg);
2274 /* Select first conference number not in use */
2275 if (ast_strlen_zero(confno) && dynamic) {
2276 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2278 snprintf(confno, sizeof(confno), "%d", i);
2285 if (ast_strlen_zero(confno)) {
2286 res = ast_streamfile(chan, "conf-noempty", chan->language);
2288 ast_waitstream(chan, "");
2290 if (sscanf(confno, "%d", &confno_int) == 1) {
2291 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2293 ast_waitstream(chan, "");
2294 res = ast_say_digits(chan, confno_int, "", chan->language);
2297 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2302 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2303 /* Prompt user for conference number */
2304 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2306 /* Don't try to validate when we catch an error */
2312 if (!ast_strlen_zero(confno)) {
2313 /* Check the validity of the conference */
2314 cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2316 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2319 res = ast_streamfile(chan, "conf-invalid", chan->language);
2321 ast_waitstream(chan, "");
2326 if ((!ast_strlen_zero(cnf->pin) &&
2327 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2328 (!ast_strlen_zero(cnf->pinadmin) &&
2329 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2330 char pin[AST_MAX_EXTENSION]="";
2333 /* Allow the pin to be retried up to 3 times */
2334 for (j = 0; j < 3; j++) {
2335 if (*the_pin && (always_prompt == 0)) {
2336 ast_copy_string(pin, the_pin, sizeof(pin));
2339 /* Prompt user for pin if pin is required */
2340 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2343 if (!strcasecmp(pin, cnf->pin) ||
2344 (!ast_strlen_zero(cnf->pinadmin) &&
2345 !strcasecmp(pin, cnf->pinadmin))) {
2348 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2349 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2350 /* Run the conference */
2351 res = conf_run(chan, cnf, confflags.flags);
2355 if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
2356 res = ast_waitstream(chan, AST_DIGIT_ANY);
2358 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2362 AST_LIST_LOCK(&confs);
2364 if (!cnf->refcount){
2367 AST_LIST_UNLOCK(&confs);
2377 /* failed when getting the pin */
2380 /* see if we need to get rid of the conference */
2381 AST_LIST_LOCK(&confs);
2383 if (!cnf->refcount) {
2386 AST_LIST_UNLOCK(&confs);
2390 /* Don't retry pin with a static pin */
2391 if (*the_pin && (always_prompt==0)) {
2396 /* No pin required */
2399 /* Run the conference */
2400 res = conf_run(chan, cnf, confflags.flags);
2404 } while (allowretry);
2406 LOCAL_USER_REMOVE(u);
2411 struct sla_originate_helper {
2420 static void *sla_originate(void *data)
2422 struct sla_originate_helper *in = data;
2424 struct ast_channel *chan = NULL;
2426 ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, 99999, in->app, in->appdata, &reason, 1,
2427 S_OR(in->cid_num, NULL),
2428 S_OR(in->cid_name, NULL),
2430 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2432 ast_channel_unlock(chan);
2437 /*! Call in stations and trunk to the SLA */
2438 static void do_invite(struct ast_channel *orig, struct ast_sla *sla, const char *tech, const char *dest, const char *app)
2440 struct sla_originate_helper *slal;
2441 pthread_attr_t attr;
2444 if (!(slal = ast_calloc(1, sizeof(*slal))))
2447 ast_copy_string(slal->tech, tech, sizeof(slal->tech));
2448 ast_copy_string(slal->data, dest, sizeof(slal->data));
2449 ast_copy_string(slal->app, app, sizeof(slal->app));
2450 ast_copy_string(slal->appdata, sla->name, sizeof(slal->appdata));
2451 if (orig->cid.cid_num)
2452 ast_copy_string(slal->cid_num, orig->cid.cid_num, sizeof(slal->cid_num));
2453 if (orig->cid.cid_name)
2454 ast_copy_string(slal->cid_name, orig->cid.cid_name, sizeof(slal->cid_name));
2455 pthread_attr_init(&attr);
2456 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2457 ast_pthread_create(&th, &attr, sla_originate, slal);
2460 static void invite_stations(struct ast_channel *orig, struct ast_sla *sla)
2462 ASTOBJ_CONTAINER_TRAVERSE(&sla->stations, 1, {
2463 do_invite(orig, sla,iterator->tech, iterator->dest, "SLAS");
2467 static void invite_trunk(struct ast_channel *orig, struct ast_sla *sla)
2469 do_invite(orig, sla,sla->trunktech, sla->trunkdest, "SLAT");
2473 static int sla_checkforhold(struct ast_conference *conf, int hangup)
2475 struct ast_conf_user *user;
2476 struct ast_channel *onhold=NULL;
2478 int stationcount = 0;
2480 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2481 if (user->userflags & CONFFLAG_SLA_STATION) {
2483 if ((user->userflags & CONFFLAG_HOLD)) {
2485 onhold = user->chan;
2489 if ((holdcount == 1) && (stationcount == 1)) {
2492 ast_softhangup(onhold, AST_SOFTHANGUP_EXPLICIT);
2493 } else if (holdcount && (stationcount == holdcount))
2499 /*! \brief The slas()/slat() application */
2500 static int sla_exec(struct ast_channel *chan, void *data, int trunk)
2503 struct localuser *u;
2504 char confno[AST_MAX_EXTENSION] = "";
2505 struct ast_sla *sla;
2506 struct ast_conference *cnf;
2508 struct ast_flags confflags = {0};
2510 AST_DECLARE_APP_ARGS(args,
2511 AST_APP_ARG(confno);
2512 AST_APP_ARG(options);
2515 if (ast_strlen_zero(data)) {
2516 ast_log(LOG_WARNING, "SLA%c requires an argument (line)\n", trunk ? 'T' : 'S');
2520 info = ast_strdupa(data);
2522 AST_STANDARD_APP_ARGS(args, info);
2524 if (ast_strlen_zero(args.confno)) {
2525 ast_log(LOG_WARNING, "SLA%c requires an SLA line number\n", trunk ? 'T' : 'S');
2533 ast_app_parse_options(sla_opts, &confflags, NULL, args.options);
2535 ast_set_flag(&confflags, CONFFLAG_QUIET|CONFFLAG_DYNAMIC);
2537 ast_set_flag(&confflags, CONFFLAG_WAITMARKED|CONFFLAG_MARKEDEXIT|CONFFLAG_SLA_TRUNK);
2539 ast_set_flag(&confflags, CONFFLAG_MARKEDUSER|CONFFLAG_SLA_STATION);
2541 sla = ASTOBJ_CONTAINER_FIND(&slas, args.confno);
2543 snprintf(confno, sizeof(confno), "sla-%s", args.confno);
2544 cnf = find_conf(chan, confno, 1, dynamic, "", 1, &confflags);
2546 sla_checkforhold(cnf, 1);
2549 ast_indicate(chan, AST_CONTROL_RINGING);
2550 invite_stations(chan, sla);
2552 invite_trunk(chan, sla);
2553 } else if (chan->_state != AST_STATE_UP)
2556 /* Run the conference */
2557 res = conf_run(chan, cnf, confflags.flags);
2559 ast_log(LOG_WARNING, "SLA%c: Found SLA '%s' but unable to build conference!\n", trunk ? 'T' : 'S', args.confno);
2560 ASTOBJ_UNREF(sla, sla_destroy);
2562 ast_log(LOG_WARNING, "SLA%c: SLA '%s' not found!\n", trunk ? 'T' : 'S', args.confno);
2565 LOCAL_USER_REMOVE(u);
2570 /*! \brief The slas() wrapper */
2571 static int slas_exec(struct ast_channel *chan, void *data)
2573 return sla_exec(chan, data, 0);
2576 /*! \brief The slat() wrapper */
2577 static int slat_exec(struct ast_channel *chan, void *data)
2579 return sla_exec(chan, data, 1);
2582 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2584 struct ast_conf_user *user = NULL;
2587 sscanf(callerident, "%i", &cid);
2588 if (conf && callerident) {
2589 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2590 if (cid == user->user_no)
2597 /*! \brief The MeetMeadmin application */
2598 /* MeetMeAdmin(confno, command, caller) */
2599 static int admin_exec(struct ast_channel *chan, void *data) {
2601 struct ast_conference *cnf;
2602 struct ast_conf_user *user = NULL;
2603 struct localuser *u;
2604 AST_DECLARE_APP_ARGS(args,
2605 AST_APP_ARG(confno);
2606 AST_APP_ARG(command);
2610 if (ast_strlen_zero(data)) {
2611 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2617 AST_LIST_LOCK(&confs);
2619 params = ast_strdupa(data);
2620 AST_STANDARD_APP_ARGS(args, params);
2622 if (!args.command) {
2623 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2624 AST_LIST_UNLOCK(&confs);
2625 LOCAL_USER_REMOVE(u);
2628 AST_LIST_TRAVERSE(&confs, cnf, list) {
2629 if (!strcmp(cnf->confno, args.confno))
2634 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2635 LOCAL_USER_REMOVE(u);
2636 AST_LIST_UNLOCK(&confs);
2641 user = find_user(cnf, args.user);
2643 switch (*args.command) {
2644 case 76: /* L: Lock */
2647 case 108: /* l: Unlock */
2650 case 75: /* K: kick all users */
2651 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2652 user->adminflags |= ADMINFLAG_KICKME;
2654 case 101: /* e: Eject last user*/
2655 user = AST_LIST_LAST(&cnf->userlist);
2656 if (!(user->userflags & CONFFLAG_ADMIN))
2657 user->adminflags |= ADMINFLAG_KICKME;
2659 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2661 case 77: /* M: Mute */
2663 user->adminflags |= ADMINFLAG_MUTED;
2665 ast_log(LOG_NOTICE, "Specified User not found!\n");
2667 case 78: /* N: Mute all (non-admin) users */
2668 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2669 if (!(user->userflags & CONFFLAG_ADMIN))