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"
68 #include "asterisk/causes.h"
73 #define CONFIG_FILE_NAME "meetme.conf"
74 #define SLA_CONFIG_FILE "sla.conf"
76 /*! each buffer is 20ms, so this is 640ms total */
77 #define DEFAULT_AUDIO_BUFFERS 32
80 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
81 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
82 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
85 #define MEETME_DELAYDETECTTALK 300
86 #define MEETME_DELAYDETECTENDTALK 1000
88 #define AST_FRAME_BITS 32
100 enum recording_state {
102 MEETME_RECORD_STARTED,
103 MEETME_RECORD_ACTIVE,
104 MEETME_RECORD_TERMINATE
107 #define CONF_SIZE 320
110 /*! user has admin access on the conference */
111 CONFFLAG_ADMIN = (1 << 0),
112 /*! If set the user can only receive audio from the conference */
113 CONFFLAG_MONITOR = (1 << 1),
114 /*! If set asterisk will exit conference when key defined in p() option is pressed */
115 CONFFLAG_KEYEXIT = (1 << 2),
116 /*! If set asterisk will provide a menu to the user when '*' is pressed */
117 CONFFLAG_STARMENU = (1 << 3),
118 /*! If set the use can only send audio to the conference */
119 CONFFLAG_TALKER = (1 << 4),
120 /*! If set there will be no enter or leave sounds */
121 CONFFLAG_QUIET = (1 << 5),
122 /*! If set, when user joins the conference, they will be told the number
123 * of users that are already in */
124 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
125 /*! Set to run AGI Script in Background */
126 CONFFLAG_AGI = (1 << 7),
127 /*! Set to have music on hold when user is alone in conference */
128 CONFFLAG_MOH = (1 << 8),
129 /*! If set the MeetMe will return if all marked with this flag left */
130 CONFFLAG_MARKEDEXIT = (1 << 9),
131 /*! If set, the MeetMe will wait until a marked user enters */
132 CONFFLAG_WAITMARKED = (1 << 10),
133 /*! If set, the MeetMe will exit to the specified context */
134 CONFFLAG_EXIT_CONTEXT = (1 << 11),
135 /*! If set, the user will be marked */
136 CONFFLAG_MARKEDUSER = (1 << 12),
137 /*! If set, user will be ask record name on entry of conference */
138 CONFFLAG_INTROUSER = (1 << 13),
139 /*! If set, the MeetMe will be recorded */
140 CONFFLAG_RECORDCONF = (1<< 14),
141 /*! If set, the user will be monitored if the user is talking or not */
142 CONFFLAG_MONITORTALKER = (1 << 15),
143 CONFFLAG_DYNAMIC = (1 << 16),
144 CONFFLAG_DYNAMICPIN = (1 << 17),
145 CONFFLAG_EMPTY = (1 << 18),
146 CONFFLAG_EMPTYNOPIN = (1 << 19),
147 CONFFLAG_ALWAYSPROMPT = (1 << 20),
148 /*! If set, won't speak the extra prompt when the first person
149 * enters the conference */
150 CONFFLAG_NOONLYPERSON = (1 << 22),
151 /*! If set, user will be asked to record name on entry of conference
153 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
154 /*! If set, the user will be initially self-muted */
155 CONFFLAG_STARTMUTED = (1 << 24),
156 /*! Pass DTMF through the conference */
157 CONFFLAG_PASS_DTMF = (1 << 25),
158 CONFFLAG_SLA_STATION = (1 << 26),
159 CONFFLAG_SLA_TRUNK = (1 << 27),
160 /*! If set, the user should continue in the dialplan if kicked out */
161 CONFFLAG_KICK_CONTINUE = (1 << 28)
165 OPT_ARG_WAITMARKED = 0,
166 OPT_ARG_EXITKEYS = 1,
167 OPT_ARG_ARRAY_SIZE = 2,
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('P', CONFFLAG_ALWAYSPROMPT ),
186 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
187 AST_APP_OPTION('q', CONFFLAG_QUIET ),
188 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
189 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
190 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
191 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
192 AST_APP_OPTION('t', CONFFLAG_TALKER ),
193 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
194 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
195 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
196 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
199 static const char *app = "MeetMe";
200 static const char *app2 = "MeetMeCount";
201 static const char *app3 = "MeetMeAdmin";
202 static const char *app4 = "MeetMeChannelAdmin";
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 *synopsis4 = "MeetMe conference Administration (channel specific)";
210 static const char *slastation_synopsis = "Shared Line Appearance Station";
211 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
213 static const char *descrip =
214 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
215 "conference. If the conference number is omitted, the user will be prompted\n"
216 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
217 "is specified, by pressing '#'.\n"
218 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
219 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
220 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
221 "The option string may contain zero or more of the following characters:\n"
222 " 'a' -- set admin mode\n"
223 " 'A' -- set marked mode\n"
224 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
225 " Default: conf-background.agi (Note: This does not work with\n"
226 " non-Zap channels in the same conference)\n"
227 " 'c' -- announce user(s) count on joining a conference\n"
228 " 'C' -- continue in dialplan when kicked out of conference\n"
229 " 'd' -- dynamically add conference\n"
230 " 'D' -- dynamically add conference, prompting for a PIN\n"
231 " 'e' -- select an empty conference\n"
232 " 'E' -- select an empty pinless conference\n"
233 " 'F' -- Pass DTMF through the conference.\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"
244 " -- allow user to exit the conference by pressing '#' (default)\n"
245 " or any of the defined keys. If keys contain '*' this will override\n"
246 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
247 " 'P' -- always prompt for the pin even if it is specified\n"
248 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
249 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
250 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
251 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
253 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
254 " 't' -- set talk only mode. (Talk only, no listening)\n"
255 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
257 " -- wait until the marked user enters the conference\n"
258 " 'x' -- close the conference when last marked user exits\n"
259 " 'X' -- allow user to exit the conference by entering a valid single\n"
260 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
261 " if that variable is not defined.\n"
262 " '1' -- do not play message when first person enters\n";
264 static const char *descrip2 =
265 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
266 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
267 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
268 "the channel, unless priority n+1 exists, in which case priority progress will\n"
270 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
272 static const char *descrip3 =
273 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
274 " 'e' -- Eject last user that joined\n"
275 " 'k' -- Kick one user out of conference\n"
276 " 'K' -- Kick all users out of conference\n"
277 " 'l' -- Unlock conference\n"
278 " 'L' -- Lock conference\n"
279 " 'm' -- Unmute one user\n"
280 " 'M' -- Mute one user\n"
281 " 'n' -- Unmute all users in the conference\n"
282 " 'N' -- Mute all non-admin users in the conference\n"
283 " 'r' -- Reset one user's volume settings\n"
284 " 'R' -- Reset all users volume settings\n"
285 " 's' -- Lower entire conference speaking volume\n"
286 " 'S' -- Raise entire conference speaking volume\n"
287 " 't' -- Lower one user's talk volume\n"
288 " 'T' -- Raise one user's talk volume\n"
289 " 'u' -- Lower one user's listen volume\n"
290 " 'U' -- Raise one user's listen volume\n"
291 " 'v' -- Lower entire conference listening volume\n"
292 " 'V' -- Raise entire conference listening volume\n"
295 static const char *descrip4 =
296 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
297 "channel in any coference.\n"
298 " 'k' -- Kick the specified user out of the conference he is in\n"
299 " 'm' -- Unmute the specified user\n"
300 " 'M' -- Mute the specified user\n"
303 static const char *slastation_desc =
304 " SLAStation(station):\n"
305 "This application should be executed by an SLA station. The argument depends\n"
306 "on how the call was initiated. If the phone was just taken off hook, then\n"
307 "the argument \"station\" should be just the station name. If the call was\n"
308 "initiated by pressing a line key, then the station name should be preceded\n"
309 "by an underscore and the trunk name associated with that line button.\n"
310 "For example: \"station1_line1\"."
311 " On exit, this application will set the variable SLASTATION_STATUS to\n"
312 "one of the following values:\n"
313 " FAILURE | CONGESTION | SUCCESS\n"
316 static const char *slatrunk_desc =
317 " SLATrunk(trunk):\n"
318 "This application should be executed by an SLA trunk on an inbound call.\n"
319 "The channel calling this application should correspond to the SLA trunk\n"
320 "with the name \"trunk\" that is being passed as an argument.\n"
321 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
322 "one of the following values:\n"
323 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
326 #define MAX_CONFNUM 80
329 /*! \brief The MeetMe Conference object */
330 struct ast_conference {
331 ast_mutex_t playlock; /*!< Conference specific lock (players) */
332 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
333 char confno[MAX_CONFNUM]; /*!< Conference */
334 struct ast_channel *chan; /*!< Announcements channel */
335 struct ast_channel *lchan; /*!< Listen/Record channel */
336 int fd; /*!< Announcements fd */
337 int zapconf; /*!< Zaptel Conf # */
338 int users; /*!< Number of active users */
339 int markedusers; /*!< Number of marked users */
340 time_t start; /*!< Start time (s) */
341 int refcount; /*!< reference count of usage */
342 enum recording_state recording:2; /*!< recording status */
343 unsigned int isdynamic:1; /*!< Created on the fly? */
344 unsigned int locked:1; /*!< Is the conference locked? */
345 pthread_t recordthread; /*!< thread for recording */
346 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
347 pthread_attr_t attr; /*!< thread attribute */
348 const char *recordingfilename; /*!< Filename to record the Conference into */
349 const char *recordingformat; /*!< Format to record the Conference in */
350 char pin[MAX_PIN]; /*!< If protected by a PIN */
351 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
353 struct ast_frame *transframe[32];
354 struct ast_frame *origframe;
355 struct ast_trans_pvt *transpath[32];
356 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
357 AST_LIST_ENTRY(ast_conference) list;
360 static AST_LIST_HEAD_STATIC(confs, ast_conference);
362 static unsigned int conf_map[1024] = {0, };
365 int desired; /*!< Desired volume adjustment */
366 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
369 /*! \brief The MeetMe User object */
370 struct ast_conf_user {
371 int user_no; /*!< User Number */
372 int userflags; /*!< Flags as set in the conference */
373 int adminflags; /*!< Flags set by the Admin */
374 struct ast_channel *chan; /*!< Connected channel */
375 int talking; /*!< Is user talking */
376 int zapchannel; /*!< Is a Zaptel channel */
377 char usrvalue[50]; /*!< Custom User Value */
378 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
379 time_t jointime; /*!< Time the user joined the conference */
381 struct volume listen;
382 AST_LIST_ENTRY(ast_conf_user) list;
385 enum sla_which_trunk_refs {
390 enum sla_trunk_state {
391 SLA_TRUNK_STATE_IDLE,
392 SLA_TRUNK_STATE_RINGING,
394 SLA_TRUNK_STATE_ONHOLD,
395 SLA_TRUNK_STATE_ONHOLD_BYME,
398 enum sla_hold_access {
399 /*! This means that any station can put it on hold, and any station
400 * can retrieve the call from hold. */
402 /*! This means that only the station that put the call on hold may
403 * retrieve it from hold. */
407 struct sla_trunk_ref;
410 AST_RWLIST_ENTRY(sla_station) entry;
411 AST_DECLARE_STRING_FIELDS(
412 AST_STRING_FIELD(name);
413 AST_STRING_FIELD(device);
414 AST_STRING_FIELD(autocontext);
416 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
417 struct ast_dial *dial;
418 /*! Ring timeout for this station, for any trunk. If a ring timeout
419 * is set for a specific trunk on this station, that will take
420 * priority over this value. */
421 unsigned int ring_timeout;
422 /*! Ring delay for this station, for any trunk. If a ring delay
423 * is set for a specific trunk on this station, that will take
424 * priority over this value. */
425 unsigned int ring_delay;
426 /*! This option uses the values in the sla_hold_access enum and sets the
427 * access control type for hold on this station. */
428 unsigned int hold_access:1;
429 /*! Use count for inside sla_station_exec */
430 unsigned int ref_count;
433 struct sla_station_ref {
434 AST_LIST_ENTRY(sla_station_ref) entry;
435 struct sla_station *station;
439 AST_RWLIST_ENTRY(sla_trunk) entry;
440 AST_DECLARE_STRING_FIELDS(
441 AST_STRING_FIELD(name);
442 AST_STRING_FIELD(device);
443 AST_STRING_FIELD(autocontext);
445 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
446 /*! Number of stations that use this trunk */
447 unsigned int num_stations;
448 /*! Number of stations currently on a call with this trunk */
449 unsigned int active_stations;
450 /*! Number of stations that have this trunk on hold. */
451 unsigned int hold_stations;
452 struct ast_channel *chan;
453 unsigned int ring_timeout;
454 /*! If set to 1, no station will be able to join an active call with
456 unsigned int barge_disabled:1;
457 /*! This option uses the values in the sla_hold_access enum and sets the
458 * access control type for hold on this trunk. */
459 unsigned int hold_access:1;
460 /*! Whether this trunk is currently on hold, meaning that once a station
461 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
462 unsigned int on_hold:1;
463 /*! Use count for inside sla_trunk_exec */
464 unsigned int ref_count;
467 struct sla_trunk_ref {
468 AST_LIST_ENTRY(sla_trunk_ref) entry;
469 struct sla_trunk *trunk;
470 enum sla_trunk_state state;
471 struct ast_channel *chan;
472 /*! Ring timeout to use when this trunk is ringing on this specific
473 * station. This takes higher priority than a ring timeout set at
474 * the station level. */
475 unsigned int ring_timeout;
476 /*! Ring delay to use when this trunk is ringing on this specific
477 * station. This takes higher priority than a ring delay set at
478 * the station level. */
479 unsigned int ring_delay;
482 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
483 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
485 static const char sla_registrar[] = "SLA";
487 /*! \brief Event types that can be queued up for the SLA thread */
488 enum sla_event_type {
489 /*! A station has put the call on hold */
491 /*! The state of a dial has changed */
492 SLA_EVENT_DIAL_STATE,
493 /*! The state of a ringing trunk has changed */
494 SLA_EVENT_RINGING_TRUNK,
495 /*! A reload of configuration has been requested */
497 /*! Poke the SLA thread so it can check if it can perform a reload */
498 SLA_EVENT_CHECK_RELOAD,
502 enum sla_event_type type;
503 struct sla_station *station;
504 struct sla_trunk_ref *trunk_ref;
505 AST_LIST_ENTRY(sla_event) entry;
508 /*! \brief A station that failed to be dialed
509 * \note Only used by the SLA thread. */
510 struct sla_failed_station {
511 struct sla_station *station;
512 struct timeval last_try;
513 AST_LIST_ENTRY(sla_failed_station) entry;
516 /*! \brief A trunk that is ringing */
517 struct sla_ringing_trunk {
518 struct sla_trunk *trunk;
519 /*! The time that this trunk started ringing */
520 struct timeval ring_begin;
521 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
522 AST_LIST_ENTRY(sla_ringing_trunk) entry;
525 enum sla_station_hangup {
526 SLA_STATION_HANGUP_NORMAL,
527 SLA_STATION_HANGUP_TIMEOUT,
530 /*! \brief A station that is ringing */
531 struct sla_ringing_station {
532 struct sla_station *station;
533 /*! The time that this station started ringing */
534 struct timeval ring_begin;
535 AST_LIST_ENTRY(sla_ringing_station) entry;
539 * \brief A structure for data used by the sla thread
542 /*! The SLA thread ID */
546 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
547 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
548 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
549 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
551 /*! Attempt to handle CallerID, even though it is known not to work
552 * properly in some situations. */
553 unsigned int attempt_callerid:1;
554 /*! A reload has been requested */
555 unsigned int reload:1;
557 .thread = AST_PTHREADT_NULL,
560 /*! The number of audio buffers to be allocated on pseudo channels
561 * when in a conference */
562 static int audio_buffers;
564 /*! Map 'volume' levels from -5 through +5 into
565 * decibel (dB) settings for channel drivers
566 * Note: these are not a straight linear-to-dB
567 * conversion... the numbers have been modified
568 * to give the user a better level of adjustability
570 static char const gain_map[] = {
585 static int admin_exec(struct ast_channel *chan, void *data);
586 static void *recordthread(void *args);
588 static char *istalking(int x)
593 return "(unmonitored)";
595 return "(not talking)";
598 static int careful_write(int fd, unsigned char *data, int len, int block)
605 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
606 res = ioctl(fd, ZT_IOMUX, &x);
610 res = write(fd, data, len);
612 if (errno != EAGAIN) {
613 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
625 static int set_talk_volume(struct ast_conf_user *user, int volume)
629 /* attempt to make the adjustment in the channel driver;
630 if successful, don't adjust in the frame reading routine
632 gain_adjust = gain_map[volume + 5];
634 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
637 static int set_listen_volume(struct ast_conf_user *user, int volume)
641 /* attempt to make the adjustment in the channel driver;
642 if successful, don't adjust in the frame reading routine
644 gain_adjust = gain_map[volume + 5];
646 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
649 static void tweak_volume(struct volume *vol, enum volume_action action)
653 switch (vol->desired) {
668 switch (vol->desired) {
684 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
686 tweak_volume(&user->talk, action);
687 /* attempt to make the adjustment in the channel driver;
688 if successful, don't adjust in the frame reading routine
690 if (!set_talk_volume(user, user->talk.desired))
691 user->talk.actual = 0;
693 user->talk.actual = user->talk.desired;
696 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
698 tweak_volume(&user->listen, action);
699 /* attempt to make the adjustment in the channel driver;
700 if successful, don't adjust in the frame reading routine
702 if (!set_listen_volume(user, user->listen.desired))
703 user->listen.actual = 0;
705 user->listen.actual = user->listen.desired;
708 static void reset_volumes(struct ast_conf_user *user)
710 signed char zero_volume = 0;
712 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
713 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
716 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
722 if (!ast_check_hangup(chan))
723 res = ast_autoservice_start(chan);
725 AST_LIST_LOCK(&confs);
741 careful_write(conf->fd, data, len, 1);
744 AST_LIST_UNLOCK(&confs);
747 ast_autoservice_stop(chan);
751 * \brief Find or create a conference
753 * \param confno The conference name/number
754 * \param pin The regular user pin
755 * \param pinadmin The admin pin
756 * \param make Make the conf if it doesn't exist
757 * \param dynamic Mark the newly created conference as dynamic
758 * \param refcount How many references to mark on the conference
759 * \param chan The asterisk channel
761 * \return A pointer to the conference struct, or NULL if it wasn't found and
762 * make or dynamic were not set.
764 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
766 struct ast_conference *cnf;
767 struct zt_confinfo ztc = { 0, };
770 AST_LIST_LOCK(&confs);
772 AST_LIST_TRAVERSE(&confs, cnf, list) {
773 if (!strcmp(confno, cnf->confno))
777 if (cnf || (!make && !dynamic))
781 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
784 ast_mutex_init(&cnf->playlock);
785 ast_mutex_init(&cnf->listenlock);
786 cnf->recordthread = AST_PTHREADT_NULL;
787 ast_mutex_init(&cnf->recordthreadlock);
788 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
789 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
790 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
791 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
793 /* Setup a new zap conference */
795 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
796 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
797 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
798 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
806 cnf->zapconf = ztc.confno;
808 /* Setup a new channel for playback of audio files */
809 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
811 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
812 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
814 ztc.confno = cnf->zapconf;
815 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
816 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
817 ast_log(LOG_WARNING, "Error setting conference\n");
819 ast_hangup(cnf->chan);
829 /* Fill the conference struct */
830 cnf->start = time(NULL);
831 cnf->isdynamic = dynamic ? 1 : 0;
832 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
833 AST_LIST_INSERT_HEAD(&confs, cnf, list);
835 /* Reserve conference number in map */
836 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
837 conf_map[confno_int] = 1;
841 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
843 AST_LIST_UNLOCK(&confs);
849 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
851 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
853 int len = strlen(word);
855 struct ast_conference *cnf = NULL;
856 struct ast_conf_user *usr = NULL;
859 char *myline, *ret = NULL;
861 if (pos == 1) { /* Command */
862 return ast_cli_complete(word, cmds, state);
863 } else if (pos == 2) { /* Conference Number */
864 AST_LIST_LOCK(&confs);
865 AST_LIST_TRAVERSE(&confs, cnf, list) {
866 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
871 ret = ast_strdup(ret); /* dup before releasing the lock */
872 AST_LIST_UNLOCK(&confs);
874 } else if (pos == 3) {
875 /* User Number || Conf Command option*/
876 if (strstr(line, "mute") || strstr(line, "kick")) {
877 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
878 return ast_strdup("all");
880 AST_LIST_LOCK(&confs);
882 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
883 myline = ast_strdupa(line);
884 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
885 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
889 AST_LIST_TRAVERSE(&confs, cnf, list) {
890 if (!strcmp(confno, cnf->confno))
895 /* Search for the user */
896 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
897 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
898 if (!strncasecmp(word, usrno, len) && ++which > state)
902 AST_LIST_UNLOCK(&confs);
903 return usr ? ast_strdup(usrno) : NULL;
904 } else if ( strstr(line, "list") && ( 0 == state ) )
905 return ast_strdup("concise");
911 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
913 /* Process the command */
914 struct ast_conference *cnf;
915 struct ast_conf_user *user;
917 int i = 0, total = 0;
919 char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
920 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
921 char cmdline[1024] = "";
925 e->command = "meetme";
927 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
928 " Executes a command for the conference or on a conferee\n";
931 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
935 ast_cli(a->fd, "Invalid Arguments.\n");
936 /* Check for length so no buffer will overflow... */
937 for (i = 0; i < a->argc; i++) {
938 if (strlen(a->argv[i]) > 100)
939 ast_cli(a->fd, "Invalid Arguments.\n");
942 /* 'MeetMe': List all the conferences */
944 AST_LIST_LOCK(&confs);
945 if (AST_LIST_EMPTY(&confs)) {
946 ast_cli(a->fd, "No active MeetMe conferences.\n");
947 AST_LIST_UNLOCK(&confs);
950 ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
951 AST_LIST_TRAVERSE(&confs, cnf, list) {
952 if (cnf->markedusers == 0)
953 strcpy(cmdline, "N/A ");
955 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
956 hr = (now - cnf->start) / 3600;
957 min = ((now - cnf->start) % 3600) / 60;
958 sec = (now - cnf->start) % 60;
960 ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
964 AST_LIST_UNLOCK(&confs);
965 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
969 return CLI_SHOWUSAGE;
970 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
971 if (strstr(a->argv[1], "lock")) {
972 if (strcmp(a->argv[1], "lock") == 0) {
974 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
977 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
979 } else if (strstr(a->argv[1], "mute")) {
981 return CLI_SHOWUSAGE;
982 if (strcmp(a->argv[1], "mute") == 0) {
984 if (strcmp(a->argv[3], "all") == 0) {
985 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
987 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
988 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
992 if (strcmp(a->argv[3], "all") == 0) {
993 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
995 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
996 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
999 } else if (strcmp(a->argv[1], "kick") == 0) {
1001 return CLI_SHOWUSAGE;
1002 if (strcmp(a->argv[3], "all") == 0) {
1004 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1006 /* Kick a single user */
1007 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1008 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1010 } else if(strcmp(a->argv[1], "list") == 0) {
1011 int concise = ( 4 == a->argc && ( !strcasecmp(a->argv[3], "concise") ) );
1012 /* List all the users in a conference */
1013 if (AST_LIST_EMPTY(&confs)) {
1015 ast_cli(a->fd, "No active conferences.\n");
1018 /* Find the right conference */
1019 AST_LIST_LOCK(&confs);
1020 AST_LIST_TRAVERSE(&confs, cnf, list) {
1021 if (strcmp(cnf->confno, a->argv[2]) == 0)
1026 ast_cli(a->fd, "No such conference: %s.\n",a->argv[2]);
1027 AST_LIST_UNLOCK(&confs);
1030 /* Show all the users */
1032 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1033 hr = (now - user->jointime) / 3600;
1034 min = ((now - user->jointime) % 3600) / 60;
1035 sec = (now - user->jointime) % 60;
1037 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
1039 S_OR(user->chan->cid.cid_num, "<unknown>"),
1040 S_OR(user->chan->cid.cid_name, "<no name>"),
1042 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1043 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1044 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1045 istalking(user->talking), hr, min, sec);
1047 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1049 S_OR(user->chan->cid.cid_num, ""),
1050 S_OR(user->chan->cid.cid_name, ""),
1052 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1053 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1054 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1055 user->talking, hr, min, sec);
1059 ast_cli(a->fd,"%d users in that conference.\n",cnf->users);
1060 AST_LIST_UNLOCK(&confs);
1063 return CLI_SHOWUSAGE;
1065 ast_debug(1, "Cmdline: %s\n", cmdline);
1067 admin_exec(NULL, cmdline);
1072 static const char *sla_hold_str(unsigned int hold_access)
1074 const char *hold = "Unknown";
1076 switch (hold_access) {
1080 case SLA_HOLD_PRIVATE:
1089 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1091 const struct sla_trunk *trunk;
1095 e->command = "sla show trunks";
1097 "Usage: sla show trunks\n"
1098 " This will list all trunks defined in sla.conf\n";
1105 "=============================================================\n"
1106 "=== Configured SLA Trunks ===================================\n"
1107 "=============================================================\n"
1109 AST_RWLIST_RDLOCK(&sla_trunks);
1110 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1111 struct sla_station_ref *station_ref;
1112 char ring_timeout[16] = "(none)";
1113 if (trunk->ring_timeout)
1114 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1115 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1116 "=== Trunk Name: %s\n"
1117 "=== ==> Device: %s\n"
1118 "=== ==> AutoContext: %s\n"
1119 "=== ==> RingTimeout: %s\n"
1120 "=== ==> BargeAllowed: %s\n"
1121 "=== ==> HoldAccess: %s\n"
1122 "=== ==> Stations ...\n",
1123 trunk->name, trunk->device,
1124 S_OR(trunk->autocontext, "(none)"),
1126 trunk->barge_disabled ? "No" : "Yes",
1127 sla_hold_str(trunk->hold_access));
1128 AST_RWLIST_RDLOCK(&sla_stations);
1129 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1130 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1131 AST_RWLIST_UNLOCK(&sla_stations);
1132 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1134 AST_RWLIST_UNLOCK(&sla_trunks);
1135 ast_cli(a->fd, "=============================================================\n\n");
1140 static const char *trunkstate2str(enum sla_trunk_state state)
1142 #define S(e) case e: return # e;
1144 S(SLA_TRUNK_STATE_IDLE)
1145 S(SLA_TRUNK_STATE_RINGING)
1146 S(SLA_TRUNK_STATE_UP)
1147 S(SLA_TRUNK_STATE_ONHOLD)
1148 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1150 return "Uknown State";
1154 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1156 const struct sla_station *station;
1160 e->command = "sla show stations";
1162 "Usage: sla show stations\n"
1163 " This will list all stations defined in sla.conf\n";
1170 "=============================================================\n"
1171 "=== Configured SLA Stations =================================\n"
1172 "=============================================================\n"
1174 AST_RWLIST_RDLOCK(&sla_stations);
1175 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1176 struct sla_trunk_ref *trunk_ref;
1177 char ring_timeout[16] = "(none)";
1178 char ring_delay[16] = "(none)";
1179 if (station->ring_timeout) {
1180 snprintf(ring_timeout, sizeof(ring_timeout),
1181 "%u", station->ring_timeout);
1183 if (station->ring_delay) {
1184 snprintf(ring_delay, sizeof(ring_delay),
1185 "%u", station->ring_delay);
1187 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1188 "=== Station Name: %s\n"
1189 "=== ==> Device: %s\n"
1190 "=== ==> AutoContext: %s\n"
1191 "=== ==> RingTimeout: %s\n"
1192 "=== ==> RingDelay: %s\n"
1193 "=== ==> HoldAccess: %s\n"
1194 "=== ==> Trunks ...\n",
1195 station->name, station->device,
1196 S_OR(station->autocontext, "(none)"),
1197 ring_timeout, ring_delay,
1198 sla_hold_str(station->hold_access));
1199 AST_RWLIST_RDLOCK(&sla_trunks);
1200 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1201 if (trunk_ref->ring_timeout) {
1202 snprintf(ring_timeout, sizeof(ring_timeout),
1203 "%u", trunk_ref->ring_timeout);
1205 strcpy(ring_timeout, "(none)");
1206 if (trunk_ref->ring_delay) {
1207 snprintf(ring_delay, sizeof(ring_delay),
1208 "%u", trunk_ref->ring_delay);
1210 strcpy(ring_delay, "(none)");
1211 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1212 "=== ==> State: %s\n"
1213 "=== ==> RingTimeout: %s\n"
1214 "=== ==> RingDelay: %s\n",
1215 trunk_ref->trunk->name,
1216 trunkstate2str(trunk_ref->state),
1217 ring_timeout, ring_delay);
1219 AST_RWLIST_UNLOCK(&sla_trunks);
1220 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1223 AST_RWLIST_UNLOCK(&sla_stations);
1224 ast_cli(a->fd, "============================================================\n"
1230 static struct ast_cli_entry cli_meetme[] = {
1231 NEW_CLI(meetme_cmd, "Execute a command on a conference or conferee"),
1232 NEW_CLI(sla_show_trunks, "Show SLA Trunks"),
1233 NEW_CLI(sla_show_stations, "Show SLA Stations"),
1236 static void conf_flush(int fd, struct ast_channel *chan)
1240 /* read any frames that may be waiting on the channel
1244 struct ast_frame *f;
1246 /* when no frames are available, this will wait
1247 for 1 millisecond maximum
1249 while (ast_waitfor(chan, 1)) {
1253 else /* channel was hung up or something else happened */
1258 /* flush any data sitting in the pseudo channel */
1260 if (ioctl(fd, ZT_FLUSH, &x))
1261 ast_log(LOG_WARNING, "Error flushing channel\n");
1265 /* Remove the conference from the list and free it.
1266 We assume that this was called while holding conflock. */
1267 static int conf_free(struct ast_conference *conf)
1271 AST_LIST_REMOVE(&confs, conf, list);
1272 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1274 if (conf->recording == MEETME_RECORD_ACTIVE) {
1275 conf->recording = MEETME_RECORD_TERMINATE;
1276 AST_LIST_UNLOCK(&confs);
1279 AST_LIST_LOCK(&confs);
1280 if (conf->recording == MEETME_RECORD_OFF)
1282 AST_LIST_UNLOCK(&confs);
1286 for (x=0;x<AST_FRAME_BITS;x++) {
1287 if (conf->transframe[x])
1288 ast_frfree(conf->transframe[x]);
1289 if (conf->transpath[x])
1290 ast_translator_free_path(conf->transpath[x]);
1292 if (conf->origframe)
1293 ast_frfree(conf->origframe);
1295 ast_hangup(conf->lchan);
1297 ast_hangup(conf->chan);
1301 ast_mutex_destroy(&conf->playlock);
1302 ast_mutex_destroy(&conf->listenlock);
1303 ast_mutex_destroy(&conf->recordthreadlock);
1309 static void conf_queue_dtmf(const struct ast_conference *conf,
1310 const struct ast_conf_user *sender, struct ast_frame *f)
1312 struct ast_conf_user *user;
1314 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1317 if (ast_write(user->chan, f) < 0)
1318 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1322 static void sla_queue_event_full(enum sla_event_type type,
1323 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1325 struct sla_event *event;
1327 if (!(event = ast_calloc(1, sizeof(*event))))
1331 event->trunk_ref = trunk_ref;
1332 event->station = station;
1335 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1339 ast_mutex_lock(&sla.lock);
1340 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1341 ast_cond_signal(&sla.cond);
1342 ast_mutex_unlock(&sla.lock);
1345 static void sla_queue_event_nolock(enum sla_event_type type)
1347 sla_queue_event_full(type, NULL, NULL, 0);
1350 static void sla_queue_event(enum sla_event_type type)
1352 sla_queue_event_full(type, NULL, NULL, 1);
1355 /*! \brief Queue a SLA event from the conference */
1356 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1357 struct ast_conference *conf)
1359 struct sla_station *station;
1360 struct sla_trunk_ref *trunk_ref = NULL;
1363 trunk_name = ast_strdupa(conf->confno);
1364 strsep(&trunk_name, "_");
1365 if (ast_strlen_zero(trunk_name)) {
1366 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1370 AST_RWLIST_RDLOCK(&sla_stations);
1371 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1372 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1373 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1379 AST_RWLIST_UNLOCK(&sla_stations);
1382 ast_debug(1, "Trunk not found for event!\n");
1386 sla_queue_event_full(type, trunk_ref, station, 1);
1389 /* Decrement reference counts, as incremented by find_conf() */
1390 static int dispose_conf(struct ast_conference *conf)
1395 AST_LIST_LOCK(&confs);
1396 if (ast_atomic_dec_and_test(&conf->refcount)) {
1397 /* Take the conference room number out of an inuse state */
1398 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1399 conf_map[confno_int] = 0;
1403 AST_LIST_UNLOCK(&confs);
1409 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1411 struct ast_conf_user *user = NULL;
1412 struct ast_conf_user *usr = NULL;
1414 struct zt_confinfo ztc, ztc_empty;
1415 struct ast_frame *f;
1416 struct ast_channel *c;
1417 struct ast_frame fr;
1425 int musiconhold = 0;
1428 int currentmarked = 0;
1431 int menu_active = 0;
1432 int using_pseudo = 0;
1437 struct ast_dsp *dsp=NULL;
1438 struct ast_app *app;
1439 const char *agifile;
1440 const char *agifiledefault = "conf-background.agi";
1441 char meetmesecs[30] = "";
1442 char exitcontext[AST_MAX_CONTEXT] = "";
1443 char recordingtmp[AST_MAX_EXTENSION] = "";
1444 char members[10] = "";
1445 int dtmf, opt_waitmarked_timeout = 0;
1448 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1449 char *buf = __buf + AST_FRIENDLY_OFFSET;
1450 char *exitkeys = NULL;
1452 if (!(user = ast_calloc(1, sizeof(*user))))
1455 /* Possible timeout waiting for marked user */
1456 if ((confflags & CONFFLAG_WAITMARKED) &&
1457 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1458 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1459 (opt_waitmarked_timeout > 0)) {
1460 timeout = time(NULL) + opt_waitmarked_timeout;
1464 if ((confflags & CONFFLAG_KEYEXIT)) {
1465 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1466 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1468 exitkeys = ast_strdupa("#"); /* Default */
1471 if (confflags & CONFFLAG_RECORDCONF) {
1472 if (!conf->recordingfilename) {
1473 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1474 if (!conf->recordingfilename) {
1475 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1476 conf->recordingfilename = ast_strdupa(recordingtmp);
1478 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1479 if (!conf->recordingformat) {
1480 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1481 conf->recordingformat = ast_strdupa(recordingtmp);
1483 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1484 conf->confno, conf->recordingfilename, conf->recordingformat);
1488 ast_mutex_lock(&conf->recordthreadlock);
1489 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1490 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1491 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1493 ztc.confno = conf->zapconf;
1494 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1495 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1496 ast_log(LOG_WARNING, "Error starting listen channel\n");
1497 ast_hangup(conf->lchan);
1500 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1503 ast_mutex_unlock(&conf->recordthreadlock);
1505 time(&user->jointime);
1507 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1508 /* Sorry, but this conference is locked! */
1509 if (!ast_streamfile(chan, "conf-locked", chan->language))
1510 ast_waitstream(chan, "");
1514 if (confflags & CONFFLAG_MARKEDUSER)
1515 conf->markedusers++;
1517 ast_mutex_lock(&conf->playlock);
1519 if (AST_LIST_EMPTY(&conf->userlist))
1522 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1524 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1527 user->userflags = confflags;
1528 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1532 snprintf(members, sizeof(members), "%d", conf->users);
1533 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1535 /* This device changed state now - if this is the first user */
1536 if (conf->users == 1)
1537 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1539 ast_mutex_unlock(&conf->playlock);
1541 /* return the unique ID of the conference */
1542 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1544 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1545 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1546 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1547 else if (!ast_strlen_zero(chan->macrocontext))
1548 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1550 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1553 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1554 snprintf(user->namerecloc, sizeof(user->namerecloc),
1555 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1556 conf->confno, user->user_no);
1557 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1558 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1560 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1565 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1566 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1567 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1568 ast_waitstream(chan, "");
1569 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1570 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1571 ast_waitstream(chan, "");
1574 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1575 int keepplaying = 1;
1577 if (conf->users == 2) {
1578 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1579 res = ast_waitstream(chan, AST_DIGIT_ANY);
1580 ast_stopstream(chan);
1587 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1588 res = ast_waitstream(chan, AST_DIGIT_ANY);
1589 ast_stopstream(chan);
1596 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1602 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1603 res = ast_waitstream(chan, AST_DIGIT_ANY);
1604 ast_stopstream(chan);
1613 ast_indicate(chan, -1);
1615 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1616 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1620 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1621 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1625 retryzap = strcasecmp(chan->tech->type, "Zap");
1626 user->zapchannel = !retryzap;
1629 origfd = chan->fds[0];
1631 fd = open("/dev/zap/pseudo", O_RDWR);
1633 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1637 /* Make non-blocking */
1638 flags = fcntl(fd, F_GETFL);
1640 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1644 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1645 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1649 /* Setup buffering information */
1650 memset(&bi, 0, sizeof(bi));
1651 bi.bufsize = CONF_SIZE/2;
1652 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1653 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1654 bi.numbufs = audio_buffers;
1655 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1656 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1661 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1662 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1668 /* XXX Make sure we're not running on a pseudo channel XXX */
1672 memset(&ztc, 0, sizeof(ztc));
1673 memset(&ztc_empty, 0, sizeof(ztc_empty));
1674 /* Check to see if we're in a conference... */
1676 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1677 ast_log(LOG_WARNING, "Error getting conference\n");
1682 /* Whoa, already in a conference... Retry... */
1684 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1689 memset(&ztc, 0, sizeof(ztc));
1690 /* Add us to the conference */
1692 ztc.confno = conf->zapconf;
1694 ast_mutex_lock(&conf->playlock);
1696 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1697 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1698 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1699 ast_waitstream(conf->chan, "");
1700 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1701 ast_waitstream(conf->chan, "");
1705 if (confflags & CONFFLAG_MONITOR)
1706 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1707 else if (confflags & CONFFLAG_TALKER)
1708 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1710 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1712 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1713 ast_log(LOG_WARNING, "Error setting conference\n");
1715 ast_mutex_unlock(&conf->playlock);
1718 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1721 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1726 chan->name, chan->uniqueid, conf->confno, user->user_no);
1730 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1732 if (!(confflags & CONFFLAG_QUIET))
1733 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1734 conf_play(chan, conf, ENTER);
1737 ast_mutex_unlock(&conf->playlock);
1739 conf_flush(fd, chan);
1741 if (confflags & CONFFLAG_AGI) {
1742 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1743 or use default filename of conf-background.agi */
1745 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1747 agifile = agifiledefault;
1749 if (user->zapchannel) {
1750 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1752 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1754 /* Find a pointer to the agi app and execute the script */
1755 app = pbx_findapp("agi");
1757 char *s = ast_strdupa(agifile);
1758 ret = pbx_exec(chan, app, s);
1760 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1763 if (user->zapchannel) {
1764 /* Remove CONFMUTE mode on Zap channel */
1766 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1769 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1770 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1772 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1774 if (!(dsp = ast_dsp_new())) {
1775 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1779 int menu_was_active = 0;
1784 if (timeout && time(NULL) >= timeout)
1787 /* if we have just exited from the menu, and the user had a channel-driver
1788 volume adjustment, restore it
1790 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1791 set_talk_volume(user, user->listen.desired);
1793 menu_was_active = menu_active;
1795 currentmarked = conf->markedusers;
1796 if (!(confflags & CONFFLAG_QUIET) &&
1797 (confflags & CONFFLAG_MARKEDUSER) &&
1798 (confflags & CONFFLAG_WAITMARKED) &&
1800 if (currentmarked == 1 && conf->users > 1) {
1801 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1802 if (conf->users - 1 == 1) {
1803 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1804 ast_waitstream(chan, "");
1806 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1807 ast_waitstream(chan, "");
1810 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1811 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1812 ast_waitstream(chan, "");
1815 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1818 /* Update the struct with the actual confflags */
1819 user->userflags = confflags;
1821 if (confflags & CONFFLAG_WAITMARKED) {
1822 if(currentmarked == 0) {
1823 if (lastmarked != 0) {
1824 if (!(confflags & CONFFLAG_QUIET))
1825 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1826 ast_waitstream(chan, "");
1827 if (confflags & CONFFLAG_MARKEDEXIT) {
1828 if (confflags & CONFFLAG_KICK_CONTINUE)
1832 ztc.confmode = ZT_CONF_CONF;
1833 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1834 ast_log(LOG_WARNING, "Error setting conference\n");
1840 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1841 ast_moh_start(chan, NULL, NULL);
1844 } else if(currentmarked >= 1 && lastmarked == 0) {
1845 /* Marked user entered, so cancel timeout */
1847 if (confflags & CONFFLAG_MONITOR)
1848 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1849 else if (confflags & CONFFLAG_TALKER)
1850 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1852 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1853 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1854 ast_log(LOG_WARNING, "Error setting conference\n");
1858 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1862 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1863 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1864 ast_waitstream(chan, "");
1865 conf_play(chan, conf, ENTER);
1870 /* trying to add moh for single person conf */
1871 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1872 if (conf->users == 1) {
1873 if (musiconhold == 0) {
1874 ast_moh_start(chan, NULL, NULL);
1885 /* Leave if the last marked user left */
1886 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1887 if (confflags & CONFFLAG_KICK_CONTINUE)
1894 /* Check if my modes have changed */
1896 /* If I should be muted but am still talker, mute me */
1897 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1898 ztc.confmode ^= ZT_CONF_TALKER;
1899 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1900 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1905 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1911 chan->name, chan->uniqueid, conf->confno, user->user_no);
1914 /* If I should be un-muted but am not talker, un-mute me */
1915 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1916 ztc.confmode |= ZT_CONF_TALKER;
1917 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1918 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1923 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1929 chan->name, chan->uniqueid, conf->confno, user->user_no);
1932 /* If I have been kicked, exit the conference */
1933 if (user->adminflags & ADMINFLAG_KICKME) {
1934 //You have been kicked.
1935 if (!(confflags & CONFFLAG_QUIET) &&
1936 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1937 ast_waitstream(chan, "");
1943 /* Perform an extra hangup check just in case */
1944 if (ast_check_hangup(chan))
1948 if (c->fds[0] != origfd) {
1950 /* Kill old pseudo */
1954 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
1955 retryzap = strcasecmp(c->tech->type, "Zap");
1956 user->zapchannel = !retryzap;
1959 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1960 f = ast_read_noaudio(c);
1965 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1966 if (user->talk.actual)
1967 ast_frame_adjust_volume(f, user->talk.actual);
1972 if (user->talking == -1)
1975 res = ast_dsp_silence(dsp, f, &totalsilence);
1976 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1978 if (confflags & CONFFLAG_MONITORTALKER)
1979 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1985 chan->name, chan->uniqueid, conf->confno, user->user_no);
1987 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1989 if (confflags & CONFFLAG_MONITORTALKER)
1990 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1996 chan->name, chan->uniqueid, conf->confno, user->user_no);
2000 /* Absolutely do _not_ use careful_write here...
2001 it is important that we read data from the channel
2002 as fast as it arrives, and feed it into the conference.
2003 The buffering in the pseudo channel will take care of any
2004 timing differences, unless they are so drastic as to lose
2005 audio frames (in which case carefully writing would only
2006 have delayed the audio even further).
2008 /* As it turns out, we do want to use careful write. We just
2009 don't want to block, but we do want to at least *try*
2010 to write out all the samples.
2013 careful_write(fd, f->data, f->datalen, 0);
2015 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2018 if (confflags & CONFFLAG_PASS_DTMF)
2019 conf_queue_dtmf(conf, user, f);
2021 tmp[0] = f->subclass;
2023 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2024 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2029 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2031 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2034 exitkey[0] = f->subclass;
2037 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2039 if (confflags & CONFFLAG_PASS_DTMF)
2040 conf_queue_dtmf(conf, user, f);
2044 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2045 if (confflags & CONFFLAG_PASS_DTMF)
2046 conf_queue_dtmf(conf, user, f);
2047 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2048 ast_log(LOG_WARNING, "Error setting conference\n");
2054 /* if we are entering the menu, and the user has a channel-driver
2055 volume adjustment, clear it
2057 if (!menu_active && user->talk.desired && !user->talk.actual)
2058 set_talk_volume(user, 0);
2063 if ((confflags & CONFFLAG_ADMIN)) {
2067 /* Record this sound! */
2068 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2069 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2070 ast_stopstream(chan);
2077 case '1': /* Un/Mute */
2080 /* for admin, change both admin and use flags */
2081 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2082 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2084 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2086 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2087 if (!ast_streamfile(chan, "conf-muted", chan->language))
2088 ast_waitstream(chan, "");
2090 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2091 ast_waitstream(chan, "");
2094 case '2': /* Un/Lock the Conference */
2098 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2099 ast_waitstream(chan, "");
2102 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2103 ast_waitstream(chan, "");
2106 case '3': /* Eject last user */
2108 usr = AST_LIST_LAST(&conf->userlist);
2109 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2110 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2111 ast_waitstream(chan, "");
2113 usr->adminflags |= ADMINFLAG_KICKME;
2114 ast_stopstream(chan);
2117 tweak_listen_volume(user, VOL_DOWN);
2120 tweak_listen_volume(user, VOL_UP);
2123 tweak_talk_volume(user, VOL_DOWN);
2129 tweak_talk_volume(user, VOL_UP);
2133 /* Play an error message! */
2134 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2135 ast_waitstream(chan, "");
2143 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2144 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2145 ast_stopstream(chan);
2152 case '1': /* Un/Mute */
2155 /* user can only toggle the self-muted state */
2156 user->adminflags ^= ADMINFLAG_SELFMUTED;
2158 /* they can't override the admin mute state */
2159 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2160 if (!ast_streamfile(chan, "conf-muted", chan->language))
2161 ast_waitstream(chan, "");
2163 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2164 ast_waitstream(chan, "");
2168 tweak_listen_volume(user, VOL_DOWN);
2171 tweak_listen_volume(user, VOL_UP);
2174 tweak_talk_volume(user, VOL_DOWN);
2180 tweak_talk_volume(user, VOL_UP);
2184 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2185 ast_waitstream(chan, "");
2191 ast_moh_start(chan, NULL, NULL);
2193 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2194 ast_log(LOG_WARNING, "Error setting conference\n");
2200 conf_flush(fd, chan);
2201 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2202 && confflags & CONFFLAG_PASS_DTMF) {
2203 conf_queue_dtmf(conf, user, f);
2204 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2205 switch (f->subclass) {
2206 case AST_CONTROL_HOLD:
2207 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2212 } else if (f->frametype == AST_FRAME_NULL) {
2213 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2216 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2217 chan->name, f->frametype, f->subclass);
2220 } else if (outfd > -1) {
2221 res = read(outfd, buf, CONF_SIZE);
2223 memset(&fr, 0, sizeof(fr));
2224 fr.frametype = AST_FRAME_VOICE;
2225 fr.subclass = AST_FORMAT_SLINEAR;
2229 fr.offset = AST_FRIENDLY_OFFSET;
2230 if ( !user->listen.actual &&
2231 ((confflags & CONFFLAG_MONITOR) ||
2232 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2233 (!user->talking)) ) {
2235 for (index=0;index<AST_FRAME_BITS;index++)
2236 if (chan->rawwriteformat & (1 << index))
2238 if (index >= AST_FRAME_BITS)
2239 goto bailoutandtrynormal;
2240 ast_mutex_lock(&conf->listenlock);
2241 if (!conf->transframe[index]) {
2242 if (conf->origframe) {
2243 if (!conf->transpath[index])
2244 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2245 if (conf->transpath[index]) {
2246 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2247 if (!conf->transframe[index])
2248 conf->transframe[index] = &ast_null_frame;
2252 if (conf->transframe[index]) {
2253 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2254 if (ast_write(chan, conf->transframe[index]))
2255 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2258 ast_mutex_unlock(&conf->listenlock);
2259 goto bailoutandtrynormal;
2261 ast_mutex_unlock(&conf->listenlock);
2263 bailoutandtrynormal:
2264 if (user->listen.actual)
2265 ast_frame_adjust_volume(&fr, user->listen.actual);
2266 if (ast_write(chan, &fr) < 0) {
2267 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2271 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2273 lastmarked = currentmarked;
2283 /* Take out of conference */
2287 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2288 ast_log(LOG_WARNING, "Error setting conference\n");
2292 reset_volumes(user);
2294 AST_LIST_LOCK(&confs);
2295 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2296 conf_play(chan, conf, LEAVE);
2298 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2299 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2300 if ((conf->chan) && (conf->users > 1)) {
2301 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2302 ast_waitstream(conf->chan, "");
2303 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2304 ast_waitstream(conf->chan, "");
2306 ast_filedelete(user->namerecloc, NULL);
2309 AST_LIST_UNLOCK(&confs);
2312 AST_LIST_LOCK(&confs);
2317 if (user->user_no) { /* Only cleanup users who really joined! */
2319 hr = (now - user->jointime) / 3600;
2320 min = ((now - user->jointime) % 3600) / 60;
2321 sec = (now - user->jointime) % 60;
2324 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2329 "CallerIDNum: %s\r\n"
2330 "CallerIDName: %s\r\n"
2331 "Duration: %ld\r\n",
2332 chan->name, chan->uniqueid, conf->confno,
2334 S_OR(user->chan->cid.cid_num, "<unknown>"),
2335 S_OR(user->chan->cid.cid_name, "<unknown>"),
2336 (long)(now - user->jointime));
2341 snprintf(members, sizeof(members), "%d", conf->users);
2342 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2343 if (confflags & CONFFLAG_MARKEDUSER)
2344 conf->markedusers--;
2345 /* Remove ourselves from the list */
2346 AST_LIST_REMOVE(&conf->userlist, user, list);
2348 /* Change any states */
2350 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2352 /* Return the number of seconds the user was in the conf */
2353 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2354 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2357 AST_LIST_UNLOCK(&confs);
2362 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2363 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2365 struct ast_variable *var;
2366 struct ast_conference *cnf;
2368 /* Check first in the conference list */
2369 AST_LIST_LOCK(&confs);
2370 AST_LIST_TRAVERSE(&confs, cnf, list) {
2371 if (!strcmp(confno, cnf->confno))
2375 cnf->refcount += refcount;
2377 AST_LIST_UNLOCK(&confs);
2380 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2382 var = ast_load_realtime("meetme", "confno", confno, NULL);
2388 if (!strcasecmp(var->name, "pin")) {
2389 pin = ast_strdupa(var->value);
2390 } else if (!strcasecmp(var->name, "adminpin")) {
2391 pinadmin = ast_strdupa(var->value);
2395 ast_variables_destroy(var);
2397 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2401 if (confflags && !cnf->chan &&
2402 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2403 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2404 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2405 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2408 if (confflags && !cnf->chan &&
2409 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2410 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2411 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2419 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2420 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2422 struct ast_config *cfg;
2423 struct ast_variable *var;
2424 struct ast_flags config_flags = { 0 };
2425 struct ast_conference *cnf;
2427 AST_DECLARE_APP_ARGS(args,
2428 AST_APP_ARG(confno);
2430 AST_APP_ARG(pinadmin);
2433 /* Check first in the conference list */
2434 ast_debug(1,"The requested confno is '%s'?\n", confno);
2435 AST_LIST_LOCK(&confs);
2436 AST_LIST_TRAVERSE(&confs, cnf, list) {
2437 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2438 if (!strcmp(confno, cnf->confno))
2442 cnf->refcount += refcount;
2444 AST_LIST_UNLOCK(&confs);
2448 /* No need to parse meetme.conf */
2449 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2451 if (dynamic_pin[0] == 'q') {
2452 /* Query the user to enter a PIN */
2453 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2456 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2458 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2461 /* Check the config */
2462 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2464 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2467 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2468 if (strcasecmp(var->name, "conf"))
2471 if (!(parse = ast_strdupa(var->value)))
2474 AST_STANDARD_APP_ARGS(args, parse);
2475 ast_debug(3,"Will conf %s match %s?\n", confno, args.confno);
2476 if (!strcasecmp(args.confno, confno)) {
2477 /* Bingo it's a valid conference */
2478 cnf = build_conf(args.confno,
2480 S_OR(args.pinadmin, ""),
2481 make, dynamic, refcount, chan);
2486 ast_debug(1, "%s isn't a valid conference\n", confno);
2488 ast_config_destroy(cfg);
2490 } else if (dynamic_pin) {
2491 /* Correct for the user selecting 'D' instead of 'd' to have
2492 someone join into a conference that has already been created
2494 if (dynamic_pin[0] == 'q')
2495 dynamic_pin[0] = '\0';
2499 if (confflags && !cnf->chan &&
2500 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2501 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2502 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2503 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2506 if (confflags && !cnf->chan &&
2507 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2508 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2509 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2516 /*! \brief The MeetmeCount application */
2517 static int count_exec(struct ast_channel *chan, void *data)
2520 struct ast_conference *conf;
2524 AST_DECLARE_APP_ARGS(args,
2525 AST_APP_ARG(confno);
2526 AST_APP_ARG(varname);
2529 if (ast_strlen_zero(data)) {
2530 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2534 if (!(localdata = ast_strdupa(data)))
2537 AST_STANDARD_APP_ARGS(args, localdata);
2539 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2542 count = conf->users;
2548 if (!ast_strlen_zero(args.varname)){
2549 /* have var so load it and exit */
2550 snprintf(val, sizeof(val), "%d",count);
2551 pbx_builtin_setvar_helper(chan, args.varname, val);
2553 if (chan->_state != AST_STATE_UP)
2555 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2561 /*! \brief The meetme() application */
2562 static int conf_exec(struct ast_channel *chan, void *data)
2565 char confno[MAX_CONFNUM] = "";
2568 struct ast_conference *cnf = NULL;
2569 struct ast_flags confflags = {0}, config_flags = { 0 };
2571 int empty = 0, empty_no_pin = 0;
2572 int always_prompt = 0;
2573 char *notdata, *info, the_pin[MAX_PIN] = "";
2574 AST_DECLARE_APP_ARGS(args,
2575 AST_APP_ARG(confno);
2576 AST_APP_ARG(options);
2579 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2581 if (ast_strlen_zero(data)) {
2588 if (chan->_state != AST_STATE_UP)
2591 info = ast_strdupa(notdata);
2593 AST_STANDARD_APP_ARGS(args, info);
2596 ast_copy_string(confno, args.confno, sizeof(confno));
2597 if (ast_strlen_zero(confno)) {
2603 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2606 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2607 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2608 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2609 strcpy(the_pin, "q");
2611 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2612 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2613 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2621 struct ast_config *cfg;
2622 struct ast_variable *var;
2625 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2626 if ((empty_no_pin) || (!dynamic)) {
2627 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2629 var = ast_variable_browse(cfg, "rooms");
2631 if (!strcasecmp(var->name, "conf")) {
2632 char *stringp = ast_strdupa(var->value);
2634 char *confno_tmp = strsep(&stringp, "|,");
2637 /* For static: run through the list and see if this conference is empty */
2638 AST_LIST_LOCK(&confs);
2639 AST_LIST_TRAVERSE(&confs, cnf, list) {
2640 if (!strcmp(confno_tmp, cnf->confno)) {
2641 /* The conference exists, therefore it's not empty */
2646 AST_LIST_UNLOCK(&confs);
2648 /* At this point, we have a confno_tmp (static conference) that is empty */
2649 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2650 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2651 * Case 2: empty_no_pin and pin is blank (but not NULL)
2652 * Case 3: not empty_no_pin
2654 ast_copy_string(confno, confno_tmp, sizeof(confno));
2656 /* XXX the map is not complete (but we do have a confno) */
2664 ast_config_destroy(cfg);
2668 /* Select first conference number not in use */
2669 if (ast_strlen_zero(confno) && dynamic) {
2670 AST_LIST_LOCK(&confs);
2671 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2673 snprintf(confno, sizeof(confno), "%d", i);
2678 AST_LIST_UNLOCK(&confs);
2682 if (ast_strlen_zero(confno)) {
2683 res = ast_streamfile(chan, "conf-noempty", chan->language);
2685 ast_waitstream(chan, "");
2687 if (sscanf(confno, "%d", &confno_int) == 1) {
2688 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2689 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2691 ast_waitstream(chan, "");
2692 res = ast_say_digits(chan, confno_int, "", chan->language);
2696 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2701 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2702 /* Prompt user for conference number */
2703 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2705 /* Don't try to validate when we catch an error */
2711 if (!ast_strlen_zero(confno)) {
2712 /* Check the validity of the conference */
2713 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2714 sizeof(the_pin), 1, &confflags);
2716 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2717 the_pin, sizeof(the_pin), 1, &confflags);
2721 res = ast_streamfile(chan, "conf-invalid", chan->language);
2723 ast_waitstream(chan, "");
2728 if ((!ast_strlen_zero(cnf->pin) &&
2729 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2730 (!ast_strlen_zero(cnf->pinadmin) &&
2731 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2732 char pin[MAX_PIN] = "";
2735 /* Allow the pin to be retried up to 3 times */
2736 for (j = 0; j < 3; j++) {
2737 if (*the_pin && (always_prompt == 0)) {
2738 ast_copy_string(pin, the_pin, sizeof(pin));
2741 /* Prompt user for pin if pin is required */
2742 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2745 if (!strcasecmp(pin, cnf->pin) ||
2746 (!ast_strlen_zero(cnf->pinadmin) &&
2747 !strcasecmp(pin, cnf->pinadmin))) {
2750 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2751 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2752 /* Run the conference */
2753 res = conf_run(chan, cnf, confflags.flags, optargs);
2757 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2758 res = ast_waitstream(chan, AST_DIGIT_ANY);
2759 ast_stopstream(chan);
2762 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2774 /* failed when getting the pin */
2777 /* see if we need to get rid of the conference */
2781 /* Don't retry pin with a static pin */
2782 if (*the_pin && (always_prompt==0)) {
2787 /* No pin required */
2790 /* Run the conference */
2791 res = conf_run(chan, cnf, confflags.flags, optargs);
2797 } while (allowretry);
2805 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2807 struct ast_conf_user *user = NULL;
2810 sscanf(callerident, "%i", &cid);
2811 if (conf && callerident) {
2812 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2813 if (cid == user->user_no)
2820 /*! \brief The MeetMeadmin application */
2821 /* MeetMeAdmin(confno, command, caller) */
2822 static int admin_exec(struct ast_channel *chan, void *data) {
2824 struct ast_conference *cnf;
2825 struct ast_conf_user *user = NULL;
2826 AST_DECLARE_APP_ARGS(args,
2827 AST_APP_ARG(confno);
2828 AST_APP_ARG(command);
2832 if (ast_strlen_zero(data)) {
2833 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2837 params = ast_strdupa(data);
2838 AST_STANDARD_APP_ARGS(args, params);
2840 if (!args.command) {
2841 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2845 AST_LIST_LOCK(&confs);
2846 AST_LIST_TRAVERSE(&confs, cnf, list) {
2847 if (!strcmp(cnf->confno, args.confno))
2852 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2853 AST_LIST_UNLOCK(&confs);
2857 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2860 user = find_user(cnf, args.user);
2862 switch (*args.command) {
2863 case 76: /* L: Lock */
2866 case 108: /* l: Unlock */
2869 case 75: /* K: kick all users */
2870 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2871 user->adminflags |= ADMINFLAG_KICKME;
2873 case 101: /* e: Eject last user*/
2874 user = AST_LIST_LAST(&cnf->userlist);
2875 if (!(user->userflags & CONFFLAG_ADMIN))
2876 user->adminflags |= ADMINFLAG_KICKME;
2878 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2880 case 77: /* M: Mute */
2882 user->adminflags |= ADMINFLAG_MUTED;
2884 ast_log(LOG_NOTICE, "Specified User not found!\n");
2886 case 78: /* N: Mute all (non-admin) users */
2887 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2888 if (!(user->userflags & CONFFLAG_ADMIN))
2889 user->adminflags |= ADMINFLAG_MUTED;
2892 case 109: /* m: Unmute */
2894 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2896 ast_log(LOG_NOTICE, "Specified User not found!\n");
2898 case 110: /* n: Unmute all users */
2899 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2900 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2902 case 107: /* k: Kick user */
2904 user->adminflags |= ADMINFLAG_KICKME;
2906 ast_log(LOG_NOTICE, "Specified User not found!\n");
2908 case 118: /* v: Lower all users listen volume */
2909 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2910 tweak_listen_volume(user, VOL_DOWN);
2912 case 86: /* V: Raise all users listen volume */
2913 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2914 tweak_listen_volume(user, VOL_UP);
2916 case 115: /* s: Lower all users speaking volume */
2917 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2918 tweak_talk_volume(user, VOL_DOWN);
2920 case 83: /* S: Raise all users speaking volume */
2921 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2922 tweak_talk_volume(user, VOL_UP);
2924 case 82: /* R: Reset all volume levels */
2925 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2926 reset_volumes(user);
2928 case 114: /* r: Reset user's volume level */
2930 reset_volumes(user);
2932 ast_log(LOG_NOTICE, "Specified User not found!\n");
2934 case 85: /* U: Raise user's listen volume */
2936 tweak_listen_volume(user, VOL_UP);
2938 ast_log(LOG_NOTICE, "Specified User not found!\n");
2940 case 117: /* u: Lower user's listen volume */
2942 tweak_listen_volume(user, VOL_DOWN);
2944 ast_log(LOG_NOTICE, "Specified User not found!\n");
2946 case 84: /* T: Raise user's talk volume */
2948 tweak_talk_volume(user, VOL_UP);
2950 ast_log(LOG_NOTICE, "Specified User not found!\n");
2952 case 116: /* t: Lower user's talk volume */
2954 tweak_talk_volume(user, VOL_DOWN);
2956 ast_log(LOG_NOTICE, "Specified User not found!\n");
2960 AST_LIST_UNLOCK(&confs);
2967 /*--- channel_admin_exec: The MeetMeChannelAdmin application */
2968 /* MeetMeChannelAdmin(channel, command) */
2969 static int channel_admin_exec(struct ast_channel *chan, void *data) {
2971 struct ast_conference *conf = NULL;
2972 struct ast_conf_user *user = NULL;
2973 AST_DECLARE_APP_ARGS(args,
2974 AST_APP_ARG(channel);
2975 AST_APP_ARG(command);
2978 if (ast_strlen_zero(data)) {
2979 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
2983 params = ast_strdupa(data);
2984 AST_STANDARD_APP_ARGS(args, params);
2986 if (!args.channel) {
2987 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
2991 if (!args.command) {
2992 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
2996 AST_LIST_LOCK(&confs);
2997 AST_LIST_TRAVERSE(&confs, conf, list) {
2998 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2999 if (!strcmp(user->chan->name, args.channel))
3005 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
3006 AST_LIST_UNLOCK(&confs);
3010 /* perform the specified action */
3011 switch (*args.command) {
3012 case 77: /* M: Mute */
3013 user->adminflags |= ADMINFLAG_MUTED;
3015 case 109: /* m: Unmute */
3016 user->adminflags &= ~ADMINFLAG_MUTED;
3018 case 107: /* k: Kick user */
3019 user->adminflags |= ADMINFLAG_KICKME;
3021 default: /* unknown command */
3022 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
3026 AST_LIST_UNLOCK(&confs);
3031 static int meetmemute(struct mansession *s, const struct message *m, int mute)
3033 struct ast_conference *conf;
3034 struct ast_conf_user *user;
3035 const char *confid = astman_get_header(m, "Meetme");
3036 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
3039 if (ast_strlen_zero(confid)) {
3040 astman_send_error(s, m, "Meetme conference not specified");
3044 if (ast_strlen_zero(userid)) {
3045 astman_send_error(s, m, "Meetme user number not specified");
3049 userno = strtoul(userid, &userid, 10);
3052 astman_send_error(s, m, "Invalid user number");
3056 /* Look in the conference list */
3057 AST_LIST_LOCK(&confs);
3058 AST_LIST_TRAVERSE(&confs, conf, list) {
3059 if (!strcmp(confid, conf->confno))
3064 AST_LIST_UNLOCK(&confs);
3065 astman_send_error(s, m, "Meetme conference does not exist");
3069 AST_LIST_TRAVERSE(&conf->userlist, user, list)
3070 if (user->user_no == userno)
3074 AST_LIST_UNLOCK(&confs);
3075 astman_send_error(s, m, "User number not found");
3080 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
3082 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
3084 AST_LIST_UNLOCK(&confs);
3086 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);
3088 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
3092 static int action_meetmemute(struct mansession *s, const struct message *m)
3094 return meetmemute(s, m, 1);
3097 static int action_meetmeunmute(struct mansession *s, const struct message *m)
3099 return meetmemute(s, m, 0);
3102 static char mandescr_meetmelist[] =
3103 "Description: Lists all users in a particular MeetMe conference.\n"
3104 "MeetmeList will follow as separate events, followed by a final event called\n"
3105 "MeetmeListComplete.\n"
3107 " *ActionId: <id>\n"
3108 " *Conference: <confno>\n";
3110 static int action_meetmelist(struct mansession *s, const struct message *m)
3112 const char *actionid = astman_get_header(m, "ActionID");
3113 const char *conference = astman_get_header(m, "Conference");
3114 char idText[80] = "";