2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
33 <depend>zaptel</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
46 #include "asterisk/zapata.h"
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/dsp.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/options.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65 #include "asterisk/astobj.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/dial.h"
72 #define CONFIG_FILE_NAME "meetme.conf"
73 #define SLA_CONFIG_FILE "sla.conf"
75 /*! each buffer is 20ms, so this is 640ms total */
76 #define DEFAULT_AUDIO_BUFFERS 32
79 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
80 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
81 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
84 #define MEETME_DELAYDETECTTALK 300
85 #define MEETME_DELAYDETECTENDTALK 1000
87 #define AST_FRAME_BITS 32
99 enum recording_state {
101 MEETME_RECORD_STARTED,
102 MEETME_RECORD_ACTIVE,
103 MEETME_RECORD_TERMINATE
106 #define CONF_SIZE 320
109 /*! user has admin access on the conference */
110 CONFFLAG_ADMIN = (1 << 0),
111 /*! If set the user can only receive audio from the conference */
112 CONFFLAG_MONITOR = (1 << 1),
113 /*! If set asterisk will exit conference when '#' is pressed */
114 CONFFLAG_POUNDEXIT = (1 << 2),
115 /*! If set asterisk will provide a menu to the user when '*' is pressed */
116 CONFFLAG_STARMENU = (1 << 3),
117 /*! If set the use can only send audio to the conference */
118 CONFFLAG_TALKER = (1 << 4),
119 /*! If set there will be no enter or leave sounds */
120 CONFFLAG_QUIET = (1 << 5),
121 /*! If set, when user joins the conference, they will be told the number
122 * of users that are already in */
123 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
124 /*! Set to run AGI Script in Background */
125 CONFFLAG_AGI = (1 << 7),
126 /*! Set to have music on hold when user is alone in conference */
127 CONFFLAG_MOH = (1 << 8),
128 /*! If set the MeetMe will return if all marked with this flag left */
129 CONFFLAG_MARKEDEXIT = (1 << 9),
130 /*! If set, the MeetMe will wait until a marked user enters */
131 CONFFLAG_WAITMARKED = (1 << 10),
132 /*! If set, the MeetMe will exit to the specified context */
133 CONFFLAG_EXIT_CONTEXT = (1 << 11),
134 /*! If set, the user will be marked */
135 CONFFLAG_MARKEDUSER = (1 << 12),
136 /*! If set, user will be ask record name on entry of conference */
137 CONFFLAG_INTROUSER = (1 << 13),
138 /*! If set, the MeetMe will be recorded */
139 CONFFLAG_RECORDCONF = (1<< 14),
140 /*! If set, the user will be monitored if the user is talking or not */
141 CONFFLAG_MONITORTALKER = (1 << 15),
142 CONFFLAG_DYNAMIC = (1 << 16),
143 CONFFLAG_DYNAMICPIN = (1 << 17),
144 CONFFLAG_EMPTY = (1 << 18),
145 CONFFLAG_EMPTYNOPIN = (1 << 19),
146 CONFFLAG_ALWAYSPROMPT = (1 << 20),
147 /*! If set, treats talking users as muted users */
148 CONFFLAG_OPTIMIZETALKER = (1 << 21),
149 /*! If set, won't speak the extra prompt when the first person
150 * enters the conference */
151 CONFFLAG_NOONLYPERSON = (1 << 22),
152 /*! If set, user will be asked to record name on entry of conference
154 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
155 /*! If set, the user will be initially self-muted */
156 CONFFLAG_STARTMUTED = (1 << 24),
157 /*! Pass DTMF through the conference */
158 CONFFLAG_PASS_DTMF = (1 << 25),
159 CONFFLAG_SLA_STATION = (1 << 26),
160 CONFFLAG_SLA_TRUNK = (1 << 27),
161 /*! If set, the user should continue in the dialplan if kicked out */
162 CONFFLAG_KICK_CONTINUE = (1 << 28)
166 OPT_ARG_WAITMARKED = 0,
167 OPT_ARG_ARRAY_SIZE = 1,
170 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
171 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
172 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
173 AST_APP_OPTION('b', CONFFLAG_AGI ),
174 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
175 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
176 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
177 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
178 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
179 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
180 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
181 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
182 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
183 AST_APP_OPTION('M', CONFFLAG_MOH ),
184 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
185 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
186 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
187 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
188 AST_APP_OPTION('q', CONFFLAG_QUIET ),
189 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
190 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
191 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
192 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
193 AST_APP_OPTION('t', CONFFLAG_TALKER ),
194 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
195 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
196 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
197 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
200 static const char *app = "MeetMe";
201 static const char *app2 = "MeetMeCount";
202 static const char *app3 = "MeetMeAdmin";
203 static const char *slastation_app = "SLAStation";
204 static const char *slatrunk_app = "SLATrunk";
206 static const char *synopsis = "MeetMe conference bridge";
207 static const char *synopsis2 = "MeetMe participant count";
208 static const char *synopsis3 = "MeetMe conference Administration";
209 static const char *slastation_synopsis = "Shared Line Appearance Station";
210 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
212 static const char *descrip =
213 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
214 "conference. If the conference number is omitted, the user will be prompted\n"
215 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
216 "is specified, by pressing '#'.\n"
217 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
218 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
219 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
220 "The option string may contain zero or more of the following characters:\n"
221 " 'a' -- set admin mode\n"
222 " 'A' -- set marked mode\n"
223 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
224 " Default: conf-background.agi (Note: This does not work with\n"
225 " non-Zap channels in the same conference)\n"
226 " 'c' -- announce user(s) count on joining a conference\n"
227 " 'C' -- continue in dialplan when kicked out of conference\n"
228 " 'd' -- dynamically add conference\n"
229 " 'D' -- dynamically add conference, prompting for a PIN\n"
230 " 'e' -- select an empty conference\n"
231 " 'E' -- select an empty pinless conference\n"
232 " 'F' -- Pass DTMF through the conference. DTMF used to activate any\n"
233 " conference features will not be passed through.\n"
234 " 'i' -- announce user join/leave with review\n"
235 " 'I' -- announce user join/leave without review\n"
236 " 'l' -- set listen only mode (Listen only, no talking)\n"
237 " 'm' -- set initially muted\n"
238 " 'M' -- enable music on hold when the conference has a single caller\n"
239 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
240 " being muted, meaning (a) No encode is done on transmission and\n"
241 " (b) Received audio that is not registered as talking is omitted\n"
242 " causing no buildup in background noise\n"
243 " 'p' -- allow user to exit the conference by pressing '#'\n"
244 " 'P' -- always prompt for the pin even if it is specified\n"
245 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
246 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
247 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
248 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
250 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
251 " 't' -- set talk only mode. (Talk only, no listening)\n"
252 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
254 " -- wait until the marked user enters the conference\n"
255 " 'x' -- close the conference when last marked user exits\n"
256 " 'X' -- allow user to exit the conference by entering a valid single\n"
257 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
258 " if that variable is not defined.\n"
259 " '1' -- do not play message when first person enters\n";
261 static const char *descrip2 =
262 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
263 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
264 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
265 "the channel, unless priority n+1 exists, in which case priority progress will\n"
267 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
269 static const char *descrip3 =
270 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
271 " 'e' -- Eject last user that joined\n"
272 " 'k' -- Kick one user out of conference\n"
273 " 'K' -- Kick all users out of conference\n"
274 " 'l' -- Unlock conference\n"
275 " 'L' -- Lock conference\n"
276 " 'm' -- Unmute one user\n"
277 " 'M' -- Mute one user\n"
278 " 'n' -- Unmute all users in the conference\n"
279 " 'N' -- Mute all non-admin users in the conference\n"
280 " 'r' -- Reset one user's volume settings\n"
281 " 'R' -- Reset all users volume settings\n"
282 " 's' -- Lower entire conference speaking volume\n"
283 " 'S' -- Raise entire conference speaking volume\n"
284 " 't' -- Lower one user's talk volume\n"
285 " 'T' -- Raise one user's talk volume\n"
286 " 'u' -- Lower one user's listen volume\n"
287 " 'U' -- Raise one user's listen volume\n"
288 " 'v' -- Lower entire conference listening volume\n"
289 " 'V' -- Raise entire conference listening volume\n"
292 static const char *slastation_desc =
295 static const char *slatrunk_desc =
298 #define MAX_CONFNUM 80
301 /*! \brief The MeetMe Conference object */
302 struct ast_conference {
303 ast_mutex_t playlock; /*!< Conference specific lock (players) */
304 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
305 char confno[MAX_CONFNUM]; /*!< Conference */
306 struct ast_channel *chan; /*!< Announcements channel */
307 struct ast_channel *lchan; /*!< Listen/Record channel */
308 int fd; /*!< Announcements fd */
309 int zapconf; /*!< Zaptel Conf # */
310 int users; /*!< Number of active users */
311 int markedusers; /*!< Number of marked users */
312 time_t start; /*!< Start time (s) */
313 int refcount; /*!< reference count of usage */
314 enum recording_state recording:2; /*!< recording status */
315 unsigned int isdynamic:1; /*!< Created on the fly? */
316 unsigned int locked:1; /*!< Is the conference locked? */
317 pthread_t recordthread; /*!< thread for recording */
318 pthread_attr_t attr; /*!< thread attribute */
319 const char *recordingfilename; /*!< Filename to record the Conference into */
320 const char *recordingformat; /*!< Format to record the Conference in */
321 char pin[MAX_PIN]; /*!< If protected by a PIN */
322 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
323 struct ast_frame *transframe[32];
324 struct ast_frame *origframe;
325 struct ast_trans_pvt *transpath[32];
326 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
327 AST_LIST_ENTRY(ast_conference) list;
330 static AST_LIST_HEAD_STATIC(confs, ast_conference);
333 int desired; /*!< Desired volume adjustment */
334 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
337 /*! \brief The MeetMe User object */
338 struct ast_conf_user {
339 int user_no; /*!< User Number */
340 int userflags; /*!< Flags as set in the conference */
341 int adminflags; /*!< Flags set by the Admin */
342 struct ast_channel *chan; /*!< Connected channel */
343 int talking; /*!< Is user talking */
344 int zapchannel; /*!< Is a Zaptel channel */
345 char usrvalue[50]; /*!< Custom User Value */
346 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
347 time_t jointime; /*!< Time the user joined the conference */
349 struct volume listen;
350 AST_LIST_ENTRY(ast_conf_user) list;
353 enum sla_which_trunk_refs {
358 enum sla_trunk_state {
359 SLA_TRUNK_STATE_IDLE,
360 SLA_TRUNK_STATE_RINGING,
362 SLA_TRUNK_STATE_ONHOLD,
365 enum sla_hold_access {
366 /*! This means that any station can put it on hold, and any station
367 * can retrieve the call from hold. */
369 /*! This means that only the station that put the call on hold may
370 * retrieve it from hold. */
374 struct sla_trunk_ref;
377 AST_RWLIST_ENTRY(sla_station) entry;
378 AST_DECLARE_STRING_FIELDS(
379 AST_STRING_FIELD(name);
380 AST_STRING_FIELD(device);
381 AST_STRING_FIELD(autocontext);
383 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
384 struct ast_dial *dial;
385 /*! Ring timeout for this station, for any trunk. If a ring timeout
386 * is set for a specific trunk on this station, that will take
387 * priority over this value. */
388 unsigned int ring_timeout;
389 /*! Ring delay for this station, for any trunk. If a ring delay
390 * is set for a specific trunk on this station, that will take
391 * priority over this value. */
392 unsigned int ring_delay;
393 /*! This option uses the values in the sla_hold_access enum and sets the
394 * access control type for hold on this station. */
395 unsigned int hold_access:1;
398 struct sla_station_ref {
399 AST_LIST_ENTRY(sla_station_ref) entry;
400 struct sla_station *station;
404 AST_RWLIST_ENTRY(sla_trunk) entry;
405 AST_DECLARE_STRING_FIELDS(
406 AST_STRING_FIELD(name);
407 AST_STRING_FIELD(device);
408 AST_STRING_FIELD(autocontext);
410 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
411 /*! Number of stations that use this trunk */
412 unsigned int num_stations;
413 /*! Number of stations currently on a call with this trunk */
414 unsigned int active_stations;
415 /*! Number of stations that have this trunk on hold. */
416 unsigned int hold_stations;
417 struct ast_channel *chan;
418 unsigned int ring_timeout;
419 /*! If set to 1, no station will be able to join an active call with
421 unsigned int barge_disabled:1;
422 /*! This option uses the values in the sla_hold_access enum and sets the
423 * access control type for hold on this trunk. */
424 unsigned int hold_access:1;
427 struct sla_trunk_ref {
428 AST_LIST_ENTRY(sla_trunk_ref) entry;
429 struct sla_trunk *trunk;
430 enum sla_trunk_state state;
431 struct ast_channel *chan;
432 /*! Ring timeout to use when this trunk is ringing on this specific
433 * station. This takes higher priority than a ring timeout set at
434 * the station level. */
435 unsigned int ring_timeout;
436 /*! Ring delay to use when this trunk is ringing on this specific
437 * station. This takes higher priority than a ring delay set at
438 * the station level. */
439 unsigned int ring_delay;
442 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
443 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
445 static const char sla_registrar[] = "SLA";
447 /*! \brief Event types that can be queued up for the SLA thread */
448 enum sla_event_type {
449 /*! A station has put the call on hold */
451 /*! A station has taken the call off of hold */
453 /*! The state of a dial has changed */
454 SLA_EVENT_DIAL_STATE,
455 /*! The state of a ringing trunk has changed */
456 SLA_EVENT_RINGING_TRUNK,
460 enum sla_event_type type;
461 struct sla_station *station;
462 struct sla_trunk_ref *trunk_ref;
463 AST_LIST_ENTRY(sla_event) entry;
466 /*! \brief A station that failed to be dialed
467 * \note Only used by the SLA thread. */
468 struct sla_failed_station {
469 struct sla_station *station;
470 struct timeval last_try;
471 AST_LIST_ENTRY(sla_failed_station) entry;
474 /*! \brief A trunk that is ringing */
475 struct sla_ringing_trunk {
476 struct sla_trunk *trunk;
477 /*! The time that this trunk started ringing */
478 struct timeval ring_begin;
479 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
480 AST_LIST_ENTRY(sla_ringing_trunk) entry;
483 enum sla_station_hangup {
484 SLA_STATION_HANGUP_NORMAL,
485 SLA_STATION_HANGUP_TIMEOUT,
488 /*! \brief A station that is ringing */
489 struct sla_ringing_station {
490 struct sla_station *station;
491 /*! The time that this station started ringing */
492 struct timeval ring_begin;
493 AST_LIST_ENTRY(sla_ringing_station) entry;
497 * \brief A structure for data used by the sla thread
500 /*! The SLA thread ID */
504 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
505 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
506 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
507 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
510 .thread = AST_PTHREADT_NULL,
513 /*! The number of audio buffers to be allocated on pseudo channels
514 * when in a conference */
515 static int audio_buffers;
517 /*! Map 'volume' levels from -5 through +5 into
518 * decibel (dB) settings for channel drivers
519 * Note: these are not a straight linear-to-dB
520 * conversion... the numbers have been modified
521 * to give the user a better level of adjustability
523 static char const gain_map[] = {
538 static int admin_exec(struct ast_channel *chan, void *data);
539 static void *recordthread(void *args);
541 static char *istalking(int x)
546 return "(unmonitored)";
548 return "(not talking)";
551 static int careful_write(int fd, unsigned char *data, int len, int block)
558 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
559 res = ioctl(fd, ZT_IOMUX, &x);
563 res = write(fd, data, len);
565 if (errno != EAGAIN) {
566 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
578 static int set_talk_volume(struct ast_conf_user *user, int volume)
582 /* attempt to make the adjustment in the channel driver;
583 if successful, don't adjust in the frame reading routine
585 gain_adjust = gain_map[volume + 5];
587 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
590 static int set_listen_volume(struct ast_conf_user *user, int volume)
594 /* attempt to make the adjustment in the channel driver;
595 if successful, don't adjust in the frame reading routine
597 gain_adjust = gain_map[volume + 5];
599 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
602 static void tweak_volume(struct volume *vol, enum volume_action action)
606 switch (vol->desired) {
621 switch (vol->desired) {
637 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
639 tweak_volume(&user->talk, action);
640 /* attempt to make the adjustment in the channel driver;
641 if successful, don't adjust in the frame reading routine
643 if (!set_talk_volume(user, user->talk.desired))
644 user->talk.actual = 0;
646 user->talk.actual = user->talk.desired;
649 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
651 tweak_volume(&user->listen, action);
652 /* attempt to make the adjustment in the channel driver;
653 if successful, don't adjust in the frame reading routine
655 if (!set_listen_volume(user, user->listen.desired))
656 user->listen.actual = 0;
658 user->listen.actual = user->listen.desired;
661 static void reset_volumes(struct ast_conf_user *user)
663 signed char zero_volume = 0;
665 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
666 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
669 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
675 if (!chan->_softhangup)
676 res = ast_autoservice_start(chan);
678 AST_LIST_LOCK(&confs);
694 careful_write(conf->fd, data, len, 1);
697 AST_LIST_UNLOCK(&confs);
700 ast_autoservice_stop(chan);
704 * \brief Find or create a conference
706 * \param confno The conference name/number
707 * \param pin The regular user pin
708 * \param pinadmin The admin pin
709 * \param make Make the conf if it doesn't exist
710 * \param dynamic Mark the newly created conference as dynamic
711 * \param refcount How many references to mark on the conference
713 * \return A pointer to the conference struct, or NULL if it wasn't found and
714 * make or dynamic were not set.
716 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
718 struct ast_conference *cnf;
719 struct zt_confinfo ztc = { 0, };
721 AST_LIST_LOCK(&confs);
723 AST_LIST_TRAVERSE(&confs, cnf, list) {
724 if (!strcmp(confno, cnf->confno))
728 if (cnf || (!make && !dynamic))
732 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
735 ast_mutex_init(&cnf->playlock);
736 ast_mutex_init(&cnf->listenlock);
737 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
738 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
739 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
740 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
742 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
743 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
744 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
746 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
747 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
749 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
756 /* Setup a new zap conference */
758 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
759 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
760 ast_log(LOG_WARNING, "Error setting conference\n");
762 ast_hangup(cnf->chan);
769 /* Fill the conference struct */
770 cnf->start = time(NULL);
771 cnf->zapconf = ztc.confno;
772 cnf->isdynamic = dynamic ? 1 : 0;
773 if (option_verbose > 2)
774 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
775 AST_LIST_INSERT_HEAD(&confs, cnf, list);
779 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
781 AST_LIST_UNLOCK(&confs);
786 static int meetme_cmd(int fd, int argc, char **argv)
788 /* Process the command */
789 struct ast_conference *cnf;
790 struct ast_conf_user *user;
792 int i = 0, total = 0;
794 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
795 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
796 char cmdline[1024] = "";
799 ast_cli(fd, "Invalid Arguments.\n");
800 /* Check for length so no buffer will overflow... */
801 for (i = 0; i < argc; i++) {
802 if (strlen(argv[i]) > 100)
803 ast_cli(fd, "Invalid Arguments.\n");
806 /* 'MeetMe': List all the conferences */
808 if (AST_LIST_EMPTY(&confs)) {
809 ast_cli(fd, "No active MeetMe conferences.\n");
810 return RESULT_SUCCESS;
812 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
813 AST_LIST_TRAVERSE(&confs, cnf, list) {
814 if (cnf->markedusers == 0)
815 strcpy(cmdline, "N/A ");
817 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
818 hr = (now - cnf->start) / 3600;
819 min = ((now - cnf->start) % 3600) / 60;
820 sec = (now - cnf->start) % 60;
822 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
826 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
827 return RESULT_SUCCESS;
830 return RESULT_SHOWUSAGE;
831 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
832 if (strstr(argv[1], "lock")) {
833 if (strcmp(argv[1], "lock") == 0) {
835 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
838 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
840 } else if (strstr(argv[1], "mute")) {
842 return RESULT_SHOWUSAGE;
843 if (strcmp(argv[1], "mute") == 0) {
845 if (strcmp(argv[3], "all") == 0) {
846 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
848 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
849 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
853 if (strcmp(argv[3], "all") == 0) {
854 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
856 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
857 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
860 } else if (strcmp(argv[1], "kick") == 0) {
862 return RESULT_SHOWUSAGE;
863 if (strcmp(argv[3], "all") == 0) {
865 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
867 /* Kick a single user */
868 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
869 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
871 } else if(strcmp(argv[1], "list") == 0) {
872 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
873 /* List all the users in a conference */
874 if (AST_LIST_EMPTY(&confs)) {
876 ast_cli(fd, "No active conferences.\n");
877 return RESULT_SUCCESS;
879 /* Find the right conference */
880 AST_LIST_TRAVERSE(&confs, cnf, list) {
881 if (strcmp(cnf->confno, argv[2]) == 0)
886 ast_cli(fd, "No such conference: %s.\n",argv[2]);
887 return RESULT_SUCCESS;
889 /* Show all the users */
890 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
892 hr = (now - user->jointime) / 3600;
893 min = ((now - user->jointime) % 3600) / 60;
894 sec = (now - user->jointime) % 60;
896 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
898 S_OR(user->chan->cid.cid_num, "<unknown>"),
899 S_OR(user->chan->cid.cid_name, "<no name>"),
901 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
902 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
903 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
904 istalking(user->talking), hr, min, sec);
906 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
908 S_OR(user->chan->cid.cid_num, ""),
909 S_OR(user->chan->cid.cid_name, ""),
911 user->userflags & CONFFLAG_ADMIN ? "1" : "",
912 user->userflags & CONFFLAG_MONITOR ? "1" : "",
913 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
914 user->talking, hr, min, sec);
918 ast_cli(fd,"%d users in that conference.\n",cnf->users);
920 return RESULT_SUCCESS;
922 return RESULT_SHOWUSAGE;
925 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
927 admin_exec(NULL, cmdline);
932 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
934 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
936 int len = strlen(word);
938 struct ast_conference *cnf = NULL;
939 struct ast_conf_user *usr = NULL;
942 char *myline, *ret = NULL;
944 if (pos == 1) { /* Command */
945 return ast_cli_complete(word, cmds, state);
946 } else if (pos == 2) { /* Conference Number */
947 AST_LIST_LOCK(&confs);
948 AST_LIST_TRAVERSE(&confs, cnf, list) {
949 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
954 ret = ast_strdup(ret); /* dup before releasing the lock */
955 AST_LIST_UNLOCK(&confs);
957 } else if (pos == 3) {
958 /* User Number || Conf Command option*/
959 if (strstr(line, "mute") || strstr(line, "kick")) {
960 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
961 return strdup("all");
963 AST_LIST_LOCK(&confs);
965 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
966 myline = ast_strdupa(line);
967 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
968 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
972 AST_LIST_TRAVERSE(&confs, cnf, list) {
973 if (!strcmp(confno, cnf->confno))
978 /* Search for the user */
979 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
980 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
981 if (!strncasecmp(word, usrno, len) && ++which > state)
985 AST_LIST_UNLOCK(&confs);
986 return usr ? strdup(usrno) : NULL;
987 } else if ( strstr(line, "list") && ( 0 == state ) )
988 return strdup("concise");
994 static const char meetme_usage[] =
995 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
996 " Executes a command for the conference or on a conferee\n";
998 static const char *sla_hold_str(unsigned int hold_access)
1000 const char *hold = "Unknown";
1002 switch (hold_access) {
1006 case SLA_HOLD_PRIVATE:
1015 static int sla_show_trunks(int fd, int argc, char **argv)
1017 const struct sla_trunk *trunk;
1020 "=============================================================\n"
1021 "=== Configured SLA Trunks ===================================\n"
1022 "=============================================================\n"
1024 AST_RWLIST_RDLOCK(&sla_trunks);
1025 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1026 struct sla_station_ref *station_ref;
1027 char ring_timeout[16] = "(none)";
1028 if (trunk->ring_timeout)
1029 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1030 ast_cli(fd, "=== ---------------------------------------------------------\n"
1031 "=== Trunk Name: %s\n"
1032 "=== ==> Device: %s\n"
1033 "=== ==> AutoContext: %s\n"
1034 "=== ==> RingTimeout: %s\n"
1035 "=== ==> BargeAllowed: %s\n"
1036 "=== ==> HoldAccess: %s\n"
1037 "=== ==> Stations ...\n",
1038 trunk->name, trunk->device,
1039 S_OR(trunk->autocontext, "(none)"),
1041 trunk->barge_disabled ? "No" : "Yes",
1042 sla_hold_str(trunk->hold_access));
1043 AST_RWLIST_RDLOCK(&sla_stations);
1044 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1045 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1046 AST_RWLIST_UNLOCK(&sla_stations);
1047 ast_cli(fd, "=== ---------------------------------------------------------\n"
1050 AST_RWLIST_UNLOCK(&sla_trunks);
1051 ast_cli(fd, "=============================================================\n"
1054 return RESULT_SUCCESS;
1057 static const char *trunkstate2str(enum sla_trunk_state state)
1059 #define S(e) case e: return # e;
1061 S(SLA_TRUNK_STATE_IDLE)
1062 S(SLA_TRUNK_STATE_RINGING)
1063 S(SLA_TRUNK_STATE_UP)
1064 S(SLA_TRUNK_STATE_ONHOLD)
1066 return "Uknown State";
1070 static const char sla_show_trunks_usage[] =
1071 "Usage: sla show trunks\n"
1072 " This will list all trunks defined in sla.conf\n";
1074 static int sla_show_stations(int fd, int argc, char **argv)
1076 const struct sla_station *station;
1079 "=============================================================\n"
1080 "=== Configured SLA Stations =================================\n"
1081 "=============================================================\n"
1083 AST_RWLIST_RDLOCK(&sla_stations);
1084 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1085 struct sla_trunk_ref *trunk_ref;
1086 char ring_timeout[16] = "(none)";
1087 char ring_delay[16] = "(none)";
1088 if (station->ring_timeout) {
1089 snprintf(ring_timeout, sizeof(ring_timeout),
1090 "%u", station->ring_timeout);
1092 if (station->ring_delay) {
1093 snprintf(ring_delay, sizeof(ring_delay),
1094 "%u", station->ring_delay);
1096 ast_cli(fd, "=== ---------------------------------------------------------\n"
1097 "=== Station Name: %s\n"
1098 "=== ==> Device: %s\n"
1099 "=== ==> AutoContext: %s\n"
1100 "=== ==> RingTimeout: %s\n"
1101 "=== ==> RingDelay: %s\n"
1102 "=== ==> HoldAccess: %s\n"
1103 "=== ==> Trunks ...\n",
1104 station->name, station->device,
1105 S_OR(station->autocontext, "(none)"),
1106 ring_timeout, ring_delay,
1107 sla_hold_str(station->hold_access));
1108 AST_RWLIST_RDLOCK(&sla_trunks);
1109 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1110 if (trunk_ref->ring_timeout) {
1111 snprintf(ring_timeout, sizeof(ring_timeout),
1112 "%u", trunk_ref->ring_timeout);
1114 strcpy(ring_timeout, "(none)");
1115 if (trunk_ref->ring_delay) {
1116 snprintf(ring_delay, sizeof(ring_delay),
1117 "%u", trunk_ref->ring_delay);
1119 strcpy(ring_delay, "(none)");
1120 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1121 "=== ==> State: %s\n"
1122 "=== ==> RingTimeout: %s\n"
1123 "=== ==> RingDelay: %s\n",
1124 trunk_ref->trunk->name,
1125 trunkstate2str(trunk_ref->state),
1126 ring_timeout, ring_delay);
1128 AST_RWLIST_UNLOCK(&sla_trunks);
1129 ast_cli(fd, "=== ---------------------------------------------------------\n"
1132 AST_RWLIST_UNLOCK(&sla_stations);
1133 ast_cli(fd, "============================================================\n"
1136 return RESULT_SUCCESS;
1139 static const char sla_show_stations_usage[] =
1140 "Usage: sla show stations\n"
1141 " This will list all stations defined in sla.conf\n";
1143 static struct ast_cli_entry cli_meetme[] = {
1144 { { "meetme", NULL, NULL },
1145 meetme_cmd, "Execute a command on a conference or conferee",
1146 meetme_usage, complete_meetmecmd },
1148 { { "sla", "show", "trunks", NULL },
1149 sla_show_trunks, "Show SLA Trunks",
1150 sla_show_trunks_usage, NULL },
1152 { { "sla", "show", "stations", NULL },
1153 sla_show_stations, "Show SLA Stations",
1154 sla_show_stations_usage, NULL },
1157 static void conf_flush(int fd, struct ast_channel *chan)
1161 /* read any frames that may be waiting on the channel
1165 struct ast_frame *f;
1167 /* when no frames are available, this will wait
1168 for 1 millisecond maximum
1170 while (ast_waitfor(chan, 1)) {
1174 else /* channel was hung up or something else happened */
1179 /* flush any data sitting in the pseudo channel */
1181 if (ioctl(fd, ZT_FLUSH, &x))
1182 ast_log(LOG_WARNING, "Error flushing channel\n");
1186 /* Remove the conference from the list and free it.
1187 We assume that this was called while holding conflock. */
1188 static int conf_free(struct ast_conference *conf)
1192 AST_LIST_REMOVE(&confs, conf, list);
1193 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1195 if (conf->recording == MEETME_RECORD_ACTIVE) {
1196 conf->recording = MEETME_RECORD_TERMINATE;
1197 AST_LIST_UNLOCK(&confs);
1200 AST_LIST_LOCK(&confs);
1201 if (conf->recording == MEETME_RECORD_OFF)
1203 AST_LIST_UNLOCK(&confs);
1207 for (x=0;x<AST_FRAME_BITS;x++) {
1208 if (conf->transframe[x])
1209 ast_frfree(conf->transframe[x]);
1210 if (conf->transpath[x])
1211 ast_translator_free_path(conf->transpath[x]);
1213 if (conf->origframe)
1214 ast_frfree(conf->origframe);
1216 ast_hangup(conf->lchan);
1218 ast_hangup(conf->chan);
1227 static void conf_queue_dtmf(const struct ast_conference *conf,
1228 const struct ast_conf_user *sender, struct ast_frame *f)
1230 struct ast_conf_user *user;
1232 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1235 if (ast_write(user->chan, f) < 0)
1236 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1240 static void sla_queue_event_full(enum sla_event_type type,
1241 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1243 struct sla_event *event;
1245 if (!(event = ast_calloc(1, sizeof(*event))))
1249 event->trunk_ref = trunk_ref;
1250 event->station = station;
1253 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1257 ast_mutex_lock(&sla.lock);
1258 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1259 ast_cond_signal(&sla.cond);
1260 ast_mutex_unlock(&sla.lock);
1263 static void sla_queue_event_nolock(enum sla_event_type type)
1265 sla_queue_event_full(type, NULL, NULL, 0);
1268 static void sla_queue_event(enum sla_event_type type)
1270 sla_queue_event_full(type, NULL, NULL, 1);
1273 /*! \brief Queue a SLA event from the conference */
1274 static void sla_queue_event_conf(enum sla_event_type type, const struct ast_channel *chan,
1275 struct ast_conference *conf)
1277 struct sla_station *station;
1278 struct sla_trunk_ref *trunk_ref = NULL;
1281 trunk_name = ast_strdupa(conf->confno);
1282 strsep(&trunk_name, "_");
1283 if (ast_strlen_zero(trunk_name)) {
1284 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1288 AST_RWLIST_RDLOCK(&sla_stations);
1289 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1290 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1291 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1297 AST_RWLIST_UNLOCK(&sla_stations);
1300 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1304 sla_queue_event_full(type, trunk_ref, station, 1);
1307 /* Decrement reference counts, as incremented by find_conf() */
1308 static int dispose_conf(struct ast_conference *conf)
1312 AST_LIST_LOCK(&confs);
1313 if (ast_atomic_dec_and_test(&conf->refcount)) {
1317 AST_LIST_UNLOCK(&confs);
1323 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1325 struct ast_conf_user *user = NULL;
1326 struct ast_conf_user *usr = NULL;
1328 struct zt_confinfo ztc, ztc_empty;
1329 struct ast_frame *f;
1330 struct ast_channel *c;
1331 struct ast_frame fr;
1339 int musiconhold = 0;
1342 int currentmarked = 0;
1345 int menu_active = 0;
1346 int using_pseudo = 0;
1351 struct ast_dsp *dsp=NULL;
1352 struct ast_app *app;
1353 const char *agifile;
1354 const char *agifiledefault = "conf-background.agi";
1355 char meetmesecs[30] = "";
1356 char exitcontext[AST_MAX_CONTEXT] = "";
1357 char recordingtmp[AST_MAX_EXTENSION] = "";
1358 char members[10] = "";
1359 int dtmf, opt_waitmarked_timeout = 0;
1362 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1363 char *buf = __buf + AST_FRIENDLY_OFFSET;
1365 if (!(user = ast_calloc(1, sizeof(*user))))
1368 /* Possible timeout waiting for marked user */
1369 if ((confflags & CONFFLAG_WAITMARKED) &&
1370 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1371 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1372 (opt_waitmarked_timeout > 0)) {
1373 timeout = time(NULL) + opt_waitmarked_timeout;
1376 if (confflags & CONFFLAG_RECORDCONF) {
1377 if (!conf->recordingfilename) {
1378 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1379 if (!conf->recordingfilename) {
1380 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1381 conf->recordingfilename = ast_strdupa(recordingtmp);
1383 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1384 if (!conf->recordingformat) {
1385 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1386 conf->recordingformat = ast_strdupa(recordingtmp);
1388 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1389 conf->confno, conf->recordingfilename, conf->recordingformat);
1393 if ((conf->recording == MEETME_RECORD_OFF) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1394 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1395 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1397 ztc.confno = conf->zapconf;
1398 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1399 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1400 ast_log(LOG_WARNING, "Error starting listen channel\n");
1401 ast_hangup(conf->lchan);
1404 pthread_attr_init(&conf->attr);
1405 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1406 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1407 pthread_attr_destroy(&conf->attr);
1411 time(&user->jointime);
1413 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1414 /* Sorry, but this conference is locked! */
1415 if (!ast_streamfile(chan, "conf-locked", chan->language))
1416 ast_waitstream(chan, "");
1420 if (confflags & CONFFLAG_MARKEDUSER)
1421 conf->markedusers++;
1423 ast_mutex_lock(&conf->playlock);
1425 if (AST_LIST_EMPTY(&conf->userlist))
1428 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1430 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1433 user->userflags = confflags;
1434 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1438 snprintf(members, sizeof(members), "%d", conf->users);
1439 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1441 /* This device changed state now - if this is the first user */
1442 if (conf->users == 1)
1443 ast_device_state_changed("meetme:%s", conf->confno);
1445 ast_mutex_unlock(&conf->playlock);
1447 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1448 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1449 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1450 else if (!ast_strlen_zero(chan->macrocontext))
1451 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1453 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1456 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1457 snprintf(user->namerecloc, sizeof(user->namerecloc),
1458 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1459 conf->confno, user->user_no);
1460 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1461 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1463 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1468 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1469 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1470 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1471 ast_waitstream(chan, "");
1472 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1473 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1474 ast_waitstream(chan, "");
1477 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1478 int keepplaying = 1;
1480 if (conf->users == 2) {
1481 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1482 res = ast_waitstream(chan, AST_DIGIT_ANY);
1483 ast_stopstream(chan);
1490 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1491 res = ast_waitstream(chan, AST_DIGIT_ANY);
1492 ast_stopstream(chan);
1499 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1505 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1506 res = ast_waitstream(chan, AST_DIGIT_ANY);
1507 ast_stopstream(chan);
1516 ast_indicate(chan, -1);
1518 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1519 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1523 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1524 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1528 retryzap = strcasecmp(chan->tech->type, "Zap");
1529 user->zapchannel = !retryzap;
1532 origfd = chan->fds[0];
1534 fd = open("/dev/zap/pseudo", O_RDWR);
1536 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1540 /* Make non-blocking */
1541 flags = fcntl(fd, F_GETFL);
1543 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1547 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1548 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1552 /* Setup buffering information */
1553 memset(&bi, 0, sizeof(bi));
1554 bi.bufsize = CONF_SIZE/2;
1555 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1556 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1557 bi.numbufs = audio_buffers;
1558 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1559 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1564 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1565 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1571 /* XXX Make sure we're not running on a pseudo channel XXX */
1575 memset(&ztc, 0, sizeof(ztc));
1576 memset(&ztc_empty, 0, sizeof(ztc_empty));
1577 /* Check to see if we're in a conference... */
1579 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1580 ast_log(LOG_WARNING, "Error getting conference\n");
1585 /* Whoa, already in a conference... Retry... */
1588 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1593 memset(&ztc, 0, sizeof(ztc));
1594 /* Add us to the conference */
1596 ztc.confno = conf->zapconf;
1598 ast_mutex_lock(&conf->playlock);
1600 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1601 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1602 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1603 ast_waitstream(conf->chan, "");
1604 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1605 ast_waitstream(conf->chan, "");
1609 if (confflags & CONFFLAG_MONITOR)
1610 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1611 else if (confflags & CONFFLAG_TALKER)
1612 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1614 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1616 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1617 ast_log(LOG_WARNING, "Error setting conference\n");
1619 ast_mutex_unlock(&conf->playlock);
1623 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1626 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1631 chan->name, chan->uniqueid, conf->confno, user->user_no);
1635 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1637 if (!(confflags & CONFFLAG_QUIET))
1638 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1639 conf_play(chan, conf, ENTER);
1642 ast_mutex_unlock(&conf->playlock);
1644 conf_flush(fd, chan);
1646 if (confflags & CONFFLAG_AGI) {
1647 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1648 or use default filename of conf-background.agi */
1650 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1652 agifile = agifiledefault;
1654 if (user->zapchannel) {
1655 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1657 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1659 /* Find a pointer to the agi app and execute the script */
1660 app = pbx_findapp("agi");
1662 char *s = ast_strdupa(agifile);
1663 ret = pbx_exec(chan, app, s);
1665 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1668 if (user->zapchannel) {
1669 /* Remove CONFMUTE mode on Zap channel */
1671 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1674 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1675 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1677 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1679 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1680 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1684 int menu_was_active = 0;
1689 if (timeout && time(NULL) >= timeout)
1692 /* if we have just exited from the menu, and the user had a channel-driver
1693 volume adjustment, restore it
1695 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1696 set_talk_volume(user, user->listen.desired);
1698 menu_was_active = menu_active;
1700 currentmarked = conf->markedusers;
1701 if (!(confflags & CONFFLAG_QUIET) &&
1702 (confflags & CONFFLAG_MARKEDUSER) &&
1703 (confflags & CONFFLAG_WAITMARKED) &&
1705 if (currentmarked == 1 && conf->users > 1) {
1706 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1707 if (conf->users - 1 == 1) {
1708 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1709 ast_waitstream(chan, "");
1711 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1712 ast_waitstream(chan, "");
1715 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1716 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1717 ast_waitstream(chan, "");
1720 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1723 /* Update the struct with the actual confflags */
1724 user->userflags = confflags;
1726 if (confflags & CONFFLAG_WAITMARKED) {
1727 if(currentmarked == 0) {
1728 if (lastmarked != 0) {
1729 if (!(confflags & CONFFLAG_QUIET))
1730 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1731 ast_waitstream(chan, "");
1732 if (confflags & CONFFLAG_MARKEDEXIT) {
1733 if (confflags & CONFFLAG_KICK_CONTINUE)
1737 ztc.confmode = ZT_CONF_CONF;
1738 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1739 ast_log(LOG_WARNING, "Error setting conference\n");
1745 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1746 ast_moh_start(chan, NULL, NULL);
1749 ztc.confmode = ZT_CONF_CONF;
1750 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1751 ast_log(LOG_WARNING, "Error setting conference\n");
1756 } else if(currentmarked >= 1 && lastmarked == 0) {
1757 /* Marked user entered, so cancel timeout */
1759 if (confflags & CONFFLAG_MONITOR)
1760 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1761 else if (confflags & CONFFLAG_TALKER)
1762 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1764 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1765 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1766 ast_log(LOG_WARNING, "Error setting conference\n");
1770 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1774 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1775 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1776 ast_waitstream(chan, "");
1777 conf_play(chan, conf, ENTER);
1782 /* trying to add moh for single person conf */
1783 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1784 if (conf->users == 1) {
1785 if (musiconhold == 0) {
1786 ast_moh_start(chan, NULL, NULL);
1797 /* Leave if the last marked user left */
1798 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1799 if (confflags & CONFFLAG_KICK_CONTINUE)
1806 /* Check if my modes have changed */
1808 /* If I should be muted but am still talker, mute me */
1809 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1810 ztc.confmode ^= ZT_CONF_TALKER;
1811 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1812 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1817 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1823 chan->name, chan->uniqueid, conf->confno, user->user_no);
1826 /* If I should be un-muted but am not talker, un-mute me */
1827 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1828 ztc.confmode |= ZT_CONF_TALKER;
1829 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1830 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1835 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1841 chan->name, chan->uniqueid, conf->confno, user->user_no);
1844 /* If I have been kicked, exit the conference */
1845 if (user->adminflags & ADMINFLAG_KICKME) {
1846 //You have been kicked.
1847 if (!(confflags & CONFFLAG_QUIET) &&
1848 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1849 ast_waitstream(chan, "");
1856 if (c->fds[0] != origfd) {
1858 /* Kill old pseudo */
1863 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1864 retryzap = strcasecmp(c->tech->type, "Zap");
1865 user->zapchannel = !retryzap;
1868 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1869 f = ast_read_noaudio(c);
1874 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1875 if (user->talk.actual)
1876 ast_frame_adjust_volume(f, user->talk.actual);
1878 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1881 if (user->talking == -1)
1884 res = ast_dsp_silence(dsp, f, &totalsilence);
1885 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1887 if (confflags & CONFFLAG_MONITORTALKER)
1888 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1894 chan->name, chan->uniqueid, conf->confno, user->user_no);
1896 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1898 if (confflags & CONFFLAG_MONITORTALKER)
1899 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1905 chan->name, chan->uniqueid, conf->confno, user->user_no);
1909 /* Absolutely do _not_ use careful_write here...
1910 it is important that we read data from the channel
1911 as fast as it arrives, and feed it into the conference.
1912 The buffering in the pseudo channel will take care of any
1913 timing differences, unless they are so drastic as to lose
1914 audio frames (in which case carefully writing would only
1915 have delayed the audio even further).
1917 /* As it turns out, we do want to use careful write. We just
1918 don't want to block, but we do want to at least *try*
1919 to write out all the samples.
1921 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1922 careful_write(fd, f->data, f->datalen, 0);
1924 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1927 tmp[0] = f->subclass;
1929 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1931 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1935 } else if (option_debug > 1)
1936 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1937 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1941 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1942 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1943 ast_log(LOG_WARNING, "Error setting conference\n");
1949 /* if we are entering the menu, and the user has a channel-driver
1950 volume adjustment, clear it
1952 if (!menu_active && user->talk.desired && !user->talk.actual)
1953 set_talk_volume(user, 0);
1958 if ((confflags & CONFFLAG_ADMIN)) {
1962 /* Record this sound! */
1963 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1964 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1965 ast_stopstream(chan);
1972 case '1': /* Un/Mute */
1975 /* for admin, change both admin and use flags */
1976 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1977 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1979 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1981 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1982 if (!ast_streamfile(chan, "conf-muted", chan->language))
1983 ast_waitstream(chan, "");
1985 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1986 ast_waitstream(chan, "");
1989 case '2': /* Un/Lock the Conference */
1993 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1994 ast_waitstream(chan, "");
1997 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1998 ast_waitstream(chan, "");
2001 case '3': /* Eject last user */
2003 usr = AST_LIST_LAST(&conf->userlist);
2004 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2005 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2006 ast_waitstream(chan, "");
2008 usr->adminflags |= ADMINFLAG_KICKME;
2009 ast_stopstream(chan);
2012 tweak_listen_volume(user, VOL_DOWN);
2015 tweak_listen_volume(user, VOL_UP);
2018 tweak_talk_volume(user, VOL_DOWN);
2024 tweak_talk_volume(user, VOL_UP);
2028 /* Play an error message! */
2029 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2030 ast_waitstream(chan, "");
2038 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2039 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2040 ast_stopstream(chan);
2047 case '1': /* Un/Mute */
2050 /* user can only toggle the self-muted state */
2051 user->adminflags ^= ADMINFLAG_SELFMUTED;
2053 /* they can't override the admin mute state */
2054 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2055 if (!ast_streamfile(chan, "conf-muted", chan->language))
2056 ast_waitstream(chan, "");
2058 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2059 ast_waitstream(chan, "");
2063 tweak_listen_volume(user, VOL_DOWN);
2066 tweak_listen_volume(user, VOL_UP);
2069 tweak_talk_volume(user, VOL_DOWN);
2075 tweak_talk_volume(user, VOL_UP);
2079 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2080 ast_waitstream(chan, "");
2086 ast_moh_start(chan, NULL, NULL);
2088 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2089 ast_log(LOG_WARNING, "Error setting conference\n");
2095 conf_flush(fd, chan);
2096 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2097 && confflags & CONFFLAG_PASS_DTMF) {
2098 conf_queue_dtmf(conf, user, f);
2099 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2100 switch (f->subclass) {
2101 case AST_CONTROL_HOLD:
2102 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2104 case AST_CONTROL_UNHOLD:
2105 sla_queue_event_conf(SLA_EVENT_UNHOLD, chan, conf);
2109 } else if (option_debug) {
2111 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2112 chan->name, f->frametype, f->subclass);
2115 } else if (outfd > -1) {
2116 res = read(outfd, buf, CONF_SIZE);
2118 memset(&fr, 0, sizeof(fr));
2119 fr.frametype = AST_FRAME_VOICE;
2120 fr.subclass = AST_FORMAT_SLINEAR;
2124 fr.offset = AST_FRIENDLY_OFFSET;
2125 if (!user->listen.actual &&
2126 ((confflags & CONFFLAG_MONITOR) ||
2127 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2128 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
2131 for (index=0;index<AST_FRAME_BITS;index++)
2132 if (chan->rawwriteformat & (1 << index))
2134 if (index >= AST_FRAME_BITS)
2135 goto bailoutandtrynormal;
2136 ast_mutex_lock(&conf->listenlock);
2137 if (!conf->transframe[index]) {
2138 if (conf->origframe) {
2139 if (!conf->transpath[index])
2140 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2141 if (conf->transpath[index]) {
2142 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2143 if (!conf->transframe[index])
2144 conf->transframe[index] = &ast_null_frame;
2148 if (conf->transframe[index]) {
2149 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2150 if (ast_write(chan, conf->transframe[index]))
2151 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2154 ast_mutex_unlock(&conf->listenlock);
2155 goto bailoutandtrynormal;
2157 ast_mutex_unlock(&conf->listenlock);
2159 bailoutandtrynormal:
2160 if (user->listen.actual)
2161 ast_frame_adjust_volume(&fr, user->listen.actual);
2162 if (ast_write(chan, &fr) < 0) {
2163 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2167 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2169 lastmarked = currentmarked;
2179 /* Take out of conference */
2183 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2184 ast_log(LOG_WARNING, "Error setting conference\n");
2188 reset_volumes(user);
2190 AST_LIST_LOCK(&confs);
2191 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2192 conf_play(chan, conf, LEAVE);
2194 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2195 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2196 if ((conf->chan) && (conf->users > 1)) {
2197 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2198 ast_waitstream(conf->chan, "");
2199 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2200 ast_waitstream(conf->chan, "");
2202 ast_filedelete(user->namerecloc, NULL);
2205 AST_LIST_UNLOCK(&confs);
2208 AST_LIST_LOCK(&confs);
2213 if (user->user_no) { /* Only cleanup users who really joined! */
2215 hr = (now - user->jointime) / 3600;
2216 min = ((now - user->jointime) % 3600) / 60;
2217 sec = (now - user->jointime) % 60;
2220 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2225 "CallerIDNum: %s\r\n"
2226 "CallerIDName: %s\r\n"
2227 "Duration: %ld\r\n",
2228 chan->name, chan->uniqueid, conf->confno,
2230 S_OR(user->chan->cid.cid_num, "<unknown>"),
2231 S_OR(user->chan->cid.cid_name, "<unknown>"),
2232 (long)(now - user->jointime));
2237 snprintf(members, sizeof(members), "%d", conf->users);
2238 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2239 if (confflags & CONFFLAG_MARKEDUSER)
2240 conf->markedusers--;
2241 /* Remove ourselves from the list */
2242 AST_LIST_REMOVE(&conf->userlist, user, list);
2244 /* Change any states */
2246 ast_device_state_changed("meetme:%s", conf->confno);
2248 /* Return the number of seconds the user was in the conf */
2249 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2250 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2253 AST_LIST_UNLOCK(&confs);
2258 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2259 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2261 struct ast_variable *var;
2262 struct ast_conference *cnf;
2264 /* Check first in the conference list */
2265 AST_LIST_LOCK(&confs);
2266 AST_LIST_TRAVERSE(&confs, cnf, list) {
2267 if (!strcmp(confno, cnf->confno))
2271 cnf->refcount += refcount;
2273 AST_LIST_UNLOCK(&confs);
2276 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2278 var = ast_load_realtime("meetme", "confno", confno, NULL);
2284 if (!strcasecmp(var->name, "pin")) {
2285 pin = ast_strdupa(var->value);
2286 } else if (!strcasecmp(var->name, "adminpin")) {
2287 pinadmin = ast_strdupa(var->value);
2291 ast_variables_destroy(var);
2293 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2297 if (confflags && !cnf->chan &&
2298 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2299 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2300 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2301 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2304 if (confflags && !cnf->chan &&
2305 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2306 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2307 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2315 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2316 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2318 struct ast_config *cfg;
2319 struct ast_variable *var;
2320 struct ast_conference *cnf;
2322 AST_DECLARE_APP_ARGS(args,
2323 AST_APP_ARG(confno);
2325 AST_APP_ARG(pinadmin);
2328 /* Check first in the conference list */
2329 AST_LIST_LOCK(&confs);
2330 AST_LIST_TRAVERSE(&confs, cnf, list) {
2331 if (!strcmp(confno, cnf->confno))
2335 cnf->refcount += refcount;
2337 AST_LIST_UNLOCK(&confs);
2341 /* No need to parse meetme.conf */
2343 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2345 if (dynamic_pin[0] == 'q') {
2346 /* Query the user to enter a PIN */
2347 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2350 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2352 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2355 /* Check the config */
2356 cfg = ast_config_load(CONFIG_FILE_NAME);
2358 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2361 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2362 if (strcasecmp(var->name, "conf"))
2365 if (!(parse = ast_strdupa(var->value)))
2368 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2369 if (!strcasecmp(args.confno, confno)) {
2370 /* Bingo it's a valid conference */
2371 cnf = build_conf(args.confno,
2373 S_OR(args.pinadmin, ""),
2374 make, dynamic, refcount);
2379 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2381 ast_config_destroy(cfg);
2383 } else if (dynamic_pin) {
2384 /* Correct for the user selecting 'D' instead of 'd' to have
2385 someone join into a conference that has already been created
2387 if (dynamic_pin[0] == 'q')
2388 dynamic_pin[0] = '\0';
2392 if (confflags && !cnf->chan &&
2393 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2394 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2395 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2396 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2399 if (confflags && !cnf->chan &&
2400 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2401 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2402 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2409 /*! \brief The MeetmeCount application */
2410 static int count_exec(struct ast_channel *chan, void *data)
2412 struct ast_module_user *u;
2414 struct ast_conference *conf;
2418 AST_DECLARE_APP_ARGS(args,
2419 AST_APP_ARG(confno);
2420 AST_APP_ARG(varname);
2423 if (ast_strlen_zero(data)) {
2424 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2428 u = ast_module_user_add(chan);
2430 if (!(localdata = ast_strdupa(data))) {
2431 ast_module_user_remove(u);
2435 AST_STANDARD_APP_ARGS(args, localdata);
2437 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2440 count = conf->users;
2446 if (!ast_strlen_zero(args.varname)){
2447 /* have var so load it and exit */
2448 snprintf(val, sizeof(val), "%d",count);
2449 pbx_builtin_setvar_helper(chan, args.varname, val);
2451 if (chan->_state != AST_STATE_UP)
2453 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2455 ast_module_user_remove(u);
2460 /*! \brief The meetme() application */
2461 static int conf_exec(struct ast_channel *chan, void *data)
2464 struct ast_module_user *u;
2465 char confno[MAX_CONFNUM] = "";
2468 struct ast_conference *cnf = NULL;
2469 struct ast_flags confflags = {0};
2471 int empty = 0, empty_no_pin = 0;
2472 int always_prompt = 0;
2473 char *notdata, *info, the_pin[MAX_PIN] = "";
2474 AST_DECLARE_APP_ARGS(args,
2475 AST_APP_ARG(confno);
2476 AST_APP_ARG(options);
2479 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2481 u = ast_module_user_add(chan);
2483 if (ast_strlen_zero(data)) {
2490 if (chan->_state != AST_STATE_UP)
2493 info = ast_strdupa(notdata);
2495 AST_STANDARD_APP_ARGS(args, info);
2498 ast_copy_string(confno, args.confno, sizeof(confno));
2499 if (ast_strlen_zero(confno)) {
2505 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2508 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2509 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2510 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2511 strcpy(the_pin, "q");
2513 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2514 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2515 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2522 int i, map[1024] = { 0, };
2523 struct ast_config *cfg;
2524 struct ast_variable *var;
2527 AST_LIST_LOCK(&confs);
2528 AST_LIST_TRAVERSE(&confs, cnf, list) {
2529 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2530 /* Disqualify in use conference */
2531 if (confno_int >= 0 && confno_int < 1024)
2535 AST_LIST_UNLOCK(&confs);
2537 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2538 if ((empty_no_pin) || (!dynamic)) {
2539 cfg = ast_config_load(CONFIG_FILE_NAME);
2541 var = ast_variable_browse(cfg, "rooms");
2543 if (!strcasecmp(var->name, "conf")) {
2544 char *stringp = ast_strdupa(var->value);
2546 char *confno_tmp = strsep(&stringp, "|,");
2548 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2549 if ((confno_int >= 0) && (confno_int < 1024)) {
2550 if (stringp && empty_no_pin) {
2556 /* For static: run through the list and see if this conference is empty */
2557 AST_LIST_LOCK(&confs);
2558 AST_LIST_TRAVERSE(&confs, cnf, list) {
2559 if (!strcmp(confno_tmp, cnf->confno)) {
2560 /* The conference exists, therefore it's not empty */
2565 AST_LIST_UNLOCK(&confs);
2567 /* At this point, we have a confno_tmp (static conference) that is empty */
2568 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2569 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2570 * Case 2: empty_no_pin and pin is blank (but not NULL)
2571 * Case 3: not empty_no_pin
2573 ast_copy_string(confno, confno_tmp, sizeof(confno));
2575 /* XXX the map is not complete (but we do have a confno) */
2583 ast_config_destroy(cfg);
2587 /* Select first conference number not in use */
2588 if (ast_strlen_zero(confno) && dynamic) {
2589 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2591 snprintf(confno, sizeof(confno), "%d", i);
2598 if (ast_strlen_zero(confno)) {
2599 res = ast_streamfile(chan, "conf-noempty", chan->language);
2601 ast_waitstream(chan, "");
2603 if (sscanf(confno, "%d", &confno_int) == 1) {
2604 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2605 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2607 ast_waitstream(chan, "");
2608 res = ast_say_digits(chan, confno_int, "", chan->language);
2612 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2617 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2618 /* Prompt user for conference number */
2619 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2621 /* Don't try to validate when we catch an error */
2627 if (!ast_strlen_zero(confno)) {
2628 /* Check the validity of the conference */
2629 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2630 sizeof(the_pin), 1, &confflags);
2632 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2633 the_pin, sizeof(the_pin), 1, &confflags);
2637 res = ast_streamfile(chan, "conf-invalid", chan->language);
2639 ast_waitstream(chan, "");
2644 if ((!ast_strlen_zero(cnf->pin) &&
2645 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2646 (!ast_strlen_zero(cnf->pinadmin) &&
2647 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2648 char pin[MAX_PIN] = "";
2651 /* Allow the pin to be retried up to 3 times */
2652 for (j = 0; j < 3; j++) {
2653 if (*the_pin && (always_prompt == 0)) {
2654 ast_copy_string(pin, the_pin, sizeof(pin));
2657 /* Prompt user for pin if pin is required */
2658 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2661 if (!strcasecmp(pin, cnf->pin) ||
2662 (!ast_strlen_zero(cnf->pinadmin) &&
2663 !strcasecmp(pin, cnf->pinadmin))) {
2666 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2667 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2668 /* Run the conference */
2669 res = conf_run(chan, cnf, confflags.flags, optargs);
2673 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2674 res = ast_waitstream(chan, AST_DIGIT_ANY);
2675 ast_stopstream(chan);
2678 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2690 /* failed when getting the pin */
2693 /* see if we need to get rid of the conference */
2697 /* Don't retry pin with a static pin */
2698 if (*the_pin && (always_prompt==0)) {
2703 /* No pin required */
2706 /* Run the conference */
2707 res = conf_run(chan, cnf, confflags.flags, optargs);
2713 } while (allowretry);
2718 ast_module_user_remove(u);
2723 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2725 struct ast_conf_user *user = NULL;
2728 sscanf(callerident, "%i", &cid);
2729 if (conf && callerident) {
2730 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2731 if (cid == user->user_no)
2738 /*! \brief The MeetMeadmin application */
2739 /* MeetMeAdmin(confno, command, caller) */
2740 static int admin_exec(struct ast_channel *chan, void *data) {
2742 struct ast_conference *cnf;
2743 struct ast_conf_user *user = NULL;
2744 struct ast_module_user *u;
2745 AST_DECLARE_APP_ARGS(args,
2746 AST_APP_ARG(confno);
2747 AST_APP_ARG(command);
2751 if (ast_strlen_zero(data)) {
2752 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2756 u = ast_module_user_add(chan);
2758 AST_LIST_LOCK(&confs);
2760 params = ast_strdupa(data);
2761 AST_STANDARD_APP_ARGS(args, params);
2763 if (!args.command) {
2764 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2765 AST_LIST_UNLOCK(&confs);
2766 ast_module_user_remove(u);
2769 AST_LIST_TRAVERSE(&confs, cnf, list) {
2770 if (!strcmp(cnf->confno, args.confno))
2775 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2776 AST_LIST_UNLOCK(&confs);
2777 ast_module_user_remove(u);
2781 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2784 user = find_user(cnf, args.user);
2786 switch (*args.command) {
2787 case 76: /* L: Lock */
2790 case 108: /* l: Unlock */
2793 case 75: /* K: kick all users */
2794 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2795 user->adminflags |= ADMINFLAG_KICKME;
2797 case 101: /* e: Eject last user*/
2798 user = AST_LIST_LAST(&cnf->userlist);
2799 if (!(user->userflags & CONFFLAG_ADMIN))
2800 user->adminflags |= ADMINFLAG_KICKME;
2802 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2804 case 77: /* M: Mute */
2806 user->adminflags |= ADMINFLAG_MUTED;
2808 ast_log(LOG_NOTICE, "Specified User not found!\n");
2810 case 78: /* N: Mute all (non-admin) users */
2811 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2812 if (!(user->userflags & CONFFLAG_ADMIN))
2813 user->adminflags |= ADMINFLAG_MUTED;
2816 case 109: /* m: Unmute */
2818 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2820 ast_log(LOG_NOTICE, "Specified User not found!\n");
2822 case 110: /* n: Unmute all users */
2823 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2824 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2826 case 107: /* k: Kick user */
2828 user->adminflags |= ADMINFLAG_KICKME;
2830 ast_log(LOG_NOTICE, "Specified User not found!\n");
2832 case 118: /* v: Lower all users listen volume */
2833 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2834 tweak_listen_volume(user, VOL_DOWN);
2836 case 86: /* V: Raise all users listen volume */
2837 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2838 tweak_listen_volume(user, VOL_UP);
2840 case 115: /* s: Lower all users speaking volume */
2841 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2842 tweak_talk_volume(user, VOL_DOWN);
2844 case 83: /* S: Raise all users speaking volume */
2845 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2846 tweak_talk_volume(user, VOL_UP);
2848 case 82: /* R: Reset all volume levels */
2849 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2850 reset_volumes(user);
2852 case 114: /* r: Reset user's volume level */
2854 reset_volumes(user);
2856 ast_log(LOG_NOTICE, "Specified User not found!\n");
2858 case 85: /* U: Raise user's listen volume */
2860 tweak_listen_volume(user, VOL_UP);
2862 ast_log(LOG_NOTICE, "Specified User not found!\n");
2864 case 117: /* u: Lower user's listen volume */
2866 tweak_listen_volume(user, VOL_DOWN);
2868 ast_log(LOG_NOTICE, "Specified User not found!\n");
2870 case 84: /* T: Raise user's talk volume */
2872 tweak_talk_volume(user, VOL_UP);
2874 ast_log(LOG_NOTICE, "Specified User not found!\n");
2876 case 116: /* t: Lower user's talk volume */
2878 tweak_talk_volume(user, VOL_DOWN);
2880 ast_log(LOG_NOTICE, "Specified User not found!\n");
2884 AST_LIST_UNLOCK(&confs);
2888 ast_module_user_remove(u);
2893 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2895 struct ast_conference *conf;
2896 struct ast_conf_user *user;
2897 const char *confid = astman_get_header(m, "Meetme");
2898 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2901 if (ast_strlen_zero(confid)) {
2902 astman_send_error(s, m, "Meetme conference not specified");
2906 if (ast_strlen_zero(userid)) {
2907 astman_send_error(s, m, "Meetme user number not specified");
2911 userno = strtoul(userid, &userid, 10);
2914 astman_send_error(s, m, "Invalid user number");
2918 /* Look in the conference list */
2919 AST_LIST_LOCK(&confs);
2920 AST_LIST_TRAVERSE(&confs, conf, list) {
2921 if (!strcmp(confid, conf->confno))
2926 AST_LIST_UNLOCK(&confs);
2927 astman_send_error(s, m, "Meetme conference does not exist");
2931 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2932 if (user->user_no == userno)
2936 AST_LIST_UNLOCK(&confs);
2937 astman_send_error(s, m, "User number not found");
2942 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2944 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2946 AST_LIST_UNLOCK(&confs);
2948 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);
2950 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2954 static int action_meetmemute(struct mansession *s, const struct message *m)
2956 return meetmemute(s, m, 1);
2959 static int action_meetmeunmute(struct mansession *s, const struct message *m)
2961 return meetmemute(s, m, 0);
2964 static void *recordthread(void *args)
2966 struct ast_conference *cnf = args;
2967 struct ast_frame *f=NULL;
2969 struct ast_filestream *s=NULL;
2972 const char *oldrecordingfilename = NULL;
2974 if (!cnf || !cnf->lchan) {
2978 ast_stopstream(cnf->lchan);
2979 flags = O_CREAT|O_TRUNC|O_WRONLY;
2982 cnf->recording = MEETME_RECORD_ACTIVE;
2983 while (ast_waitfor(cnf->lchan, -1) > -1) {
2984 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2985 AST_LIST_LOCK(&confs);
2986 AST_LIST_UNLOCK(&confs);
2989 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2990 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
2991 oldrecordingfilename = cnf->recordingfilename;
2994 f = ast_read(cnf->lchan);
2999 if (f->frametype == AST_FRAME_VOICE) {
3000 ast_mutex_lock(&cnf->listenlock);
3001 for (x=0;x<AST_FRAME_BITS;x++) {
3002 /* Free any translations that have occured */
3003 if (cnf->transframe[x]) {
3004 ast_frfree(cnf->transframe[x]);
3005 cnf->transframe[x] = NULL;
3009 ast_frfree(cnf->origframe);
3011 ast_mutex_unlock(&cnf->listenlock);
3013 res = ast_writestream(s, f);
3021 cnf->recording = MEETME_RECORD_OFF;
3028 /*! \brief Callback for devicestate providers */
3029 static enum ast_device_state meetmestate(const char *data)
3031 struct ast_conference *conf;
3033 /* Find conference */
3034 AST_LIST_LOCK(&confs);
3035 AST_LIST_TRAVERSE(&confs, conf, list) {
3036 if (!strcmp(data, conf->confno))
3039 AST_LIST_UNLOCK(&confs);
3041 return AST_DEVICE_INVALID;
3046 return AST_DEVICE_NOT_INUSE;
3048 return AST_DEVICE_INUSE;
3051 static void load_config_meetme(void)
3053 struct ast_config *cfg;
3056 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3058 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
3061 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
3062 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
3063 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
3064 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3065 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
3066 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
3067 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
3068 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3070 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3071 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3074 ast_config_destroy(cfg);
3077 /*! \brief Find an SLA trunk by name
3078 * \note This must be called with the sla_trunks container locked
3080 static struct sla_trunk *sla_find_trunk(const char *name)
3082 struct sla_trunk *trunk = NULL;
3084 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
3085 if (!strcasecmp(trunk->name, name))
3092 /*! \brief Find an SLA station by name
3093 * \note This must be called with the sla_stations container locked
3095 static struct sla_station *sla_find_station(const char *name)
3097 struct sla_station *station = NULL;
3099 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
3100 if (!strcasecmp(station->name, name))
3107 static int sla_check_station_hold_access(const struct sla_trunk *trunk)
3109 struct sla_station_ref *station_ref;
3110 struct sla_trunk_ref *trunk_ref;
3112 /* For each station that has this call on hold, check for private hold. */
3113 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
3114 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
3115 if (trunk_ref->trunk != trunk)
3117 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD && trunk_ref->chan &&
3118 station_ref->station->hold_access == SLA_HOLD_PRIVATE)