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"
67 #define CONFIG_FILE_NAME "meetme.conf"
69 /*! each buffer is 20ms, so this is 640ms total */
70 #define DEFAULT_AUDIO_BUFFERS 32
73 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
74 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
75 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
78 #define MEETME_DELAYDETECTTALK 300
79 #define MEETME_DELAYDETECTENDTALK 1000
81 #define AST_FRAME_BITS 32
93 enum recording_state {
95 MEETME_RECORD_STARTED,
97 MEETME_RECORD_TERMINATE
100 #define CONF_SIZE 320
103 /*! user has admin access on the conference */
104 CONFFLAG_ADMIN = (1 << 0),
105 /*! If set the user can only receive audio from the conference */
106 CONFFLAG_MONITOR = (1 << 1),
107 /*! If set asterisk will exit conference when '#' is pressed */
108 CONFFLAG_POUNDEXIT = (1 << 2),
109 /*! If set asterisk will provide a menu to the user when '*' is pressed */
110 CONFFLAG_STARMENU = (1 << 3),
111 /*! If set the use can only send audio to the conference */
112 CONFFLAG_TALKER = (1 << 4),
113 /*! If set there will be no enter or leave sounds */
114 CONFFLAG_QUIET = (1 << 5),
115 /*! If set, when user joins the conference, they will be told the number
116 * of users that are already in */
117 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
118 /*! Set to run AGI Script in Background */
119 CONFFLAG_AGI = (1 << 7),
120 /*! Set to have music on hold when user is alone in conference */
121 CONFFLAG_MOH = (1 << 8),
122 /*! If set the MeetMe will return if all marked with this flag left */
123 CONFFLAG_MARKEDEXIT = (1 << 9),
124 /*! If set, the MeetMe will wait until a marked user enters */
125 CONFFLAG_WAITMARKED = (1 << 10),
126 /*! If set, the MeetMe will exit to the specified context */
127 CONFFLAG_EXIT_CONTEXT = (1 << 11),
128 /*! If set, the user will be marked */
129 CONFFLAG_MARKEDUSER = (1 << 12),
130 /*! If set, user will be ask record name on entry of conference */
131 CONFFLAG_INTROUSER = (1 << 13),
132 /*! If set, the MeetMe will be recorded */
133 CONFFLAG_RECORDCONF = (1<< 14),
134 /*! If set, the user will be monitored if the user is talking or not */
135 CONFFLAG_MONITORTALKER = (1 << 15),
136 CONFFLAG_DYNAMIC = (1 << 16),
137 CONFFLAG_DYNAMICPIN = (1 << 17),
138 CONFFLAG_EMPTY = (1 << 18),
139 CONFFLAG_EMPTYNOPIN = (1 << 19),
140 CONFFLAG_ALWAYSPROMPT = (1 << 20),
141 /*! If set, treats talking users as muted users */
142 CONFFLAG_OPTIMIZETALKER = (1 << 21),
143 /*! If set, won't speak the extra prompt when the first person
144 * enters the conference */
145 CONFFLAG_NOONLYPERSON = (1 << 22),
146 /*! If set, user will be asked to record name on entry of conference
148 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
149 /*! If set, the user will be initially self-muted */
150 CONFFLAG_STARTMUTED = (1 << 24),
151 /*! If set, the user is a shared line appearance station */
152 CONFFLAG_SLA_STATION = (1 << 25),
153 /*! If set, the user is a shared line appearance trunk */
154 CONFFLAG_SLA_TRUNK = (1 << 26),
155 /*! If set, the user has put us on hold */
156 CONFFLAG_HOLD = (1 << 27),
157 /*! If set, the user should continue in the dialplan if kicked out */
158 CONFFLAG_KICK_CONTINUE = (1 << 28)
162 OPT_ARG_WAITMARKED = 0,
163 OPT_ARG_ARRAY_SIZE = 1,
164 } meetme_option_args;
166 AST_APP_OPTIONS(meetme_opts, {
167 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
168 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
169 AST_APP_OPTION('b', CONFFLAG_AGI ),
170 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
171 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
172 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
173 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
174 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
175 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
176 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
177 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
178 AST_APP_OPTION('M', CONFFLAG_MOH ),
179 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
180 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
181 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
182 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
183 AST_APP_OPTION('q', CONFFLAG_QUIET ),
184 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
185 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
186 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
187 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
188 AST_APP_OPTION('t', CONFFLAG_TALKER ),
189 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
190 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
191 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
192 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
195 AST_APP_OPTIONS(sla_opts, {
196 /* Just a placeholder for now */
198 static const char *app = "MeetMe";
199 static const char *app2 = "MeetMeCount";
200 static const char *app3 = "MeetMeAdmin";
201 static const char *appslas = "SLAStation";
202 static const char *appslat = "SLATrunk";
204 static const char *synopsis = "MeetMe conference bridge";
205 static const char *synopsis2 = "MeetMe participant count";
206 static const char *synopsis3 = "MeetMe conference Administration";
207 static const char *synopslas = "Shared Line Appearance - Station";
208 static const char *synopslat = "Shared Line Appearance - Trunk";
210 static const char *descrip =
211 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
212 "conference. If the conference number is omitted, the user will be prompted\n"
213 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
214 "is specified, by pressing '#'.\n"
215 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
216 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
217 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
218 "The option string may contain zero or more of the following characters:\n"
219 " 'a' -- set admin mode\n"
220 " 'A' -- set marked mode\n"
221 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
222 " Default: conf-background.agi (Note: This does not work with\n"
223 " non-Zap channels in the same conference)\n"
224 " 'c' -- announce user(s) count on joining a conference\n"
225 " 'C' -- continue in dialplan when kicked out of conference\n"
226 " 'd' -- dynamically add conference\n"
227 " 'D' -- dynamically add conference, prompting for a PIN\n"
228 " 'e' -- select an empty conference\n"
229 " 'E' -- select an empty pinless conference\n"
230 " 'i' -- announce user join/leave with review\n"
231 " 'I' -- announce user join/leave without review\n"
232 " 'l' -- set listen only mode (Listen only, no talking)\n"
233 " 'm' -- set initially muted\n"
234 " 'M' -- enable music on hold when the conference has a single caller\n"
235 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
236 " being muted, meaning (a) No encode is done on transmission and\n"
237 " (b) Received audio that is not registered as talking is omitted\n"
238 " causing no buildup in background noise\n"
239 " 'p' -- allow user to exit the conference by pressing '#'\n"
240 " 'P' -- always prompt for the pin even if it is specified\n"
241 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
242 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
243 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
244 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
246 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
247 " 't' -- set talk only mode. (Talk only, no listening)\n"
248 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
250 " -- wait until the marked user enters the conference\n"
251 " 'x' -- close the conference when last marked user exits\n"
252 " 'X' -- allow user to exit the conference by entering a valid single\n"
253 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
254 " if that variable is not defined.\n"
255 " '1' -- do not play message when first person enters\n";
257 static const char *descrip2 =
258 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
259 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
260 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
261 "the channel, unless priority n+1 exists, in which case priority progress will\n"
263 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
265 static const char *descrip3 =
266 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
267 " 'e' -- Eject last user that joined\n"
268 " 'k' -- Kick one user out of conference\n"
269 " 'K' -- Kick all users out of conference\n"
270 " 'l' -- Unlock conference\n"
271 " 'L' -- Lock conference\n"
272 " 'm' -- Unmute one user\n"
273 " 'M' -- Mute one user\n"
274 " 'n' -- Unmute all users in the conference\n"
275 " 'N' -- Mute all non-admin users in the conference\n"
276 " 'r' -- Reset one user's volume settings\n"
277 " 'R' -- Reset all users volume settings\n"
278 " 's' -- Lower entire conference speaking volume\n"
279 " 'S' -- Raise entire conference speaking volume\n"
280 " 't' -- Lower one user's talk volume\n"
281 " 'T' -- Raise one user's talk volume\n"
282 " 'u' -- Lower one user's listen volume\n"
283 " 'U' -- Raise one user's listen volume\n"
284 " 'v' -- Lower entire conference listening volume\n"
285 " 'V' -- Raise entire conference listening volume\n"
288 static const char *descripslas =
289 " SLAStation(sla[,options]): Run Shared Line Appearance for station\n"
290 "Runs the share line appearance for a station calling in. If there are no\n"
291 "other participants in the conference, the trunk is called and is dumped into\n"
294 static const char *descripslat =
295 " SLATrunk(sla[,options]): Run Shared Line Appearance for trunk\n"
296 "Runs the share line appearance for a trunk calling in. If there are no\n"
297 "other participants in the conference, all member stations are invited into\n"
300 #define CONFIG_FILE_NAME "meetme.conf"
301 #define CONFIG_FILE_NAME_SLA "sla.conf"
303 /*! \brief The MeetMe Conference object */
304 struct ast_conference {
305 ast_mutex_t playlock; /*!< Conference specific lock (players) */
306 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
307 char confno[AST_MAX_EXTENSION]; /*!< Conference */
308 struct ast_channel *chan; /*!< Announcements channel */
309 struct ast_channel *lchan; /*!< Listen/Record channel */
310 int fd; /*!< Announcements fd */
311 int zapconf; /*!< Zaptel Conf # */
312 int users; /*!< Number of active users */
313 int markedusers; /*!< Number of marked users */
314 time_t start; /*!< Start time (s) */
315 int refcount; /*!< reference count of usage */
316 enum recording_state recording:2; /*!< recording status */
317 unsigned int isdynamic:1; /*!< Created on the fly? */
318 unsigned int locked:1; /*!< Is the conference locked? */
319 pthread_t recordthread; /*!< thread for recording */
320 pthread_attr_t attr; /*!< thread attribute */
321 const char *recordingfilename; /*!< Filename to record the Conference into */
322 const char *recordingformat; /*!< Format to record the Conference in */
323 char pin[AST_MAX_EXTENSION]; /*!< If protected by a PIN */
324 char pinadmin[AST_MAX_EXTENSION]; /*!< If protected by a admin PIN */
325 struct ast_frame *transframe[32];
326 struct ast_frame *origframe;
327 struct ast_trans_pvt *transpath[32];
328 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
329 AST_LIST_ENTRY(ast_conference) list;
332 static AST_LIST_HEAD_STATIC(confs, ast_conference);
335 int desired; /*!< Desired volume adjustment */
336 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
339 struct ast_conf_user {
340 int user_no; /*!< User Number */
341 int userflags; /*!< Flags as set in the conference */
342 int adminflags; /*!< Flags set by the Admin */
343 struct ast_channel *chan; /*!< Connected channel */
344 int talking; /*!< Is user talking */
345 int zapchannel; /*!< Is a Zaptel channel */
346 char usrvalue[50]; /*!< Custom User Value */
347 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
348 int control; /*! Queue Control for transmission */
349 int dtmf; /*! Queue DTMF for transmission */
350 time_t jointime; /*!< Time the user joined the conference */
352 struct volume listen;
353 AST_LIST_ENTRY(ast_conf_user) list;
356 /*! SLA station - one device in an SLA configuration */
357 struct ast_sla_station {
358 ASTOBJ_COMPONENTS(struct ast_sla_station);
363 struct ast_sla_station_box {
364 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla_station);
367 /*! SLA - Shared Line Appearance object. These consist of one trunk (outbound line)
368 and stations that receive incoming calls and place outbound calls over the trunk
371 ASTOBJ_COMPONENTS (struct ast_sla);
372 struct ast_sla_station_box stations; /*!< Stations connected to this SLA */
373 char confname[80]; /*!< Name for this SLA bridge */
374 char trunkdest[256]; /*!< Device (channel) identifier for the trunk line */
375 char trunktech[20]; /*!< Technology used for the trunk (channel driver) */
379 ASTOBJ_CONTAINER_COMPONENTS(struct ast_sla);
382 static int audio_buffers; /*!< The number of audio buffers to be allocated on pseudo channels
385 /*! The number of audio buffers to be allocated on pseudo channels
386 * when in a conference */
387 static int audio_buffers;
389 /*! Map 'volume' levels from -5 through +5 into
390 * decibel (dB) settings for channel drivers
391 * Note: these are not a straight linear-to-dB
392 * conversion... the numbers have been modified
393 * to give the user a better level of adjustability
395 static signed char gain_map[] = {
410 static int admin_exec(struct ast_channel *chan, void *data);
411 static void *recordthread(void *args);
413 static char *istalking(int x)
418 return "(unmonitored)";
420 return "(not talking)";
423 static int careful_write(int fd, unsigned char *data, int len, int block)
430 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
431 res = ioctl(fd, ZT_IOMUX, &x);
435 res = write(fd, data, len);
437 if (errno != EAGAIN) {
438 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
450 static int set_talk_volume(struct ast_conf_user *user, int volume)
452 signed char gain_adjust;
454 /* attempt to make the adjustment in the channel driver;
455 if successful, don't adjust in the frame reading routine
457 gain_adjust = gain_map[volume + 5];
459 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
462 static int set_listen_volume(struct ast_conf_user *user, int volume)
464 signed char gain_adjust;
466 /* attempt to make the adjustment in the channel driver;
467 if successful, don't adjust in the frame reading routine
469 gain_adjust = gain_map[volume + 5];
471 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
474 static void tweak_volume(struct volume *vol, enum volume_action action)
478 switch (vol->desired) {
493 switch (vol->desired) {
509 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
511 tweak_volume(&user->talk, action);
512 /* attempt to make the adjustment in the channel driver;
513 if successful, don't adjust in the frame reading routine
515 if (!set_talk_volume(user, user->talk.desired))
516 user->talk.actual = 0;
518 user->talk.actual = user->talk.desired;
521 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
523 tweak_volume(&user->listen, action);
524 /* attempt to make the adjustment in the channel driver;
525 if successful, don't adjust in the frame reading routine
527 if (!set_listen_volume(user, user->listen.desired))
528 user->listen.actual = 0;
530 user->listen.actual = user->listen.desired;
533 static void reset_volumes(struct ast_conf_user *user)
535 signed char zero_volume = 0;
537 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
538 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
541 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
547 if (!chan->_softhangup)
548 res = ast_autoservice_start(chan);
550 AST_LIST_LOCK(&confs);
566 careful_write(conf->fd, data, len, 1);
569 AST_LIST_UNLOCK(&confs);
572 ast_autoservice_stop(chan);
575 static void station_destroy(struct ast_sla_station *station)
580 static void sla_destroy(struct ast_sla *sla)
582 ASTOBJ_CONTAINER_DESTROYALL(&sla->stations, station_destroy);
583 ASTOBJ_CONTAINER_DESTROY(&sla->stations);
587 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
589 struct ast_conference *cnf;
590 struct zt_confinfo ztc;
592 AST_LIST_LOCK(&confs);
594 AST_LIST_TRAVERSE(&confs, cnf, list) {
595 if (!strcmp(confno, cnf->confno))
599 if (!cnf && (make || dynamic)) {
601 if ((cnf = ast_calloc(1, sizeof(*cnf)))) {
602 ast_mutex_init(&cnf->playlock);
603 ast_mutex_init(&cnf->listenlock);
604 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
605 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
606 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
608 cnf->markedusers = 0;
609 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
611 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
612 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
613 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
615 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
616 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
618 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
624 memset(&ztc, 0, sizeof(ztc));
625 /* Setup a new zap conference */
628 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
629 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
630 ast_log(LOG_WARNING, "Error setting conference\n");
632 ast_hangup(cnf->chan);
639 cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
641 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
642 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
644 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
645 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
646 ast_log(LOG_WARNING, "Error setting conference\n");
647 ast_hangup(cnf->lchan);
651 /* Fill the conference struct */
652 cnf->start = time(NULL);
653 cnf->zapconf = ztc.confno;
654 cnf->isdynamic = dynamic ? 1 : 0;
655 if (option_verbose > 2)
656 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
657 AST_LIST_INSERT_HEAD(&confs, cnf, list);
658 manager_event(EVENT_FLAG_CALL, "MeetmeStart", "Meetme: %s\r\n", cnf->confno);
663 cnf->refcount += refcount;
665 AST_LIST_UNLOCK(&confs);
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 int meetme_cmd(int fd, int argc, char **argv)
696 /* Process the command */
697 struct ast_conference *cnf;
698 struct ast_conf_user *user;
700 int i = 0, total = 0;
702 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
703 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
704 char cmdline[1024] = "";
707 ast_cli(fd, "Invalid Arguments.\n");
708 /* Check for length so no buffer will overflow... */
709 for (i = 0; i < argc; i++) {
710 if (strlen(argv[i]) > 100)
711 ast_cli(fd, "Invalid Arguments.\n");
714 /* 'MeetMe': List all the conferences */
716 if (AST_LIST_EMPTY(&confs)) {
717 ast_cli(fd, "No active MeetMe conferences.\n");
718 return RESULT_SUCCESS;
720 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
721 AST_LIST_TRAVERSE(&confs, cnf, list) {
722 if (cnf->markedusers == 0)
723 strcpy(cmdline, "N/A ");
725 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
726 hr = (now - cnf->start) / 3600;
727 min = ((now - cnf->start) % 3600) / 60;
728 sec = (now - cnf->start) % 60;
730 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
734 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
735 return RESULT_SUCCESS;
738 return RESULT_SHOWUSAGE;
739 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
740 if (strstr(argv[1], "lock")) {
741 if (strcmp(argv[1], "lock") == 0) {
743 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
746 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
748 } else if (strstr(argv[1], "mute")) {
750 return RESULT_SHOWUSAGE;
751 if (strcmp(argv[1], "mute") == 0) {
753 if (strcmp(argv[3], "all") == 0) {
754 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
756 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
757 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
761 if (strcmp(argv[3], "all") == 0) {
762 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
764 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
765 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
768 } else if (strcmp(argv[1], "kick") == 0) {
770 return RESULT_SHOWUSAGE;
771 if (strcmp(argv[3], "all") == 0) {
773 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
775 /* Kick a single user */
776 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
777 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
779 } else if(strcmp(argv[1], "list") == 0) {
780 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
781 /* List all the users in a conference */
782 if (AST_LIST_EMPTY(&confs)) {
784 ast_cli(fd, "No active conferences.\n");
785 return RESULT_SUCCESS;
787 /* Find the right conference */
788 AST_LIST_TRAVERSE(&confs, cnf, list) {
789 if (strcmp(cnf->confno, argv[2]) == 0)
794 ast_cli(fd, "No such conference: %s.\n",argv[2]);
795 return RESULT_SUCCESS;
797 /* Show all the users */
798 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
800 hr = (now - user->jointime) / 3600;
801 min = ((now - user->jointime) % 3600) / 60;
802 sec = (now - user->jointime) % 60;
804 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
806 S_OR(user->chan->cid.cid_num, "<unknown>"),
807 S_OR(user->chan->cid.cid_name, "<no name>"),
809 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
810 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
811 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
812 istalking(user->talking),
813 user->userflags & CONFFLAG_HOLD ? " (On Hold) " : "", hr, min, sec);
815 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
817 S_OR(user->chan->cid.cid_num, ""),
818 S_OR(user->chan->cid.cid_name, ""),
820 user->userflags & CONFFLAG_ADMIN ? "1" : "",
821 user->userflags & CONFFLAG_MONITOR ? "1" : "",
822 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
823 user->talking, hr, min, sec);
827 ast_cli(fd,"%d users in that conference.\n",cnf->users);
829 return RESULT_SUCCESS;
831 return RESULT_SHOWUSAGE;
834 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
836 admin_exec(NULL, cmdline);
841 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
843 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
845 int len = strlen(word);
847 struct ast_conference *cnf = NULL;
848 struct ast_conf_user *usr = NULL;
851 char *myline, *ret = NULL;
853 if (pos == 1) { /* Command */
854 return ast_cli_complete(word, cmds, state);
855 } else if (pos == 2) { /* Conference Number */
856 AST_LIST_LOCK(&confs);
857 AST_LIST_TRAVERSE(&confs, cnf, list) {
858 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
863 ret = ast_strdup(ret); /* dup before releasing the lock */
864 AST_LIST_UNLOCK(&confs);
866 } else if (pos == 3) {
867 /* User Number || Conf Command option*/
868 if (strstr(line, "mute") || strstr(line, "kick")) {
869 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
870 return strdup("all");
872 AST_LIST_LOCK(&confs);
874 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
875 myline = ast_strdupa(line);
876 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
877 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
881 AST_LIST_TRAVERSE(&confs, cnf, list) {
882 if (!strcmp(confno, cnf->confno))
887 /* Search for the user */
888 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
889 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
890 if (!strncasecmp(word, usrno, len) && ++which > state)
894 AST_LIST_UNLOCK(&confs);
895 return usr ? strdup(usrno) : NULL;
896 } else if ( strstr(line, "list") && ( 0 == state ) )
897 return strdup("concise");
903 static const char meetme_usage[] =
904 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
905 " Executes a command for the conference or on a conferee\n";
907 static const char sla_show_usage[] =
909 " Lists status of all shared line appearances\n";
911 static struct ast_cli_entry cli_meetme[] = {
912 { { "sla", "show", NULL },
913 sla_show, "Show status of Shared Line Appearances",
914 sla_show_usage, NULL },
916 { { "meetme", NULL, NULL },
917 meetme_cmd, "Execute a command on a conference or conferee",
918 meetme_usage, complete_meetmecmd },
921 static void conf_flush(int fd, struct ast_channel *chan)
925 /* read any frames that may be waiting on the channel
931 /* when no frames are available, this will wait
932 for 1 millisecond maximum
934 while (ast_waitfor(chan, 1)) {
938 else /* channel was hung up or something else happened */
943 /* flush any data sitting in the pseudo channel */
945 if (ioctl(fd, ZT_FLUSH, &x))
946 ast_log(LOG_WARNING, "Error flushing channel\n");
950 /* Remove the conference from the list and free it.
951 We assume that this was called while holding conflock. */
952 static int conf_free(struct ast_conference *conf)
956 AST_LIST_REMOVE(&confs, conf, list);
957 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
959 if (conf->recording == MEETME_RECORD_ACTIVE) {
960 conf->recording = MEETME_RECORD_TERMINATE;
961 AST_LIST_UNLOCK(&confs);
963 AST_LIST_LOCK(&confs);
964 if (conf->recording == MEETME_RECORD_OFF)
966 AST_LIST_UNLOCK(&confs);
970 for (x=0;x<AST_FRAME_BITS;x++) {
971 if (conf->transframe[x])
972 ast_frfree(conf->transframe[x]);
973 if (conf->transpath[x])
974 ast_translator_free_path(conf->transpath[x]);
977 ast_frfree(conf->origframe);
979 ast_hangup(conf->lchan);
981 ast_hangup(conf->chan);
990 static void conf_queue_dtmf(struct ast_conference *conf, int digit)
992 struct ast_conf_user *user;
993 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
998 static void conf_queue_control(struct ast_conference *conf, int control)
1000 struct ast_conf_user *user;
1001 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1002 user->control = control;
1007 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1009 struct ast_conf_user *user = NULL;
1010 struct ast_conf_user *usr = NULL;
1012 struct zt_confinfo ztc, ztc_empty;
1013 struct ast_frame *f;
1014 struct ast_channel *c;
1015 struct ast_frame fr;
1023 int musiconhold = 0;
1026 int currentmarked = 0;
1029 int menu_active = 0;
1030 int using_pseudo = 0;
1035 struct ast_dsp *dsp=NULL;
1036 struct ast_app *app;
1037 const char *agifile;
1038 const char *agifiledefault = "conf-background.agi";
1039 char meetmesecs[30] = "";
1040 char exitcontext[AST_MAX_CONTEXT] = "";
1041 char recordingtmp[AST_MAX_EXTENSION] = "";
1042 char members[10] = "";
1043 int dtmf, opt_waitmarked_timeout = 0;
1046 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1047 char *buf = __buf + AST_FRIENDLY_OFFSET;
1049 if (!(user = ast_calloc(1, sizeof(*user)))) {
1050 AST_LIST_LOCK(&confs);
1052 if (!conf->refcount){
1055 AST_LIST_UNLOCK(&confs);
1059 /* Possible timeout waiting for marked user */
1060 if ((confflags & CONFFLAG_WAITMARKED) &&
1061 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1062 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1063 (opt_waitmarked_timeout > 0)) {
1064 timeout = time(NULL) + opt_waitmarked_timeout;
1067 if (confflags & CONFFLAG_RECORDCONF) {
1068 if (!conf->recordingfilename) {
1069 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1070 if (!conf->recordingfilename) {
1071 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1072 conf->recordingfilename = ast_strdupa(recordingtmp);
1074 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1075 if (!conf->recordingformat) {
1076 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1077 conf->recordingformat = ast_strdupa(recordingtmp);
1079 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1080 conf->confno, conf->recordingfilename, conf->recordingformat);
1084 if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
1085 pthread_attr_init(&conf->attr);
1086 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1087 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1090 time(&user->jointime);
1092 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1093 /* Sorry, but this conference is locked! */
1094 if (!ast_streamfile(chan, "conf-locked", chan->language))
1095 ast_waitstream(chan, "");
1099 if (confflags & CONFFLAG_MARKEDUSER)
1100 conf->markedusers++;
1102 ast_mutex_lock(&conf->playlock);
1104 if (AST_LIST_EMPTY(&conf->userlist))
1107 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1109 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1112 user->userflags = confflags;
1113 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1117 snprintf(members, sizeof(members), "%d", conf->users);
1118 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1120 /* This device changed state now - if this is the first user */
1121 if (conf->users == 1)
1122 ast_device_state_changed("meetme:%s", conf->confno);
1123 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1124 ast_device_state_changed("SLA:%s", conf->confno + 4);
1126 ast_mutex_unlock(&conf->playlock);
1128 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1129 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1130 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1131 else if (!ast_strlen_zero(chan->macrocontext))
1132 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1134 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1137 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1138 snprintf(user->namerecloc, sizeof(user->namerecloc),
1139 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1140 conf->confno, user->user_no);
1141 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1142 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1144 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1149 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1150 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1151 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1152 ast_waitstream(chan, "");
1153 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1154 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1155 ast_waitstream(chan, "");
1158 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1159 int keepplaying = 1;
1161 if (conf->users == 2) {
1162 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1163 res = ast_waitstream(chan, AST_DIGIT_ANY);
1164 ast_stopstream(chan);
1171 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1172 res = ast_waitstream(chan, AST_DIGIT_ANY);
1173 ast_stopstream(chan);
1180 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1186 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1187 res = ast_waitstream(chan, AST_DIGIT_ANY);
1188 ast_stopstream(chan);
1197 ast_indicate(chan, -1);
1199 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1200 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1204 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1205 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1209 retryzap = strcasecmp(chan->tech->type, "Zap");
1210 user->zapchannel = !retryzap;
1213 origfd = chan->fds[0];
1215 fd = open("/dev/zap/pseudo", O_RDWR);
1217 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1221 /* Make non-blocking */
1222 flags = fcntl(fd, F_GETFL);
1224 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1228 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1229 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1233 /* Setup buffering information */
1234 memset(&bi, 0, sizeof(bi));
1235 bi.bufsize = CONF_SIZE/2;
1236 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1237 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1238 bi.numbufs = audio_buffers;
1239 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1240 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1245 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1246 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1252 /* XXX Make sure we're not running on a pseudo channel XXX */
1256 memset(&ztc, 0, sizeof(ztc));
1257 memset(&ztc_empty, 0, sizeof(ztc_empty));
1258 /* Check to see if we're in a conference... */
1260 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1261 ast_log(LOG_WARNING, "Error getting conference\n");
1266 /* Whoa, already in a conference... Retry... */
1269 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1274 memset(&ztc, 0, sizeof(ztc));
1275 /* Add us to the conference */
1277 ztc.confno = conf->zapconf;
1279 ast_mutex_lock(&conf->playlock);
1281 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1282 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1283 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1284 ast_waitstream(conf->chan, "");
1285 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1286 ast_waitstream(conf->chan, "");
1290 if (confflags & CONFFLAG_MONITOR)
1291 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1292 else if (confflags & CONFFLAG_TALKER)
1293 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1295 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1297 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1298 ast_log(LOG_WARNING, "Error setting conference\n");
1300 ast_mutex_unlock(&conf->playlock);
1304 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1307 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1312 chan->name, chan->uniqueid, conf->confno, user->user_no);
1316 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1318 if (!(confflags & CONFFLAG_QUIET))
1319 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1320 conf_play(chan, conf, ENTER);
1323 ast_mutex_unlock(&conf->playlock);
1325 conf_flush(fd, chan);
1327 if (confflags & CONFFLAG_AGI) {
1328 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1329 or use default filename of conf-background.agi */
1331 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1333 agifile = agifiledefault;
1335 if (user->zapchannel) {
1336 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1338 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1340 /* Find a pointer to the agi app and execute the script */
1341 app = pbx_findapp("agi");
1343 char *s = ast_strdupa(agifile);
1344 ret = pbx_exec(chan, app, s);
1346 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1349 if (user->zapchannel) {
1350 /* Remove CONFMUTE mode on Zap channel */
1352 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1355 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1356 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1358 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1360 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1361 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1365 int menu_was_active = 0;
1370 if (timeout && time(NULL) >= timeout)
1373 /* if we have just exited from the menu, and the user had a channel-driver
1374 volume adjustment, restore it
1376 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1377 set_talk_volume(user, user->listen.desired);
1379 menu_was_active = menu_active;
1381 currentmarked = conf->markedusers;
1382 if (!(confflags & CONFFLAG_QUIET) &&
1383 (confflags & CONFFLAG_MARKEDUSER) &&
1384 (confflags & CONFFLAG_WAITMARKED) &&
1386 if (currentmarked == 1 && conf->users > 1) {
1387 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1388 if (conf->users - 1 == 1) {
1389 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1390 ast_waitstream(chan, "");
1392 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1393 ast_waitstream(chan, "");
1396 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1397 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1398 ast_waitstream(chan, "");
1401 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1404 /* Update the struct with the actual confflags */
1405 user->userflags = confflags;
1407 if (confflags & CONFFLAG_WAITMARKED) {
1408 if(currentmarked == 0) {
1409 if (lastmarked != 0) {
1410 if (!(confflags & CONFFLAG_QUIET))
1411 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1412 ast_waitstream(chan, "");
1413 if (confflags & CONFFLAG_MARKEDEXIT) {
1414 if (confflags & CONFFLAG_KICK_CONTINUE)
1418 ztc.confmode = ZT_CONF_CONF;
1419 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1420 ast_log(LOG_WARNING, "Error setting conference\n");
1426 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1427 ast_moh_start(chan, NULL, NULL);
1430 ztc.confmode = ZT_CONF_CONF;
1431 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1432 ast_log(LOG_WARNING, "Error setting conference\n");
1437 } else if(currentmarked >= 1 && lastmarked == 0) {
1438 /* Marked user entered, so cancel timeout */
1440 if (confflags & CONFFLAG_MONITOR)
1441 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1442 else if (confflags & CONFFLAG_TALKER)
1443 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1445 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1446 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1447 ast_log(LOG_WARNING, "Error setting conference\n");
1451 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1455 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1456 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1457 ast_waitstream(chan, "");
1458 conf_play(chan, conf, ENTER);
1463 /* trying to add moh for single person conf */
1464 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1465 if (conf->users == 1) {
1466 if (musiconhold == 0) {
1467 ast_moh_start(chan, NULL, NULL);
1478 /* Leave if the last marked user left */
1479 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1480 if (confflags & CONFFLAG_KICK_CONTINUE)
1487 /* Check if my modes have changed */
1489 /* If I should be muted but am still talker, mute me */
1490 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1491 ztc.confmode ^= ZT_CONF_TALKER;
1492 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1493 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1498 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1504 chan->name, chan->uniqueid, conf->confno, user->user_no);
1507 /* If I should be un-muted but am not talker, un-mute me */
1508 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1509 ztc.confmode |= ZT_CONF_TALKER;
1510 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1511 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1516 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1522 chan->name, chan->uniqueid, conf->confno, user->user_no);
1525 /* If I have been kicked, exit the conference */
1526 if (user->adminflags & ADMINFLAG_KICKME) {
1527 //You have been kicked.
1528 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1529 ast_waitstream(chan, "");
1535 if (c->fds[0] != origfd) {
1537 /* Kill old pseudo */
1542 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1543 retryzap = strcasecmp(c->tech->type, "Zap");
1544 user->zapchannel = !retryzap;
1547 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1548 f = ast_read_noaudio(c);
1553 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1554 if (user->talk.actual)
1555 ast_frame_adjust_volume(f, user->talk.actual);
1557 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1560 if (user->talking == -1)
1563 res = ast_dsp_silence(dsp, f, &totalsilence);
1564 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1566 if (confflags & CONFFLAG_MONITORTALKER)
1567 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1573 chan->name, chan->uniqueid, conf->confno, user->user_no);
1575 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1577 if (confflags & CONFFLAG_MONITORTALKER)
1578 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1584 chan->name, chan->uniqueid, conf->confno, user->user_no);
1588 /* Absolutely do _not_ use careful_write here...
1589 it is important that we read data from the channel
1590 as fast as it arrives, and feed it into the conference.
1591 The buffering in the pseudo channel will take care of any
1592 timing differences, unless they are so drastic as to lose
1593 audio frames (in which case carefully writing would only
1594 have delayed the audio even further).
1596 /* As it turns out, we do want to use careful write. We just
1597 don't want to block, but we do want to at least *try*
1598 to write out all the samples.
1600 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1601 careful_write(fd, f->data, f->datalen, 0);
1603 } else if ((f->frametype == AST_FRAME_DTMF) &&
1604 (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) {
1605 conf_queue_dtmf(conf, f->subclass);
1606 } else if ((f->frametype == AST_FRAME_CONTROL) &&
1607 (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))) {
1608 conf_queue_control(conf, f->subclass);
1609 if (f->subclass == AST_CONTROL_HOLD)
1610 confflags |= CONFFLAG_HOLD;
1611 else if (f->subclass == AST_CONTROL_UNHOLD)
1612 confflags &= ~CONFFLAG_HOLD;
1613 user->userflags = confflags;
1614 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1617 tmp[0] = f->subclass;
1619 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1621 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1625 } else if (option_debug > 1)
1626 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1627 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1631 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1632 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1633 ast_log(LOG_WARNING, "Error setting conference\n");
1639 /* if we are entering the menu, and the user has a channel-driver
1640 volume adjustment, clear it
1642 if (!menu_active && user->talk.desired && !user->talk.actual)
1643 set_talk_volume(user, 0);
1648 if ((confflags & CONFFLAG_ADMIN)) {
1652 /* Record this sound! */
1653 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1654 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1655 ast_stopstream(chan);
1662 case '1': /* Un/Mute */
1665 /* for admin, change both admin and use flags */
1666 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1667 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1669 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1671 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1672 if (!ast_streamfile(chan, "conf-muted", chan->language))
1673 ast_waitstream(chan, "");
1675 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1676 ast_waitstream(chan, "");
1679 case '2': /* Un/Lock the Conference */
1683 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1684 ast_waitstream(chan, "");
1687 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1688 ast_waitstream(chan, "");
1691 case '3': /* Eject last user */
1693 usr = AST_LIST_LAST(&conf->userlist);
1694 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1695 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1696 ast_waitstream(chan, "");
1698 usr->adminflags |= ADMINFLAG_KICKME;
1699 ast_stopstream(chan);
1702 tweak_listen_volume(user, VOL_DOWN);
1705 tweak_listen_volume(user, VOL_UP);
1708 tweak_talk_volume(user, VOL_DOWN);
1714 tweak_talk_volume(user, VOL_UP);
1718 /* Play an error message! */
1719 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1720 ast_waitstream(chan, "");
1728 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1729 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1730 ast_stopstream(chan);
1737 case '1': /* Un/Mute */
1740 /* user can only toggle the self-muted state */
1741 user->adminflags ^= ADMINFLAG_SELFMUTED;
1743 /* they can't override the admin mute state */
1744 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1745 if (!ast_streamfile(chan, "conf-muted", chan->language))
1746 ast_waitstream(chan, "");
1748 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1749 ast_waitstream(chan, "");
1753 tweak_listen_volume(user, VOL_DOWN);
1756 tweak_listen_volume(user, VOL_UP);
1759 tweak_talk_volume(user, VOL_DOWN);
1765 tweak_talk_volume(user, VOL_UP);
1769 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1770 ast_waitstream(chan, "");
1776 ast_moh_start(chan, NULL, NULL);
1778 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1779 ast_log(LOG_WARNING, "Error setting conference\n");
1785 conf_flush(fd, chan);
1786 } else if (option_debug) {
1788 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1789 chan->name, f->frametype, f->subclass);
1792 } else if (outfd > -1) {
1793 if (user->control) {
1794 switch(user->control) {
1795 case AST_CONTROL_RINGING:
1796 case AST_CONTROL_PROGRESS:
1797 case AST_CONTROL_PROCEEDING:
1798 ast_indicate(chan, user->control);
1800 case AST_CONTROL_ANSWER:
1801 if (chan->_state != AST_STATE_UP)
1806 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1807 ast_device_state_changed("SLA:%s", conf->confno + 4);
1811 memset(&fr, 0, sizeof(fr));
1812 fr.frametype = AST_FRAME_DTMF;
1813 fr.subclass = user->dtmf;
1814 if (ast_write(chan, &fr) < 0) {
1815 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1820 res = read(outfd, buf, CONF_SIZE);
1822 memset(&fr, 0, sizeof(fr));
1823 fr.frametype = AST_FRAME_VOICE;
1824 fr.subclass = AST_FORMAT_SLINEAR;
1828 fr.offset = AST_FRIENDLY_OFFSET;
1829 if (!user->listen.actual &&
1830 ((confflags & CONFFLAG_MONITOR) ||
1831 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1832 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1835 for (index=0;index<AST_FRAME_BITS;index++)
1836 if (chan->rawwriteformat & (1 << index))
1838 if (index >= AST_FRAME_BITS)
1839 goto bailoutandtrynormal;
1840 ast_mutex_lock(&conf->listenlock);
1841 if (!conf->transframe[index]) {
1842 if (conf->origframe) {
1843 if (!conf->transpath[index])
1844 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1845 if (conf->transpath[index]) {
1846 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1847 if (!conf->transframe[index])
1848 conf->transframe[index] = &ast_null_frame;
1852 if (conf->transframe[index]) {
1853 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1854 if (ast_write(chan, conf->transframe[index]))
1855 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1858 ast_mutex_unlock(&conf->listenlock);
1859 goto bailoutandtrynormal;
1861 ast_mutex_unlock(&conf->listenlock);
1863 bailoutandtrynormal:
1864 if (user->listen.actual)
1865 ast_frame_adjust_volume(&fr, user->listen.actual);
1866 if (ast_write(chan, &fr) < 0) {
1867 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1871 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1873 lastmarked = currentmarked;
1883 /* Take out of conference */
1887 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1888 ast_log(LOG_WARNING, "Error setting conference\n");
1892 reset_volumes(user);
1894 AST_LIST_LOCK(&confs);
1895 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1896 conf_play(chan, conf, LEAVE);
1898 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1899 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1900 if ((conf->chan) && (conf->users > 1)) {
1901 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1902 ast_waitstream(conf->chan, "");
1903 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1904 ast_waitstream(conf->chan, "");
1906 ast_filedelete(user->namerecloc, NULL);
1909 AST_LIST_UNLOCK(&confs);
1912 AST_LIST_LOCK(&confs);
1917 if (user->user_no) { /* Only cleanup users who really joined! */
1919 hr = (now - user->jointime) / 3600;
1920 min = ((now - user->jointime) % 3600) / 60;
1921 sec = (now - user->jointime) % 60;
1924 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1929 "CallerIDNum: %s\r\n"
1930 "CallerIDName: %s\r\n"
1931 "Duration: %ld\r\n",
1932 chan->name, chan->uniqueid, conf->confno,
1934 S_OR(user->chan->cid.cid_num, "<unknown>"),
1935 S_OR(user->chan->cid.cid_name, "<unknown>"),
1936 (now - user->jointime));
1942 snprintf(members, sizeof(members), "%d", conf->users);
1943 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1944 if (confflags & CONFFLAG_MARKEDUSER)
1945 conf->markedusers--;
1946 /* Remove ourselves from the list */
1947 AST_LIST_REMOVE(&conf->userlist, user, list);
1949 /* Change any states */
1951 ast_device_state_changed("meetme:%s", conf->confno);
1952 if (confflags & (CONFFLAG_SLA_STATION|CONFFLAG_SLA_TRUNK))
1953 ast_device_state_changed("SLA:%s", conf->confno + 4);
1955 if (AST_LIST_EMPTY(&conf->userlist)) {
1956 /* close this one when no more users and no references*/
1957 if (!conf->refcount)
1960 /* Return the number of seconds the user was in the conf */
1961 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1962 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1965 AST_LIST_UNLOCK(&confs);
1970 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1971 char *dynamic_pin, int refcount, struct ast_flags *confflags)
1973 struct ast_variable *var;
1974 struct ast_conference *cnf;
1976 /* Check first in the conference list */
1977 AST_LIST_LOCK(&confs);
1978 AST_LIST_TRAVERSE(&confs, cnf, list) {
1979 if (!strcmp(confno, cnf->confno))
1983 cnf->refcount += refcount;
1985 AST_LIST_UNLOCK(&confs);
1988 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1990 cnf = ast_calloc(1, sizeof(struct ast_conference));
1992 ast_log(LOG_ERROR, "Out of memory\n");
1996 var = ast_load_realtime("meetme", "confno", confno, NULL);
1998 if (!strcasecmp(var->name, "confno")) {
1999 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
2000 } else if (!strcasecmp(var->name, "pin")) {
2001 pin = ast_strdupa(var->value);
2002 } else if (!strcasecmp(var->name, "adminpin")) {
2003 pinadmin = ast_strdupa(var->value);
2007 ast_variables_destroy(var);
2009 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2013 if (confflags && !cnf->chan &&
2014 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2015 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2016 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2017 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2020 if (confflags && !cnf->chan &&
2021 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2022 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2023 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2031 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2032 char *dynamic_pin, int refcount, struct ast_flags *confflags)
2034 struct ast_config *cfg;
2035 struct ast_variable *var;
2036 struct ast_conference *cnf;
2038 AST_DECLARE_APP_ARGS(args,
2039 AST_APP_ARG(confno);
2041 AST_APP_ARG(pinadmin);
2044 /* Check first in the conference list */
2045 AST_LIST_LOCK(&confs);
2046 AST_LIST_TRAVERSE(&confs, cnf, list) {
2047 if (!strcmp(confno, cnf->confno))
2051 cnf->refcount += refcount;
2053 AST_LIST_UNLOCK(&confs);
2057 /* No need to parse meetme.conf */
2059 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2061 if (dynamic_pin[0] == 'q') {
2062 /* Query the user to enter a PIN */
2063 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
2066 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2068 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2071 /* Check the config */
2072 cfg = ast_config_load(CONFIG_FILE_NAME);
2074 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2077 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2078 if (strcasecmp(var->name, "conf"))
2081 if (!(parse = ast_strdupa(var->value)))
2084 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2085 if (!strcasecmp(args.confno, confno)) {
2086 /* Bingo it's a valid conference */
2087 cnf = build_conf(args.confno,
2089 S_OR(args.pinadmin, ""),
2090 make, dynamic, refcount);
2096 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2098 ast_config_destroy(cfg);
2100 } else if (dynamic_pin) {
2101 /* Correct for the user selecting 'D' instead of 'd' to have
2102 someone join into a conference that has already been created
2104 if (dynamic_pin[0] == 'q')
2105 dynamic_pin[0] = '\0';
2109 if (confflags && !cnf->chan &&
2110 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2111 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2112 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2113 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2116 if (confflags && !cnf->chan &&
2117 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2118 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2119 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2126 /*! \brief The MeetmeCount application */
2127 static int count_exec(struct ast_channel *chan, void *data)
2129 struct ast_module_user *u;
2131 struct ast_conference *conf;
2135 AST_DECLARE_APP_ARGS(args,
2136 AST_APP_ARG(confno);
2137 AST_APP_ARG(varname);
2140 if (ast_strlen_zero(data)) {
2141 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2145 u = ast_module_user_add(chan);
2147 if (!(localdata = ast_strdupa(data))) {
2148 ast_module_user_remove(u);
2152 AST_STANDARD_APP_ARGS(args, localdata);
2154 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
2157 count = conf->users;
2161 if (!ast_strlen_zero(args.varname)){
2162 /* have var so load it and exit */
2163 snprintf(val, sizeof(val), "%d",count);
2164 pbx_builtin_setvar_helper(chan, args.varname, val);
2166 if (chan->_state != AST_STATE_UP)
2168 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2170 ast_module_user_remove(u);
2175 /*! \brief The meetme() application */
2176 static int conf_exec(struct ast_channel *chan, void *data)
2179 struct ast_module_user *u;
2180 char confno[AST_MAX_EXTENSION] = "";
2183 struct ast_conference *cnf;
2184 struct ast_flags confflags = {0};
2186 int empty = 0, empty_no_pin = 0;
2187 int always_prompt = 0;
2188 char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
2189 AST_DECLARE_APP_ARGS(args,
2190 AST_APP_ARG(confno);
2191 AST_APP_ARG(options);
2194 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2196 u = ast_module_user_add(chan);
2198 if (ast_strlen_zero(data)) {
2205 if (chan->_state != AST_STATE_UP)
2208 info = ast_strdupa(notdata);
2210 AST_STANDARD_APP_ARGS(args, info);
2213 ast_copy_string(confno, args.confno, sizeof(confno));
2214 if (ast_strlen_zero(confno)) {
2220 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2223 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2224 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2225 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2226 strcpy(the_pin, "q");
2228 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2229 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2230 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2237 int i, map[1024] = { 0, };
2238 struct ast_config *cfg;
2239 struct ast_variable *var;
2242 AST_LIST_LOCK(&confs);
2243 AST_LIST_TRAVERSE(&confs, cnf, list) {
2244 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2245 /* Disqualify in use conference */
2246 if (confno_int >= 0 && confno_int < 1024)
2250 AST_LIST_UNLOCK(&confs);
2252 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2253 if ((empty_no_pin) || (!dynamic)) {
2254 cfg = ast_config_load(CONFIG_FILE_NAME);
2256 var = ast_variable_browse(cfg, "rooms");
2258 if (!strcasecmp(var->name, "conf")) {
2259 char *stringp = ast_strdupa(var->value);
2261 char *confno_tmp = strsep(&stringp, "|,");
2263 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2264 if ((confno_int >= 0) && (confno_int < 1024)) {
2265 if (stringp && empty_no_pin) {
2271 /* For static: run through the list and see if this conference is empty */
2272 AST_LIST_LOCK(&confs);
2273 AST_LIST_TRAVERSE(&confs, cnf, list) {
2274 if (!strcmp(confno_tmp, cnf->confno)) {
2275 /* The conference exists, therefore it's not empty */
2280 AST_LIST_UNLOCK(&confs);
2282 /* At this point, we have a confno_tmp (static conference) that is empty */
2283 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2284 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2285 * Case 2: empty_no_pin and pin is blank (but not NULL)
2286 * Case 3: not empty_no_pin
2288 ast_copy_string(confno, confno_tmp, sizeof(confno));
2290 /* XXX the map is not complete (but we do have a confno) */
2298 ast_config_destroy(cfg);
2302 /* Select first conference number not in use */
2303 if (ast_strlen_zero(confno) && dynamic) {
2304 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2306 snprintf(confno, sizeof(confno), "%d", i);
2313 if (ast_strlen_zero(confno)) {
2314 res = ast_streamfile(chan, "conf-noempty", chan->language);
2316 ast_waitstream(chan, "");
2318 if (sscanf(confno, "%d", &confno_int) == 1) {
2319 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2320 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2322 ast_waitstream(chan, "");
2323 res = ast_say_digits(chan, confno_int, "", chan->language);
2327 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2332 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2333 /* Prompt user for conference number */
2334 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2336 /* Don't try to validate when we catch an error */
2342 if (!ast_strlen_zero(confno)) {
2343 /* Check the validity of the conference */
2344 cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2346 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2349 res = ast_streamfile(chan, "conf-invalid", chan->language);
2351 ast_waitstream(chan, "");
2356 if ((!ast_strlen_zero(cnf->pin) &&
2357 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2358 (!ast_strlen_zero(cnf->pinadmin) &&
2359 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2360 char pin[AST_MAX_EXTENSION]="";
2363 /* Allow the pin to be retried up to 3 times */
2364 for (j = 0; j < 3; j++) {
2365 if (*the_pin && (always_prompt == 0)) {
2366 ast_copy_string(pin, the_pin, sizeof(pin));
2369 /* Prompt user for pin if pin is required */
2370 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2373 if (!strcasecmp(pin, cnf->pin) ||
2374 (!ast_strlen_zero(cnf->pinadmin) &&
2375 !strcasecmp(pin, cnf->pinadmin))) {
2378 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2379 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2380 /* Run the conference */
2381 res = conf_run(chan, cnf, confflags.flags, optargs);
2385 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2386 res = ast_waitstream(chan, AST_DIGIT_ANY);
2387 ast_stopstream(chan);
2390 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2394 AST_LIST_LOCK(&confs);
2396 if (!cnf->refcount){
2399 AST_LIST_UNLOCK(&confs);
2409 /* failed when getting the pin */
2412 /* see if we need to get rid of the conference */
2413 AST_LIST_LOCK(&confs);
2415 if (!cnf->refcount) {
2418 AST_LIST_UNLOCK(&confs);
2422 /* Don't retry pin with a static pin */
2423 if (*the_pin && (always_prompt==0)) {
2428 /* No pin required */
2431 /* Run the conference */
2432 res = conf_run(chan, cnf, confflags.flags, optargs);
2436 } while (allowretry);
2438 ast_module_user_remove(u);
2443 struct sla_originate_helper {
2452 static void *sla_originate(void *data)
2454 struct sla_originate_helper *in = data;
2456 struct ast_channel *chan = NULL;
2458 ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, 99999, in->app, in->appdata, &reason, 1,
2459 S_OR(in->cid_num, NULL),
2460 S_OR(in->cid_name, NULL),
2462 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2464 ast_channel_unlock(chan);
2469 /*! Call in stations and trunk to the SLA */
2470 static void do_invite(struct ast_channel *orig, const char *tech, const char *dest, const char *app, const char *data)
2472 struct sla_originate_helper *slal;
2473 pthread_attr_t attr;
2476 if (!(slal = ast_calloc(1, sizeof(*slal))))
2479 ast_copy_string(slal->tech, tech, sizeof(slal->tech));
2480 ast_copy_string(slal->data, dest, sizeof(slal->data));
2481 ast_copy_string(slal->app, app, sizeof(slal->app));
2482 ast_copy_string(slal->appdata, data, sizeof(slal->appdata));
2483 if (orig->cid.cid_num)
2484 ast_copy_string(slal->cid_num, orig->cid.cid_num, sizeof(slal->cid_num));
2485 if (orig->cid.cid_name)
2486 ast_copy_string(slal->cid_name, orig->cid.cid_name, sizeof(slal->cid_name));
2487 pthread_attr_init(&attr);
2488 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2489 ast_pthread_create(&th, &attr, sla_originate, slal);
2492 static void invite_stations(struct ast_channel *orig, struct ast_sla *sla)
2494 ASTOBJ_CONTAINER_TRAVERSE(&sla->stations, 1, {
2495 do_invite(orig, iterator->tech, iterator->dest, "SLAStation", sla->name);
2499 static void invite_trunk(struct ast_channel *orig, struct ast_sla *sla)
2501 do_invite(orig, sla->trunktech, sla->trunkdest, "SLATrunk", sla->name);
2505 static int sla_checkforhold(struct ast_conference *conf, int hangup)
2507 struct ast_conf_user *user;
2508 struct ast_channel *onhold=NULL;
2510 int stationcount = 0;
2512 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2513 if (user->userflags & CONFFLAG_SLA_STATION) {
2515 if ((user->userflags & CONFFLAG_HOLD)) {
2517 onhold = user->chan;
2521 if ((holdcount == 1) && (stationcount == 1)) {
2524 ast_softhangup(onhold, AST_SOFTHANGUP_EXPLICIT);
2525 } else if (holdcount && (stationcount == holdcount))
2531 /*! \brief The slas()/slat() application */
2532 static int sla_exec(struct ast_channel *chan, void *data, int trunk)
2535 struct ast_module_user *u;
2536 char confno[AST_MAX_EXTENSION] = "";
2537 struct ast_sla *sla;
2538 struct ast_conference *cnf;
2540 struct ast_flags confflags = {0};
2542 char *options[OPT_ARG_ARRAY_SIZE] = { NULL, };
2543 AST_DECLARE_APP_ARGS(args,
2544 AST_APP_ARG(confno);
2545 AST_APP_ARG(options);
2548 if (ast_strlen_zero(data)) {
2549 ast_log(LOG_WARNING, "SLA%c requires an argument (line)\n", trunk ? 'T' : 'S');
2553 info = ast_strdupa(data);
2555 AST_STANDARD_APP_ARGS(args, info);
2557 if (ast_strlen_zero(args.confno)) {
2558 ast_log(LOG_WARNING, "SLA%c requires an SLA line number\n", trunk ? 'T' : 'S');
2562 u = ast_module_user_add(chan);
2565 ast_app_parse_options(sla_opts, &confflags, NULL, args.options);
2567 ast_set_flag(&confflags, CONFFLAG_QUIET|CONFFLAG_DYNAMIC);
2569 ast_set_flag(&confflags, CONFFLAG_WAITMARKED|CONFFLAG_MARKEDEXIT|CONFFLAG_SLA_TRUNK);
2571 ast_set_flag(&confflags, CONFFLAG_MARKEDUSER|CONFFLAG_SLA_STATION);
2573 sla = ASTOBJ_CONTAINER_FIND(&slas, args.confno);
2575 snprintf(confno, sizeof(confno), "sla-%s", args.confno);
2576 cnf = find_conf(chan, confno, 1, dynamic, "", 1, &confflags);
2578 sla_checkforhold(cnf, 1);
2581 ast_indicate(chan, AST_CONTROL_RINGING);
2582 invite_stations(chan, sla);
2584 invite_trunk(chan, sla);
2585 } else if (chan->_state != AST_STATE_UP)
2588 /* Run the conference */
2589 res = conf_run(chan, cnf, confflags.flags, options);
2591 ast_log(LOG_WARNING, "SLA%c: Found SLA '%s' but unable to build conference!\n", trunk ? 'T' : 'S', args.confno);
2592 ASTOBJ_UNREF(sla, sla_destroy);
2594 ast_log(LOG_WARNING, "SLA%c: SLA '%s' not found!\n", trunk ? 'T' : 'S', args.confno);
2597 ast_module_user_remove(u);
2602 /*! \brief The slas() wrapper */
2603 static int slas_exec(struct ast_channel *chan, void *data)
2605 return sla_exec(chan, data, 0);
2608 /*! \brief The slat() wrapper */
2609 static int slat_exec(struct ast_channel *chan, void *data)
2611 return sla_exec(chan, data, 1);
2614 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2616 struct ast_conf_user *user = NULL;
2619 sscanf(callerident, "%i", &cid);
2620 if (conf && callerident) {
2621 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2622 if (cid == user->user_no)
2629 /*! \brief The MeetMeadmin application */
2630 /* MeetMeAdmin(confno, command, caller) */
2631 static int admin_exec(struct ast_channel *chan, void *data) {
2633 struct ast_conference *cnf;
2634 struct ast_conf_user *user = NULL;
2635 struct ast_module_user *u;
2636 AST_DECLARE_APP_ARGS(args,
2637 AST_APP_ARG(confno);
2638 AST_APP_ARG(command);
2642 if (ast_strlen_zero(data)) {
2643 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2647 u = ast_module_user_add(chan);
2649 AST_LIST_LOCK(&confs);
2651 params = ast_strdupa(data);
2652 AST_STANDARD_APP_ARGS(args, params);
2654 if (!args.command) {
2655 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2656 AST_LIST_UNLOCK(&confs);
2657 ast_module_user_remove(u);
2660 AST_LIST_TRAVERSE(&confs, cnf, list) {
2661 if (!strcmp(cnf->confno, args.confno))
2666 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2667 AST_LIST_UNLOCK(&confs);
2668 ast_module_user_remove(u);
2673 user = find_user(cnf, args.user);
2675 switch (*args.command) {
2676 case 76: /* L: Lock */
2679 case 108: /* l: Unlock */
2682 case 75: /* K: kick all users */
2683 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2684 user->adminflags |= ADMINFLAG_KICKME;
2686 case 101: /* e: Eject last user*/
2687 user = AST_LIST_LAST(&cnf->userlist);
2688 if (!(user->userflags & CONFFLAG_ADMIN))
2689 user->adminflags |= ADMINFLAG_KICKME;
2691 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2693 case 77: /* M: Mute */
2695 user->adminflags |= ADMINFLAG_MUTED;
2697 ast_log(LOG_NOTICE, "Specified User not found!\n");
2699 case 78: /* N: Mute all (non-admin) users */
2700 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2701 if (!(user->userflags & CONFFLAG_ADMIN))
2702 user->adminflags |= ADMINFLAG_MUTED;
2705 case 109: /* m: Unmute */
2707 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2709 ast_log(LOG_NOTICE, "Specified User not found!\n");
2711 case 110: /* n: Unmute all users */
2712 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2713 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2715 case 107: /* k: Kick user */
2717 user->adminflags |= ADMINFLAG_KICKME;
2719 ast_log(LOG_NOTICE, "Specified User not found!\n");
2721 case 118: /* v: Lower all users listen volume */
2722 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2723 tweak_listen_volume(user, VOL_DOWN);
2725 case 86: /* V: Raise all users listen volume */
2726 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2727 tweak_listen_volume(user, VOL_UP);
2729 case 115: /* s: Lower all users speaking volume */
2730 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2731 tweak_talk_volume(user, VOL_DOWN);
2733 case 83: /* S: Raise all users speaking volume */
2734 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2735 tweak_talk_volume(user, VOL_UP);
2737 case 82: /* R: Reset all volume levels */
2738 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2739 reset_volumes(user);
2741 case 114: /* r: Reset user's volume level */
2743 reset_volumes(user);
2745 ast_log(LOG_NOTICE, "Specified User not found!\n");
2747 case 85: /* U: Raise user's listen volume */
2749 tweak_listen_volume(user, VOL_UP);
2751 ast_log(LOG_NOTICE, "Specified User not found!\n");
2753 case 117: /* u: Lower user's listen volume */
2755 tweak_listen_volume(user, VOL_DOWN);
2757 ast_log(LOG_NOTICE, "Specified User not found!\n");
2759 case 84: /* T: Raise user's talk volume */
2761 tweak_talk_volume(user, VOL_UP);
2763 ast_log(LOG_NOTICE, "Specified User not found!\n");
2765 case 116: /* t: Lower user's talk volume */
2767 tweak_talk_volume(user, VOL_DOWN);
2769 ast_log(LOG_NOTICE, "Specified User not found!\n");
2773 AST_LIST_UNLOCK(&confs);
2775 ast_module_user_remove(u);
2780 static int meetmemute(struct mansession *s, struct message *m, int mute)
2782 struct ast_conference *conf;
2783 struct ast_conf_user *user;
2784 char *confid = astman_get_header(m, "Meetme");
2785 char *userid = astman_get_header(m, "Usernum");
2788 if (ast_strlen_zero(confid)) {
2789 astman_send_error(s, m, "Meetme conference not specified");
2793 if (ast_strlen_zero(userid)) {
2794 astman_send_error(s, m, "Meetme user number not specified");
2798 userno = strtoul(userid, &userid, 10);
2801 astman_send_error(s, m, "Invalid user number");
2805 /* Look in the conference list */
2806 AST_LIST_LOCK(&confs);
2807 AST_LIST_TRAVERSE(&confs, conf, list) {
2808 if (!strcmp(confid, conf->confno))
2813 AST_LIST_UNLOCK(&confs);
2814 astman_send_error(s, m, "Meetme conference does not exist");
2818 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2819 if (user->user_no == userno)
2823 AST_LIST_UNLOCK(&confs);
2824 astman_send_error(s, m, "User number not found");
2829 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2831 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2833 AST_LIST_UNLOCK(&confs);
2835 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);
2837 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2841 static int action_meetmemute(struct mansession *s, struct message *m)
2843 return meetmemute(s, m, 1);
2846 static int action_meetmeunmute(struct mansession *s, struct message *m)
2848 return meetmemute(s, m, 0);
2851 static void *recordthread(void *args)
2853 struct ast_conference *cnf = args;
2854 struct ast_frame *f=NULL;
2856 struct ast_filestream *s=NULL;
2859 const char *oldrecordingfilename = NULL;
2861 if (!cnf || !cnf->lchan) {
2865 ast_stopstream(cnf->lchan);
2866 flags = O_CREAT|O_TRUNC|O_WRONLY;
2869 cnf->recording = MEETME_RECORD_ACTIVE;
2870 while (ast_waitfor(cnf->lchan, -1) > -1) {
2871 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2872 AST_LIST_LOCK(&confs);
2873 AST_LIST_UNLOCK(&confs);
2876 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2877 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
2878 oldrecordingfilename = cnf->recordingfilename;
2881 f = ast_read(cnf->lchan);
2886 if (f->frametype == AST_FRAME_VOICE) {
2887 ast_mutex_lock(&cnf->listenlock);
2888 for (x=0;x<AST_FRAME_BITS;x++) {
2889 /* Free any translations that have occured */
2890 if (cnf->transframe[x]) {
2891 ast_frfree(cnf->transframe[x]);
2892 cnf->transframe[x] = NULL;
2896 ast_frfree(cnf->origframe);
2898 ast_mutex_unlock(&cnf->listenlock);
2900 res = ast_writestream(s, f);
2908 cnf->recording = MEETME_RECORD_OFF;
2915 /*! \brief Callback for devicestate providers */
2916 static int meetmestate(const char *data)
2918 struct ast_conference *conf;
2920 /* Find conference */
2921 AST_LIST_LOCK(&confs);
2922 AST_LIST_TRAVERSE(&confs, conf, list) {
2923 if (!strcmp(data, conf->confno))
2926 AST_LIST_UNLOCK(&confs);
2928 return AST_DEVICE_INVALID;
2933 return AST_DEVICE_NOT_INUSE;
2935 return AST_DEVICE_INUSE;
2938 /*! \brief Callback for devicestate providers */
2939 static int slastate(const char *data)
2941 struct ast_conference *conf;
2942 struct ast_sla *sla, *sla2;
2945 ast_log(LOG_DEBUG, "asked for sla state for '%s'\n", data);
2947 /* Find conference */
2948 AST_LIST_LOCK(&confs);
2949 AST_LIST_TRAVERSE(&confs, conf, list) {
2950 if (!strncmp(conf->confno, "sla-", 4) && !strcmp(data, conf->confno + 4))
2953 AST_LIST_UNLOCK(&confs);
2955 /* Find conference */
2956 sla = sla2 = ASTOBJ_CONTAINER_FIND(&slas, data);
2959 return AST_DEVICE_INVALID;
2961 ASTOBJ_UNREF(sla2, sla_destroy);
2964 ast_log(LOG_DEBUG, "for '%s' conf = %p, sla = %p\n", data, conf, sla);
2967 return AST_DEVICE_INVALID;
2970 if (!conf || !conf->users)
2971 return AST_DEVICE_NOT_INUSE;
2973 if (conf && sla_checkforhold(conf, 0))
2974 return AST_DEVICE_ONHOLD;
2976 if ((conf->users == 1) && (AST_LIST_FIRST(&conf->userlist)->userflags & CONFFLAG_SLA_TRUNK))
2977 return AST_DEVICE_RINGING;
2979 return AST_DEVICE_INUSE;
2982 static void load_config_meetme(void)
2984 struct ast_config *cfg;
2987 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2989 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2992 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2993 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2994 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2995 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2996 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2997 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2998 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2999 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3001 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3002 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3005 ast_config_destroy(cfg);
3008 /*! Append SLA station to station list */
3009 static void append_station(struct ast_sla *sla, const char *station)
3011 struct ast_sla_station *s;
3014 s = ast_calloc(1, sizeof(struct ast_sla_station) + strlen(station) + 2);
3017 strcpy(s->tech, station);
3018 c = strchr(s->tech, '/');
3022 ASTOBJ_CONTAINER_LINK(&sla->stations, s);
3024 ast_log(LOG_WARNING, "station '%s' should be in tech/destination format! Ignoring!\n", station);
3030 /*! Parse SLA configuration file and create objects */
3031 static void parse_sla(const char *cat, struct ast_variable *v)
3033 struct ast_sla *sla;
3035 sla = ASTOBJ_CONTAINER_FIND(&slas, cat);
3037 sla = ast_calloc(1, sizeof(struct ast_sla));
3040 ast_copy_string(sla->name, cat, sizeof(sla->name));
3041 snprintf(sla->confname, sizeof(sla->confname), "sla-%s", sla->name);
3042 ASTOBJ_CONTAINER_LINK(&slas, sla);
3048 ASTOBJ_CONTAINER_DESTROYALL(&sla->stations, station_destroy);
3050 if (!strcasecmp(v->name, "trunk")) {
3052 c = strchr(v->value, '/');
3054 ast_copy_string(sla->trunktech, v->value, (c - v->value) + 1);
3055 ast_copy_string(sla->trunkdest, c + 1, sizeof(sla->trunkdest));
3057 } else if (!strcasecmp(v->name, "station")) {
3058 append_station(sla, v->value);
3063 ast_device_state_changed("SLA:%s", cat);
3067 /*! If there is a SLA configuration file, parse it */