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 const char *recordingfilename; /*!< Filename to record the Conference into */
347 const char *recordingformat; /*!< Format to record the Conference in */
348 char pin[MAX_PIN]; /*!< If protected by a PIN */
349 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
351 struct ast_frame *transframe[32];
352 struct ast_frame *origframe;
353 struct ast_trans_pvt *transpath[32];
354 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
355 AST_LIST_ENTRY(ast_conference) list;
358 static AST_LIST_HEAD_STATIC(confs, ast_conference);
360 static unsigned int conf_map[1024] = {0, };
363 int desired; /*!< Desired volume adjustment */
364 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
367 /*! \brief The MeetMe User object */
368 struct ast_conf_user {
369 int user_no; /*!< User Number */
370 int userflags; /*!< Flags as set in the conference */
371 int adminflags; /*!< Flags set by the Admin */
372 struct ast_channel *chan; /*!< Connected channel */
373 int talking; /*!< Is user talking */
374 int zapchannel; /*!< Is a Zaptel channel */
375 char usrvalue[50]; /*!< Custom User Value */
376 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
377 time_t jointime; /*!< Time the user joined the conference */
379 struct volume listen;
380 AST_LIST_ENTRY(ast_conf_user) list;
383 enum sla_which_trunk_refs {
388 enum sla_trunk_state {
389 SLA_TRUNK_STATE_IDLE,
390 SLA_TRUNK_STATE_RINGING,
392 SLA_TRUNK_STATE_ONHOLD,
393 SLA_TRUNK_STATE_ONHOLD_BYME,
396 enum sla_hold_access {
397 /*! This means that any station can put it on hold, and any station
398 * can retrieve the call from hold. */
400 /*! This means that only the station that put the call on hold may
401 * retrieve it from hold. */
405 struct sla_trunk_ref;
408 AST_RWLIST_ENTRY(sla_station) entry;
409 AST_DECLARE_STRING_FIELDS(
410 AST_STRING_FIELD(name);
411 AST_STRING_FIELD(device);
412 AST_STRING_FIELD(autocontext);
414 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
415 struct ast_dial *dial;
416 /*! Ring timeout for this station, for any trunk. If a ring timeout
417 * is set for a specific trunk on this station, that will take
418 * priority over this value. */
419 unsigned int ring_timeout;
420 /*! Ring delay for this station, for any trunk. If a ring delay
421 * is set for a specific trunk on this station, that will take
422 * priority over this value. */
423 unsigned int ring_delay;
424 /*! This option uses the values in the sla_hold_access enum and sets the
425 * access control type for hold on this station. */
426 unsigned int hold_access:1;
427 /*! Use count for inside sla_station_exec */
428 unsigned int ref_count;
431 struct sla_station_ref {
432 AST_LIST_ENTRY(sla_station_ref) entry;
433 struct sla_station *station;
437 AST_RWLIST_ENTRY(sla_trunk) entry;
438 AST_DECLARE_STRING_FIELDS(
439 AST_STRING_FIELD(name);
440 AST_STRING_FIELD(device);
441 AST_STRING_FIELD(autocontext);
443 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
444 /*! Number of stations that use this trunk */
445 unsigned int num_stations;
446 /*! Number of stations currently on a call with this trunk */
447 unsigned int active_stations;
448 /*! Number of stations that have this trunk on hold. */
449 unsigned int hold_stations;
450 struct ast_channel *chan;
451 unsigned int ring_timeout;
452 /*! If set to 1, no station will be able to join an active call with
454 unsigned int barge_disabled:1;
455 /*! This option uses the values in the sla_hold_access enum and sets the
456 * access control type for hold on this trunk. */
457 unsigned int hold_access:1;
458 /*! Whether this trunk is currently on hold, meaning that once a station
459 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
460 unsigned int on_hold:1;
461 /*! Use count for inside sla_trunk_exec */
462 unsigned int ref_count;
465 struct sla_trunk_ref {
466 AST_LIST_ENTRY(sla_trunk_ref) entry;
467 struct sla_trunk *trunk;
468 enum sla_trunk_state state;
469 struct ast_channel *chan;
470 /*! Ring timeout to use when this trunk is ringing on this specific
471 * station. This takes higher priority than a ring timeout set at
472 * the station level. */
473 unsigned int ring_timeout;
474 /*! Ring delay to use when this trunk is ringing on this specific
475 * station. This takes higher priority than a ring delay set at
476 * the station level. */
477 unsigned int ring_delay;
480 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
481 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
483 static const char sla_registrar[] = "SLA";
485 /*! \brief Event types that can be queued up for the SLA thread */
486 enum sla_event_type {
487 /*! A station has put the call on hold */
489 /*! The state of a dial has changed */
490 SLA_EVENT_DIAL_STATE,
491 /*! The state of a ringing trunk has changed */
492 SLA_EVENT_RINGING_TRUNK,
493 /*! A reload of configuration has been requested */
495 /*! Poke the SLA thread so it can check if it can perform a reload */
496 SLA_EVENT_CHECK_RELOAD,
500 enum sla_event_type type;
501 struct sla_station *station;
502 struct sla_trunk_ref *trunk_ref;
503 AST_LIST_ENTRY(sla_event) entry;
506 /*! \brief A station that failed to be dialed
507 * \note Only used by the SLA thread. */
508 struct sla_failed_station {
509 struct sla_station *station;
510 struct timeval last_try;
511 AST_LIST_ENTRY(sla_failed_station) entry;
514 /*! \brief A trunk that is ringing */
515 struct sla_ringing_trunk {
516 struct sla_trunk *trunk;
517 /*! The time that this trunk started ringing */
518 struct timeval ring_begin;
519 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
520 AST_LIST_ENTRY(sla_ringing_trunk) entry;
523 enum sla_station_hangup {
524 SLA_STATION_HANGUP_NORMAL,
525 SLA_STATION_HANGUP_TIMEOUT,
528 /*! \brief A station that is ringing */
529 struct sla_ringing_station {
530 struct sla_station *station;
531 /*! The time that this station started ringing */
532 struct timeval ring_begin;
533 AST_LIST_ENTRY(sla_ringing_station) entry;
537 * \brief A structure for data used by the sla thread
540 /*! The SLA thread ID */
544 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
545 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
546 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
547 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
549 /*! Attempt to handle CallerID, even though it is known not to work
550 * properly in some situations. */
551 unsigned int attempt_callerid:1;
552 /*! A reload has been requested */
553 unsigned int reload:1;
555 .thread = AST_PTHREADT_NULL,
558 /*! The number of audio buffers to be allocated on pseudo channels
559 * when in a conference */
560 static int audio_buffers;
562 /*! Map 'volume' levels from -5 through +5 into
563 * decibel (dB) settings for channel drivers
564 * Note: these are not a straight linear-to-dB
565 * conversion... the numbers have been modified
566 * to give the user a better level of adjustability
568 static char const gain_map[] = {
583 static int admin_exec(struct ast_channel *chan, void *data);
584 static void *recordthread(void *args);
586 static char *istalking(int x)
591 return "(unmonitored)";
593 return "(not talking)";
596 static int careful_write(int fd, unsigned char *data, int len, int block)
603 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
604 res = ioctl(fd, ZT_IOMUX, &x);
608 res = write(fd, data, len);
610 if (errno != EAGAIN) {
611 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
623 static int set_talk_volume(struct ast_conf_user *user, int volume)
627 /* attempt to make the adjustment in the channel driver;
628 if successful, don't adjust in the frame reading routine
630 gain_adjust = gain_map[volume + 5];
632 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
635 static int set_listen_volume(struct ast_conf_user *user, int volume)
639 /* attempt to make the adjustment in the channel driver;
640 if successful, don't adjust in the frame reading routine
642 gain_adjust = gain_map[volume + 5];
644 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
647 static void tweak_volume(struct volume *vol, enum volume_action action)
651 switch (vol->desired) {
666 switch (vol->desired) {
682 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
684 tweak_volume(&user->talk, action);
685 /* attempt to make the adjustment in the channel driver;
686 if successful, don't adjust in the frame reading routine
688 if (!set_talk_volume(user, user->talk.desired))
689 user->talk.actual = 0;
691 user->talk.actual = user->talk.desired;
694 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
696 tweak_volume(&user->listen, action);
697 /* attempt to make the adjustment in the channel driver;
698 if successful, don't adjust in the frame reading routine
700 if (!set_listen_volume(user, user->listen.desired))
701 user->listen.actual = 0;
703 user->listen.actual = user->listen.desired;
706 static void reset_volumes(struct ast_conf_user *user)
708 signed char zero_volume = 0;
710 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
711 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
714 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
720 if (!ast_check_hangup(chan))
721 res = ast_autoservice_start(chan);
723 AST_LIST_LOCK(&confs);
739 careful_write(conf->fd, data, len, 1);
742 AST_LIST_UNLOCK(&confs);
745 ast_autoservice_stop(chan);
749 * \brief Find or create a conference
751 * \param confno The conference name/number
752 * \param pin The regular user pin
753 * \param pinadmin The admin pin
754 * \param make Make the conf if it doesn't exist
755 * \param dynamic Mark the newly created conference as dynamic
756 * \param refcount How many references to mark on the conference
757 * \param chan The asterisk channel
759 * \return A pointer to the conference struct, or NULL if it wasn't found and
760 * make or dynamic were not set.
762 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
764 struct ast_conference *cnf;
765 struct zt_confinfo ztc = { 0, };
768 AST_LIST_LOCK(&confs);
770 AST_LIST_TRAVERSE(&confs, cnf, list) {
771 if (!strcmp(confno, cnf->confno))
775 if (cnf || (!make && !dynamic))
779 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
782 ast_mutex_init(&cnf->playlock);
783 ast_mutex_init(&cnf->listenlock);
784 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
785 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
786 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
787 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
789 /* Setup a new zap conference */
791 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
792 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
793 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
794 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
802 cnf->zapconf = ztc.confno;
804 /* Setup a new channel for playback of audio files */
805 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
807 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
808 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
810 ztc.confno = cnf->zapconf;
811 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
812 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
813 ast_log(LOG_WARNING, "Error setting conference\n");
815 ast_hangup(cnf->chan);
825 /* Fill the conference struct */
826 cnf->start = time(NULL);
827 cnf->isdynamic = dynamic ? 1 : 0;
828 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
829 AST_LIST_INSERT_HEAD(&confs, cnf, list);
831 /* Reserve conference number in map */
832 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
833 conf_map[confno_int] = 1;
837 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
839 AST_LIST_UNLOCK(&confs);
844 static int meetme_cmd(int fd, int argc, char **argv)
846 /* Process the command */
847 struct ast_conference *cnf;
848 struct ast_conf_user *user;
850 int i = 0, total = 0;
852 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
853 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
854 char cmdline[1024] = "";
857 ast_cli(fd, "Invalid Arguments.\n");
858 /* Check for length so no buffer will overflow... */
859 for (i = 0; i < argc; i++) {
860 if (strlen(argv[i]) > 100)
861 ast_cli(fd, "Invalid Arguments.\n");
864 /* 'MeetMe': List all the conferences */
866 AST_LIST_LOCK(&confs);
867 if (AST_LIST_EMPTY(&confs)) {
868 ast_cli(fd, "No active MeetMe conferences.\n");
869 AST_LIST_UNLOCK(&confs);
870 return RESULT_SUCCESS;
872 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
873 AST_LIST_TRAVERSE(&confs, cnf, list) {
874 if (cnf->markedusers == 0)
875 strcpy(cmdline, "N/A ");
877 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
878 hr = (now - cnf->start) / 3600;
879 min = ((now - cnf->start) % 3600) / 60;
880 sec = (now - cnf->start) % 60;
882 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
886 AST_LIST_UNLOCK(&confs);
887 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
888 return RESULT_SUCCESS;
891 return RESULT_SHOWUSAGE;
892 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
893 if (strstr(argv[1], "lock")) {
894 if (strcmp(argv[1], "lock") == 0) {
896 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
899 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
901 } else if (strstr(argv[1], "mute")) {
903 return RESULT_SHOWUSAGE;
904 if (strcmp(argv[1], "mute") == 0) {
906 if (strcmp(argv[3], "all") == 0) {
907 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
909 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
910 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
914 if (strcmp(argv[3], "all") == 0) {
915 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
917 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
918 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
921 } else if (strcmp(argv[1], "kick") == 0) {
923 return RESULT_SHOWUSAGE;
924 if (strcmp(argv[3], "all") == 0) {
926 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
928 /* Kick a single user */
929 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
930 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
932 } else if(strcmp(argv[1], "list") == 0) {
933 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
934 /* List all the users in a conference */
935 if (AST_LIST_EMPTY(&confs)) {
937 ast_cli(fd, "No active conferences.\n");
938 return RESULT_SUCCESS;
940 /* Find the right conference */
941 AST_LIST_LOCK(&confs);
942 AST_LIST_TRAVERSE(&confs, cnf, list) {
943 if (strcmp(cnf->confno, argv[2]) == 0)
948 ast_cli(fd, "No such conference: %s.\n",argv[2]);
949 AST_LIST_UNLOCK(&confs);
950 return RESULT_SUCCESS;
952 /* Show all the users */
954 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
955 hr = (now - user->jointime) / 3600;
956 min = ((now - user->jointime) % 3600) / 60;
957 sec = (now - user->jointime) % 60;
959 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
961 S_OR(user->chan->cid.cid_num, "<unknown>"),
962 S_OR(user->chan->cid.cid_name, "<no name>"),
964 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
965 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
966 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
967 istalking(user->talking), hr, min, sec);
969 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
971 S_OR(user->chan->cid.cid_num, ""),
972 S_OR(user->chan->cid.cid_name, ""),
974 user->userflags & CONFFLAG_ADMIN ? "1" : "",
975 user->userflags & CONFFLAG_MONITOR ? "1" : "",
976 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
977 user->talking, hr, min, sec);
981 ast_cli(fd,"%d users in that conference.\n",cnf->users);
982 AST_LIST_UNLOCK(&confs);
983 return RESULT_SUCCESS;
985 return RESULT_SHOWUSAGE;
987 ast_debug(1, "Cmdline: %s\n", cmdline);
989 admin_exec(NULL, cmdline);
994 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
996 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
998 int len = strlen(word);
1000 struct ast_conference *cnf = NULL;
1001 struct ast_conf_user *usr = NULL;
1002 char *confno = NULL;
1003 char usrno[50] = "";
1004 char *myline, *ret = NULL;
1006 if (pos == 1) { /* Command */
1007 return ast_cli_complete(word, cmds, state);
1008 } else if (pos == 2) { /* Conference Number */
1009 AST_LIST_LOCK(&confs);
1010 AST_LIST_TRAVERSE(&confs, cnf, list) {
1011 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1016 ret = ast_strdup(ret); /* dup before releasing the lock */
1017 AST_LIST_UNLOCK(&confs);
1019 } else if (pos == 3) {
1020 /* User Number || Conf Command option*/
1021 if (strstr(line, "mute") || strstr(line, "kick")) {
1022 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
1023 return ast_strdup("all");
1025 AST_LIST_LOCK(&confs);
1027 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1028 myline = ast_strdupa(line);
1029 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1030 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1034 AST_LIST_TRAVERSE(&confs, cnf, list) {
1035 if (!strcmp(confno, cnf->confno))
1040 /* Search for the user */
1041 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1042 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1043 if (!strncasecmp(word, usrno, len) && ++which > state)
1047 AST_LIST_UNLOCK(&confs);
1048 return usr ? ast_strdup(usrno) : NULL;
1049 } else if ( strstr(line, "list") && ( 0 == state ) )
1050 return ast_strdup("concise");
1056 static const char meetme_usage[] =
1057 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1058 " Executes a command for the conference or on a conferee\n";
1060 static const char *sla_hold_str(unsigned int hold_access)
1062 const char *hold = "Unknown";
1064 switch (hold_access) {
1068 case SLA_HOLD_PRIVATE:
1077 static int sla_show_trunks(int fd, int argc, char **argv)
1079 const struct sla_trunk *trunk;
1082 "=============================================================\n"
1083 "=== Configured SLA Trunks ===================================\n"
1084 "=============================================================\n"
1086 AST_RWLIST_RDLOCK(&sla_trunks);
1087 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1088 struct sla_station_ref *station_ref;
1089 char ring_timeout[16] = "(none)";
1090 if (trunk->ring_timeout)
1091 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1092 ast_cli(fd, "=== ---------------------------------------------------------\n"
1093 "=== Trunk Name: %s\n"
1094 "=== ==> Device: %s\n"
1095 "=== ==> AutoContext: %s\n"
1096 "=== ==> RingTimeout: %s\n"
1097 "=== ==> BargeAllowed: %s\n"
1098 "=== ==> HoldAccess: %s\n"
1099 "=== ==> Stations ...\n",
1100 trunk->name, trunk->device,
1101 S_OR(trunk->autocontext, "(none)"),
1103 trunk->barge_disabled ? "No" : "Yes",
1104 sla_hold_str(trunk->hold_access));
1105 AST_RWLIST_RDLOCK(&sla_stations);
1106 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1107 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1108 AST_RWLIST_UNLOCK(&sla_stations);
1109 ast_cli(fd, "=== ---------------------------------------------------------\n"
1112 AST_RWLIST_UNLOCK(&sla_trunks);
1113 ast_cli(fd, "=============================================================\n"
1116 return RESULT_SUCCESS;
1119 static const char *trunkstate2str(enum sla_trunk_state state)
1121 #define S(e) case e: return # e;
1123 S(SLA_TRUNK_STATE_IDLE)
1124 S(SLA_TRUNK_STATE_RINGING)
1125 S(SLA_TRUNK_STATE_UP)
1126 S(SLA_TRUNK_STATE_ONHOLD)
1127 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1129 return "Uknown State";
1133 static const char sla_show_trunks_usage[] =
1134 "Usage: sla show trunks\n"
1135 " This will list all trunks defined in sla.conf\n";
1137 static int sla_show_stations(int fd, int argc, char **argv)
1139 const struct sla_station *station;
1142 "=============================================================\n"
1143 "=== Configured SLA Stations =================================\n"
1144 "=============================================================\n"
1146 AST_RWLIST_RDLOCK(&sla_stations);
1147 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1148 struct sla_trunk_ref *trunk_ref;
1149 char ring_timeout[16] = "(none)";
1150 char ring_delay[16] = "(none)";
1151 if (station->ring_timeout) {
1152 snprintf(ring_timeout, sizeof(ring_timeout),
1153 "%u", station->ring_timeout);
1155 if (station->ring_delay) {
1156 snprintf(ring_delay, sizeof(ring_delay),
1157 "%u", station->ring_delay);
1159 ast_cli(fd, "=== ---------------------------------------------------------\n"
1160 "=== Station Name: %s\n"
1161 "=== ==> Device: %s\n"
1162 "=== ==> AutoContext: %s\n"
1163 "=== ==> RingTimeout: %s\n"
1164 "=== ==> RingDelay: %s\n"
1165 "=== ==> HoldAccess: %s\n"
1166 "=== ==> Trunks ...\n",
1167 station->name, station->device,
1168 S_OR(station->autocontext, "(none)"),
1169 ring_timeout, ring_delay,
1170 sla_hold_str(station->hold_access));
1171 AST_RWLIST_RDLOCK(&sla_trunks);
1172 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1173 if (trunk_ref->ring_timeout) {
1174 snprintf(ring_timeout, sizeof(ring_timeout),
1175 "%u", trunk_ref->ring_timeout);
1177 strcpy(ring_timeout, "(none)");
1178 if (trunk_ref->ring_delay) {
1179 snprintf(ring_delay, sizeof(ring_delay),
1180 "%u", trunk_ref->ring_delay);
1182 strcpy(ring_delay, "(none)");
1183 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1184 "=== ==> State: %s\n"
1185 "=== ==> RingTimeout: %s\n"
1186 "=== ==> RingDelay: %s\n",
1187 trunk_ref->trunk->name,
1188 trunkstate2str(trunk_ref->state),
1189 ring_timeout, ring_delay);
1191 AST_RWLIST_UNLOCK(&sla_trunks);
1192 ast_cli(fd, "=== ---------------------------------------------------------\n"
1195 AST_RWLIST_UNLOCK(&sla_stations);
1196 ast_cli(fd, "============================================================\n"
1199 return RESULT_SUCCESS;
1202 static const char sla_show_stations_usage[] =
1203 "Usage: sla show stations\n"
1204 " This will list all stations defined in sla.conf\n";
1206 static struct ast_cli_entry cli_meetme[] = {
1207 { { "meetme", NULL, NULL },
1208 meetme_cmd, "Execute a command on a conference or conferee",
1209 meetme_usage, complete_meetmecmd },
1211 { { "sla", "show", "trunks", NULL },
1212 sla_show_trunks, "Show SLA Trunks",
1213 sla_show_trunks_usage, NULL },
1215 { { "sla", "show", "stations", NULL },
1216 sla_show_stations, "Show SLA Stations",
1217 sla_show_stations_usage, NULL },
1220 static void conf_flush(int fd, struct ast_channel *chan)
1224 /* read any frames that may be waiting on the channel
1228 struct ast_frame *f;
1230 /* when no frames are available, this will wait
1231 for 1 millisecond maximum
1233 while (ast_waitfor(chan, 1)) {
1237 else /* channel was hung up or something else happened */
1242 /* flush any data sitting in the pseudo channel */
1244 if (ioctl(fd, ZT_FLUSH, &x))
1245 ast_log(LOG_WARNING, "Error flushing channel\n");
1249 /* Remove the conference from the list and free it.
1250 We assume that this was called while holding conflock. */
1251 static int conf_free(struct ast_conference *conf)
1255 AST_LIST_REMOVE(&confs, conf, list);
1256 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1258 if (conf->recording == MEETME_RECORD_ACTIVE) {
1259 conf->recording = MEETME_RECORD_TERMINATE;
1260 AST_LIST_UNLOCK(&confs);
1263 AST_LIST_LOCK(&confs);
1264 if (conf->recording == MEETME_RECORD_OFF)
1266 AST_LIST_UNLOCK(&confs);
1270 for (x=0;x<AST_FRAME_BITS;x++) {
1271 if (conf->transframe[x])
1272 ast_frfree(conf->transframe[x]);
1273 if (conf->transpath[x])
1274 ast_translator_free_path(conf->transpath[x]);
1276 if (conf->origframe)
1277 ast_frfree(conf->origframe);
1279 ast_hangup(conf->lchan);
1281 ast_hangup(conf->chan);
1290 static void conf_queue_dtmf(const struct ast_conference *conf,
1291 const struct ast_conf_user *sender, struct ast_frame *f)
1293 struct ast_conf_user *user;
1295 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1298 if (ast_write(user->chan, f) < 0)
1299 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1303 static void sla_queue_event_full(enum sla_event_type type,
1304 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1306 struct sla_event *event;
1308 if (!(event = ast_calloc(1, sizeof(*event))))
1312 event->trunk_ref = trunk_ref;
1313 event->station = station;
1316 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1320 ast_mutex_lock(&sla.lock);
1321 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1322 ast_cond_signal(&sla.cond);
1323 ast_mutex_unlock(&sla.lock);
1326 static void sla_queue_event_nolock(enum sla_event_type type)
1328 sla_queue_event_full(type, NULL, NULL, 0);
1331 static void sla_queue_event(enum sla_event_type type)
1333 sla_queue_event_full(type, NULL, NULL, 1);
1336 /*! \brief Queue a SLA event from the conference */
1337 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1338 struct ast_conference *conf)
1340 struct sla_station *station;
1341 struct sla_trunk_ref *trunk_ref = NULL;
1344 trunk_name = ast_strdupa(conf->confno);
1345 strsep(&trunk_name, "_");
1346 if (ast_strlen_zero(trunk_name)) {
1347 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1351 AST_RWLIST_RDLOCK(&sla_stations);
1352 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1353 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1354 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1360 AST_RWLIST_UNLOCK(&sla_stations);
1363 ast_debug(1, "Trunk not found for event!\n");
1367 sla_queue_event_full(type, trunk_ref, station, 1);
1370 /* Decrement reference counts, as incremented by find_conf() */
1371 static int dispose_conf(struct ast_conference *conf)
1376 AST_LIST_LOCK(&confs);
1377 if (ast_atomic_dec_and_test(&conf->refcount)) {
1378 /* Take the conference room number out of an inuse state */
1379 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1380 conf_map[confno_int] = 0;
1384 AST_LIST_UNLOCK(&confs);
1390 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1392 struct ast_conf_user *user = NULL;
1393 struct ast_conf_user *usr = NULL;
1395 struct zt_confinfo ztc, ztc_empty;
1396 struct ast_frame *f;
1397 struct ast_channel *c;
1398 struct ast_frame fr;
1406 int musiconhold = 0;
1409 int currentmarked = 0;
1412 int menu_active = 0;
1413 int using_pseudo = 0;
1418 struct ast_dsp *dsp=NULL;
1419 struct ast_app *app;
1420 const char *agifile;
1421 const char *agifiledefault = "conf-background.agi";
1422 char meetmesecs[30] = "";
1423 char exitcontext[AST_MAX_CONTEXT] = "";
1424 char recordingtmp[AST_MAX_EXTENSION] = "";
1425 char members[10] = "";
1426 int dtmf, opt_waitmarked_timeout = 0;
1429 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1430 char *buf = __buf + AST_FRIENDLY_OFFSET;
1431 char *exitkeys = NULL;
1433 if (!(user = ast_calloc(1, sizeof(*user))))
1436 /* Possible timeout waiting for marked user */
1437 if ((confflags & CONFFLAG_WAITMARKED) &&
1438 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1439 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1440 (opt_waitmarked_timeout > 0)) {
1441 timeout = time(NULL) + opt_waitmarked_timeout;
1445 if ((confflags & CONFFLAG_KEYEXIT)) {
1446 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1447 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1449 exitkeys = ast_strdupa("#"); /* Default */
1452 if (confflags & CONFFLAG_RECORDCONF) {
1453 if (!conf->recordingfilename) {
1454 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1455 if (!conf->recordingfilename) {
1456 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1457 conf->recordingfilename = ast_strdupa(recordingtmp);
1459 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1460 if (!conf->recordingformat) {
1461 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1462 conf->recordingformat = ast_strdupa(recordingtmp);
1464 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1465 conf->confno, conf->recordingfilename, conf->recordingformat);
1469 if ((conf->recording == MEETME_RECORD_OFF) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1470 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1471 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1473 ztc.confno = conf->zapconf;
1474 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1475 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1476 ast_log(LOG_WARNING, "Error starting listen channel\n");
1477 ast_hangup(conf->lchan);
1480 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1484 time(&user->jointime);
1486 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1487 /* Sorry, but this conference is locked! */
1488 if (!ast_streamfile(chan, "conf-locked", chan->language))
1489 ast_waitstream(chan, "");
1493 if (confflags & CONFFLAG_MARKEDUSER)
1494 conf->markedusers++;
1496 ast_mutex_lock(&conf->playlock);
1498 if (AST_LIST_EMPTY(&conf->userlist))
1501 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1503 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1506 user->userflags = confflags;
1507 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1511 snprintf(members, sizeof(members), "%d", conf->users);
1512 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1514 /* This device changed state now - if this is the first user */
1515 if (conf->users == 1)
1516 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1518 ast_mutex_unlock(&conf->playlock);
1520 /* return the unique ID of the conference */
1521 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1523 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1524 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1525 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1526 else if (!ast_strlen_zero(chan->macrocontext))
1527 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1529 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1532 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1533 snprintf(user->namerecloc, sizeof(user->namerecloc),
1534 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1535 conf->confno, user->user_no);
1536 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1537 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1539 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1544 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1545 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1546 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1547 ast_waitstream(chan, "");
1548 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1549 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1550 ast_waitstream(chan, "");
1553 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1554 int keepplaying = 1;
1556 if (conf->users == 2) {
1557 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1558 res = ast_waitstream(chan, AST_DIGIT_ANY);
1559 ast_stopstream(chan);
1566 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1567 res = ast_waitstream(chan, AST_DIGIT_ANY);
1568 ast_stopstream(chan);
1575 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1581 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1582 res = ast_waitstream(chan, AST_DIGIT_ANY);
1583 ast_stopstream(chan);
1592 ast_indicate(chan, -1);
1594 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1595 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1599 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1600 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1604 retryzap = strcasecmp(chan->tech->type, "Zap");
1605 user->zapchannel = !retryzap;
1608 origfd = chan->fds[0];
1610 fd = open("/dev/zap/pseudo", O_RDWR);
1612 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1616 /* Make non-blocking */
1617 flags = fcntl(fd, F_GETFL);
1619 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1623 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1624 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1628 /* Setup buffering information */
1629 memset(&bi, 0, sizeof(bi));
1630 bi.bufsize = CONF_SIZE/2;
1631 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1632 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1633 bi.numbufs = audio_buffers;
1634 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1635 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1640 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1641 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1647 /* XXX Make sure we're not running on a pseudo channel XXX */
1651 memset(&ztc, 0, sizeof(ztc));
1652 memset(&ztc_empty, 0, sizeof(ztc_empty));
1653 /* Check to see if we're in a conference... */
1655 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1656 ast_log(LOG_WARNING, "Error getting conference\n");
1661 /* Whoa, already in a conference... Retry... */
1663 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1668 memset(&ztc, 0, sizeof(ztc));
1669 /* Add us to the conference */
1671 ztc.confno = conf->zapconf;
1673 ast_mutex_lock(&conf->playlock);
1675 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1676 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1677 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1678 ast_waitstream(conf->chan, "");
1679 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1680 ast_waitstream(conf->chan, "");
1684 if (confflags & CONFFLAG_MONITOR)
1685 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1686 else if (confflags & CONFFLAG_TALKER)
1687 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1689 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1691 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1692 ast_log(LOG_WARNING, "Error setting conference\n");
1694 ast_mutex_unlock(&conf->playlock);
1697 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1700 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1705 chan->name, chan->uniqueid, conf->confno, user->user_no);
1709 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1711 if (!(confflags & CONFFLAG_QUIET))
1712 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1713 conf_play(chan, conf, ENTER);
1716 ast_mutex_unlock(&conf->playlock);
1718 conf_flush(fd, chan);
1720 if (confflags & CONFFLAG_AGI) {
1721 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1722 or use default filename of conf-background.agi */
1724 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1726 agifile = agifiledefault;
1728 if (user->zapchannel) {
1729 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1731 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1733 /* Find a pointer to the agi app and execute the script */
1734 app = pbx_findapp("agi");
1736 char *s = ast_strdupa(agifile);
1737 ret = pbx_exec(chan, app, s);
1739 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1742 if (user->zapchannel) {
1743 /* Remove CONFMUTE mode on Zap channel */
1745 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1748 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1749 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1751 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1753 if (!(dsp = ast_dsp_new())) {
1754 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1758 int menu_was_active = 0;
1763 if (timeout && time(NULL) >= timeout)
1766 /* if we have just exited from the menu, and the user had a channel-driver
1767 volume adjustment, restore it
1769 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1770 set_talk_volume(user, user->listen.desired);
1772 menu_was_active = menu_active;
1774 currentmarked = conf->markedusers;
1775 if (!(confflags & CONFFLAG_QUIET) &&
1776 (confflags & CONFFLAG_MARKEDUSER) &&
1777 (confflags & CONFFLAG_WAITMARKED) &&
1779 if (currentmarked == 1 && conf->users > 1) {
1780 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1781 if (conf->users - 1 == 1) {
1782 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1783 ast_waitstream(chan, "");
1785 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1786 ast_waitstream(chan, "");
1789 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1790 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1791 ast_waitstream(chan, "");
1794 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1797 /* Update the struct with the actual confflags */
1798 user->userflags = confflags;
1800 if (confflags & CONFFLAG_WAITMARKED) {
1801 if(currentmarked == 0) {
1802 if (lastmarked != 0) {
1803 if (!(confflags & CONFFLAG_QUIET))
1804 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1805 ast_waitstream(chan, "");
1806 if (confflags & CONFFLAG_MARKEDEXIT) {
1807 if (confflags & CONFFLAG_KICK_CONTINUE)
1811 ztc.confmode = ZT_CONF_CONF;
1812 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1813 ast_log(LOG_WARNING, "Error setting conference\n");
1819 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1820 ast_moh_start(chan, NULL, NULL);
1823 ztc.confmode = ZT_CONF_CONF;
1824 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1825 ast_log(LOG_WARNING, "Error setting conference\n");
1830 } else if(currentmarked >= 1 && lastmarked == 0) {
1831 /* Marked user entered, so cancel timeout */
1833 if (confflags & CONFFLAG_MONITOR)
1834 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1835 else if (confflags & CONFFLAG_TALKER)
1836 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1838 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1839 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1840 ast_log(LOG_WARNING, "Error setting conference\n");
1844 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1848 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1849 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1850 ast_waitstream(chan, "");
1851 conf_play(chan, conf, ENTER);
1856 /* trying to add moh for single person conf */
1857 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1858 if (conf->users == 1) {
1859 if (musiconhold == 0) {
1860 ast_moh_start(chan, NULL, NULL);
1871 /* Leave if the last marked user left */
1872 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1873 if (confflags & CONFFLAG_KICK_CONTINUE)
1880 /* Check if my modes have changed */
1882 /* If I should be muted but am still talker, mute me */
1883 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1884 ztc.confmode ^= ZT_CONF_TALKER;
1885 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1886 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1891 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1897 chan->name, chan->uniqueid, conf->confno, user->user_no);
1900 /* If I should be un-muted but am not talker, un-mute me */
1901 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1902 ztc.confmode |= ZT_CONF_TALKER;
1903 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1904 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1909 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1915 chan->name, chan->uniqueid, conf->confno, user->user_no);
1918 /* If I have been kicked, exit the conference */
1919 if (user->adminflags & ADMINFLAG_KICKME) {
1920 //You have been kicked.
1921 if (!(confflags & CONFFLAG_QUIET) &&
1922 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1923 ast_waitstream(chan, "");
1929 /* Perform an extra hangup check just in case */
1930 if (ast_check_hangup(chan))
1934 if (c->fds[0] != origfd) {
1936 /* Kill old pseudo */
1940 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
1941 retryzap = strcasecmp(c->tech->type, "Zap");
1942 user->zapchannel = !retryzap;
1945 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1946 f = ast_read_noaudio(c);
1951 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1952 if (user->talk.actual)
1953 ast_frame_adjust_volume(f, user->talk.actual);
1958 if (user->talking == -1)
1961 res = ast_dsp_silence(dsp, f, &totalsilence);
1962 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1964 if (confflags & CONFFLAG_MONITORTALKER)
1965 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1971 chan->name, chan->uniqueid, conf->confno, user->user_no);
1973 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1975 if (confflags & CONFFLAG_MONITORTALKER)
1976 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1982 chan->name, chan->uniqueid, conf->confno, user->user_no);
1986 /* Absolutely do _not_ use careful_write here...
1987 it is important that we read data from the channel
1988 as fast as it arrives, and feed it into the conference.
1989 The buffering in the pseudo channel will take care of any
1990 timing differences, unless they are so drastic as to lose
1991 audio frames (in which case carefully writing would only
1992 have delayed the audio even further).
1994 /* As it turns out, we do want to use careful write. We just
1995 don't want to block, but we do want to at least *try*
1996 to write out all the samples.
1999 careful_write(fd, f->data, f->datalen, 0);
2001 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2004 if (confflags & CONFFLAG_PASS_DTMF)
2005 conf_queue_dtmf(conf, user, f);
2007 tmp[0] = f->subclass;
2009 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2010 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2015 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2017 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2020 exitkey[0] = f->subclass;
2023 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2025 if (confflags & CONFFLAG_PASS_DTMF)
2026 conf_queue_dtmf(conf, user, f);
2030 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2031 if (confflags & CONFFLAG_PASS_DTMF)
2032 conf_queue_dtmf(conf, user, f);
2033 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2034 ast_log(LOG_WARNING, "Error setting conference\n");
2040 /* if we are entering the menu, and the user has a channel-driver
2041 volume adjustment, clear it
2043 if (!menu_active && user->talk.desired && !user->talk.actual)
2044 set_talk_volume(user, 0);
2049 if ((confflags & CONFFLAG_ADMIN)) {
2053 /* Record this sound! */
2054 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2055 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2056 ast_stopstream(chan);
2063 case '1': /* Un/Mute */
2066 /* for admin, change both admin and use flags */
2067 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2068 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2070 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2072 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2073 if (!ast_streamfile(chan, "conf-muted", chan->language))
2074 ast_waitstream(chan, "");
2076 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2077 ast_waitstream(chan, "");
2080 case '2': /* Un/Lock the Conference */
2084 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2085 ast_waitstream(chan, "");
2088 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2089 ast_waitstream(chan, "");
2092 case '3': /* Eject last user */
2094 usr = AST_LIST_LAST(&conf->userlist);
2095 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2096 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2097 ast_waitstream(chan, "");
2099 usr->adminflags |= ADMINFLAG_KICKME;
2100 ast_stopstream(chan);
2103 tweak_listen_volume(user, VOL_DOWN);
2106 tweak_listen_volume(user, VOL_UP);
2109 tweak_talk_volume(user, VOL_DOWN);
2115 tweak_talk_volume(user, VOL_UP);
2119 /* Play an error message! */
2120 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2121 ast_waitstream(chan, "");
2129 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2130 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2131 ast_stopstream(chan);
2138 case '1': /* Un/Mute */
2141 /* user can only toggle the self-muted state */
2142 user->adminflags ^= ADMINFLAG_SELFMUTED;
2144 /* they can't override the admin mute state */
2145 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2146 if (!ast_streamfile(chan, "conf-muted", chan->language))
2147 ast_waitstream(chan, "");
2149 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2150 ast_waitstream(chan, "");
2154 tweak_listen_volume(user, VOL_DOWN);
2157 tweak_listen_volume(user, VOL_UP);
2160 tweak_talk_volume(user, VOL_DOWN);
2166 tweak_talk_volume(user, VOL_UP);
2170 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2171 ast_waitstream(chan, "");
2177 ast_moh_start(chan, NULL, NULL);
2179 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2180 ast_log(LOG_WARNING, "Error setting conference\n");
2186 conf_flush(fd, chan);
2187 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2188 && confflags & CONFFLAG_PASS_DTMF) {
2189 conf_queue_dtmf(conf, user, f);
2190 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2191 switch (f->subclass) {
2192 case AST_CONTROL_HOLD:
2193 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2198 } else if (f->frametype == AST_FRAME_NULL) {
2199 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2202 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2203 chan->name, f->frametype, f->subclass);
2206 } else if (outfd > -1) {
2207 res = read(outfd, buf, CONF_SIZE);
2209 memset(&fr, 0, sizeof(fr));
2210 fr.frametype = AST_FRAME_VOICE;
2211 fr.subclass = AST_FORMAT_SLINEAR;
2215 fr.offset = AST_FRIENDLY_OFFSET;
2216 if ( !user->listen.actual &&
2217 ((confflags & CONFFLAG_MONITOR) ||
2218 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2219 (!user->talking)) ) {
2221 for (index=0;index<AST_FRAME_BITS;index++)
2222 if (chan->rawwriteformat & (1 << index))
2224 if (index >= AST_FRAME_BITS)
2225 goto bailoutandtrynormal;
2226 ast_mutex_lock(&conf->listenlock);
2227 if (!conf->transframe[index]) {
2228 if (conf->origframe) {
2229 if (!conf->transpath[index])
2230 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2231 if (conf->transpath[index]) {
2232 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2233 if (!conf->transframe[index])
2234 conf->transframe[index] = &ast_null_frame;
2238 if (conf->transframe[index]) {
2239 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2240 if (ast_write(chan, conf->transframe[index]))
2241 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2244 ast_mutex_unlock(&conf->listenlock);
2245 goto bailoutandtrynormal;
2247 ast_mutex_unlock(&conf->listenlock);
2249 bailoutandtrynormal:
2250 if (user->listen.actual)
2251 ast_frame_adjust_volume(&fr, user->listen.actual);
2252 if (ast_write(chan, &fr) < 0) {
2253 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2257 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2259 lastmarked = currentmarked;
2269 /* Take out of conference */
2273 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2274 ast_log(LOG_WARNING, "Error setting conference\n");
2278 reset_volumes(user);
2280 AST_LIST_LOCK(&confs);
2281 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2282 conf_play(chan, conf, LEAVE);
2284 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2285 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2286 if ((conf->chan) && (conf->users > 1)) {
2287 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2288 ast_waitstream(conf->chan, "");
2289 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2290 ast_waitstream(conf->chan, "");
2292 ast_filedelete(user->namerecloc, NULL);
2295 AST_LIST_UNLOCK(&confs);
2298 AST_LIST_LOCK(&confs);
2303 if (user->user_no) { /* Only cleanup users who really joined! */
2305 hr = (now - user->jointime) / 3600;
2306 min = ((now - user->jointime) % 3600) / 60;
2307 sec = (now - user->jointime) % 60;
2310 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2315 "CallerIDNum: %s\r\n"
2316 "CallerIDName: %s\r\n"
2317 "Duration: %ld\r\n",
2318 chan->name, chan->uniqueid, conf->confno,
2320 S_OR(user->chan->cid.cid_num, "<unknown>"),
2321 S_OR(user->chan->cid.cid_name, "<unknown>"),
2322 (long)(now - user->jointime));
2327 snprintf(members, sizeof(members), "%d", conf->users);
2328 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2329 if (confflags & CONFFLAG_MARKEDUSER)
2330 conf->markedusers--;
2331 /* Remove ourselves from the list */
2332 AST_LIST_REMOVE(&conf->userlist, user, list);
2334 /* Change any states */
2336 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2338 /* Return the number of seconds the user was in the conf */
2339 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2340 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2343 AST_LIST_UNLOCK(&confs);
2348 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2349 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2351 struct ast_variable *var;
2352 struct ast_conference *cnf;
2354 /* Check first in the conference list */
2355 AST_LIST_LOCK(&confs);
2356 AST_LIST_TRAVERSE(&confs, cnf, list) {
2357 if (!strcmp(confno, cnf->confno))
2361 cnf->refcount += refcount;
2363 AST_LIST_UNLOCK(&confs);
2366 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2368 var = ast_load_realtime("meetme", "confno", confno, NULL);
2374 if (!strcasecmp(var->name, "pin")) {
2375 pin = ast_strdupa(var->value);
2376 } else if (!strcasecmp(var->name, "adminpin")) {
2377 pinadmin = ast_strdupa(var->value);
2381 ast_variables_destroy(var);
2383 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2387 if (confflags && !cnf->chan &&
2388 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2389 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2390 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2391 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2394 if (confflags && !cnf->chan &&
2395 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2396 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2397 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2405 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2406 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2408 struct ast_config *cfg;
2409 struct ast_variable *var;
2410 struct ast_conference *cnf;
2412 AST_DECLARE_APP_ARGS(args,
2413 AST_APP_ARG(confno);
2415 AST_APP_ARG(pinadmin);
2418 /* Check first in the conference list */
2419 ast_log(LOG_NOTICE,"The requested confno is '%s'?\n", confno);
2420 AST_LIST_LOCK(&confs);
2421 AST_LIST_TRAVERSE(&confs, cnf, list) {
2422 ast_log(LOG_NOTICE,"Does conf %s match %s?\n", confno, cnf->confno);
2423 if (!strcmp(confno, cnf->confno))
2427 cnf->refcount += refcount;
2429 AST_LIST_UNLOCK(&confs);
2433 /* No need to parse meetme.conf */
2434 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2436 if (dynamic_pin[0] == 'q') {
2437 /* Query the user to enter a PIN */
2438 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2441 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2443 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2446 /* Check the config */
2447 cfg = ast_config_load(CONFIG_FILE_NAME);
2449 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2452 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2453 if (strcasecmp(var->name, "conf"))
2456 if (!(parse = ast_strdupa(var->value)))
2459 AST_STANDARD_APP_ARGS(args, parse);
2460 ast_log(LOG_NOTICE,"Will conf %s match %s?\n", confno, args.confno);
2461 if (!strcasecmp(args.confno, confno)) {
2462 /* Bingo it's a valid conference */
2463 cnf = build_conf(args.confno,
2465 S_OR(args.pinadmin, ""),
2466 make, dynamic, refcount, chan);
2471 ast_debug(1, "%s isn't a valid conference\n", confno);
2473 ast_config_destroy(cfg);
2475 } else if (dynamic_pin) {
2476 /* Correct for the user selecting 'D' instead of 'd' to have
2477 someone join into a conference that has already been created
2479 if (dynamic_pin[0] == 'q')
2480 dynamic_pin[0] = '\0';
2484 if (confflags && !cnf->chan &&
2485 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2486 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2487 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2488 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2491 if (confflags && !cnf->chan &&
2492 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2493 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2494 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2501 /*! \brief The MeetmeCount application */
2502 static int count_exec(struct ast_channel *chan, void *data)
2505 struct ast_conference *conf;
2509 AST_DECLARE_APP_ARGS(args,
2510 AST_APP_ARG(confno);
2511 AST_APP_ARG(varname);
2514 if (ast_strlen_zero(data)) {
2515 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2519 if (!(localdata = ast_strdupa(data)))
2522 AST_STANDARD_APP_ARGS(args, localdata);
2524 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2527 count = conf->users;
2533 if (!ast_strlen_zero(args.varname)){
2534 /* have var so load it and exit */
2535 snprintf(val, sizeof(val), "%d",count);
2536 pbx_builtin_setvar_helper(chan, args.varname, val);
2538 if (chan->_state != AST_STATE_UP)
2540 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2546 /*! \brief The meetme() application */
2547 static int conf_exec(struct ast_channel *chan, void *data)
2550 char confno[MAX_CONFNUM] = "";
2553 struct ast_conference *cnf = NULL;
2554 struct ast_flags confflags = {0};
2556 int empty = 0, empty_no_pin = 0;
2557 int always_prompt = 0;
2558 char *notdata, *info, the_pin[MAX_PIN] = "";
2559 AST_DECLARE_APP_ARGS(args,
2560 AST_APP_ARG(confno);
2561 AST_APP_ARG(options);
2564 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2566 if (ast_strlen_zero(data)) {
2573 if (chan->_state != AST_STATE_UP)
2576 info = ast_strdupa(notdata);
2578 AST_STANDARD_APP_ARGS(args, info);
2581 ast_copy_string(confno, args.confno, sizeof(confno));
2582 if (ast_strlen_zero(confno)) {
2588 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2591 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2592 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2593 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2594 strcpy(the_pin, "q");
2596 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2597 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2598 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2606 struct ast_config *cfg;
2607 struct ast_variable *var;
2610 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2611 if ((empty_no_pin) || (!dynamic)) {
2612 cfg = ast_config_load(CONFIG_FILE_NAME);
2614 var = ast_variable_browse(cfg, "rooms");
2616 if (!strcasecmp(var->name, "conf")) {
2617 char *stringp = ast_strdupa(var->value);
2619 char *confno_tmp = strsep(&stringp, "|,");
2622 /* For static: run through the list and see if this conference is empty */
2623 AST_LIST_LOCK(&confs);
2624 AST_LIST_TRAVERSE(&confs, cnf, list) {
2625 if (!strcmp(confno_tmp, cnf->confno)) {
2626 /* The conference exists, therefore it's not empty */
2631 AST_LIST_UNLOCK(&confs);
2633 /* At this point, we have a confno_tmp (static conference) that is empty */
2634 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2635 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2636 * Case 2: empty_no_pin and pin is blank (but not NULL)
2637 * Case 3: not empty_no_pin
2639 ast_copy_string(confno, confno_tmp, sizeof(confno));
2641 /* XXX the map is not complete (but we do have a confno) */
2649 ast_config_destroy(cfg);
2653 /* Select first conference number not in use */
2654 if (ast_strlen_zero(confno) && dynamic) {
2655 AST_LIST_LOCK(&confs);
2656 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2658 snprintf(confno, sizeof(confno), "%d", i);
2663 AST_LIST_UNLOCK(&confs);
2667 if (ast_strlen_zero(confno)) {
2668 res = ast_streamfile(chan, "conf-noempty", chan->language);
2670 ast_waitstream(chan, "");
2672 if (sscanf(confno, "%d", &confno_int) == 1) {
2673 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2674 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2676 ast_waitstream(chan, "");
2677 res = ast_say_digits(chan, confno_int, "", chan->language);
2681 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2686 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2687 /* Prompt user for conference number */
2688 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2690 /* Don't try to validate when we catch an error */
2696 if (!ast_strlen_zero(confno)) {
2697 /* Check the validity of the conference */
2698 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2699 sizeof(the_pin), 1, &confflags);
2701 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2702 the_pin, sizeof(the_pin), 1, &confflags);
2706 res = ast_streamfile(chan, "conf-invalid", chan->language);
2708 ast_waitstream(chan, "");
2713 if ((!ast_strlen_zero(cnf->pin) &&
2714 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2715 (!ast_strlen_zero(cnf->pinadmin) &&
2716 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2717 char pin[MAX_PIN] = "";
2720 /* Allow the pin to be retried up to 3 times */
2721 for (j = 0; j < 3; j++) {
2722 if (*the_pin && (always_prompt == 0)) {
2723 ast_copy_string(pin, the_pin, sizeof(pin));
2726 /* Prompt user for pin if pin is required */
2727 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2730 if (!strcasecmp(pin, cnf->pin) ||
2731 (!ast_strlen_zero(cnf->pinadmin) &&
2732 !strcasecmp(pin, cnf->pinadmin))) {
2735 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))