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. DTMF used to activate any\n"
234 " conference features will not be passed through.\n"
235 " 'i' -- announce user join/leave with review\n"
236 " 'I' -- announce user join/leave without review\n"
237 " 'l' -- set listen only mode (Listen only, no talking)\n"
238 " 'm' -- set initially muted\n"
239 " 'M' -- enable music on hold when the conference has a single caller\n"
240 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
241 " being muted, meaning (a) No encode is done on transmission and\n"
242 " (b) Received audio that is not registered as talking is omitted\n"
243 " causing no buildup in background noise\n"
245 " -- allow user to exit the conference by pressing '#' (default)\n"
246 " or any of the defined keys. If keys contain '*' this will override\n"
247 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
248 " 'P' -- always prompt for the pin even if it is specified\n"
249 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
250 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
251 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
252 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
254 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
255 " 't' -- set talk only mode. (Talk only, no listening)\n"
256 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
258 " -- wait until the marked user enters the conference\n"
259 " 'x' -- close the conference when last marked user exits\n"
260 " 'X' -- allow user to exit the conference by entering a valid single\n"
261 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
262 " if that variable is not defined.\n"
263 " '1' -- do not play message when first person enters\n";
265 static const char *descrip2 =
266 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
267 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
268 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
269 "the channel, unless priority n+1 exists, in which case priority progress will\n"
271 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
273 static const char *descrip3 =
274 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
275 " 'e' -- Eject last user that joined\n"
276 " 'k' -- Kick one user out of conference\n"
277 " 'K' -- Kick all users out of conference\n"
278 " 'l' -- Unlock conference\n"
279 " 'L' -- Lock conference\n"
280 " 'm' -- Unmute one user\n"
281 " 'M' -- Mute one user\n"
282 " 'n' -- Unmute all users in the conference\n"
283 " 'N' -- Mute all non-admin users in the conference\n"
284 " 'r' -- Reset one user's volume settings\n"
285 " 'R' -- Reset all users volume settings\n"
286 " 's' -- Lower entire conference speaking volume\n"
287 " 'S' -- Raise entire conference speaking volume\n"
288 " 't' -- Lower one user's talk volume\n"
289 " 'T' -- Raise one user's talk volume\n"
290 " 'u' -- Lower one user's listen volume\n"
291 " 'U' -- Raise one user's listen volume\n"
292 " 'v' -- Lower entire conference listening volume\n"
293 " 'V' -- Raise entire conference listening volume\n"
296 static const char *descrip4 =
297 " MeetMeChannelAdmin(channel|command): Run admin command for a specific\n"
298 "channel in any coference.\n"
299 " 'k' -- Kick the specified user out of the conference he is in\n"
300 " 'm' -- Unmute the specified user\n"
301 " 'M' -- Mute the specified user\n"
304 static const char *slastation_desc =
305 " SLAStation(station):\n"
306 "This application should be executed by an SLA station. The argument depends\n"
307 "on how the call was initiated. If the phone was just taken off hook, then\n"
308 "the argument \"station\" should be just the station name. If the call was\n"
309 "initiated by pressing a line key, then the station name should be preceded\n"
310 "by an underscore and the trunk name associated with that line button.\n"
311 "For example: \"station1_line1\"."
312 " On exit, this application will set the variable SLASTATION_STATUS to\n"
313 "one of the following values:\n"
314 " FAILURE | CONGESTION | SUCCESS\n"
317 static const char *slatrunk_desc =
318 " SLATrunk(trunk):\n"
319 "This application should be executed by an SLA trunk on an inbound call.\n"
320 "The channel calling this application should correspond to the SLA trunk\n"
321 "with the name \"trunk\" that is being passed as an argument.\n"
322 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
323 "one of the following values:\n"
324 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
327 #define MAX_CONFNUM 80
330 /*! \brief The MeetMe Conference object */
331 struct ast_conference {
332 ast_mutex_t playlock; /*!< Conference specific lock (players) */
333 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
334 char confno[MAX_CONFNUM]; /*!< Conference */
335 struct ast_channel *chan; /*!< Announcements channel */
336 struct ast_channel *lchan; /*!< Listen/Record channel */
337 int fd; /*!< Announcements fd */
338 int zapconf; /*!< Zaptel Conf # */
339 int users; /*!< Number of active users */
340 int markedusers; /*!< Number of marked users */
341 time_t start; /*!< Start time (s) */
342 int refcount; /*!< reference count of usage */
343 enum recording_state recording:2; /*!< recording status */
344 unsigned int isdynamic:1; /*!< Created on the fly? */
345 unsigned int locked:1; /*!< Is the conference locked? */
346 pthread_t recordthread; /*!< thread for recording */
347 const char *recordingfilename; /*!< Filename to record the Conference into */
348 const char *recordingformat; /*!< Format to record the Conference in */
349 char pin[MAX_PIN]; /*!< If protected by a PIN */
350 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
352 struct ast_frame *transframe[32];
353 struct ast_frame *origframe;
354 struct ast_trans_pvt *transpath[32];
355 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
356 AST_LIST_ENTRY(ast_conference) list;
359 static AST_LIST_HEAD_STATIC(confs, ast_conference);
361 static unsigned int conf_map[1024] = {0, };
364 int desired; /*!< Desired volume adjustment */
365 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
368 /*! \brief The MeetMe User object */
369 struct ast_conf_user {
370 int user_no; /*!< User Number */
371 int userflags; /*!< Flags as set in the conference */
372 int adminflags; /*!< Flags set by the Admin */
373 struct ast_channel *chan; /*!< Connected channel */
374 int talking; /*!< Is user talking */
375 int zapchannel; /*!< Is a Zaptel channel */
376 char usrvalue[50]; /*!< Custom User Value */
377 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
378 time_t jointime; /*!< Time the user joined the conference */
380 struct volume listen;
381 AST_LIST_ENTRY(ast_conf_user) list;
384 enum sla_which_trunk_refs {
389 enum sla_trunk_state {
390 SLA_TRUNK_STATE_IDLE,
391 SLA_TRUNK_STATE_RINGING,
393 SLA_TRUNK_STATE_ONHOLD,
394 SLA_TRUNK_STATE_ONHOLD_BYME,
397 enum sla_hold_access {
398 /*! This means that any station can put it on hold, and any station
399 * can retrieve the call from hold. */
401 /*! This means that only the station that put the call on hold may
402 * retrieve it from hold. */
406 struct sla_trunk_ref;
409 AST_RWLIST_ENTRY(sla_station) entry;
410 AST_DECLARE_STRING_FIELDS(
411 AST_STRING_FIELD(name);
412 AST_STRING_FIELD(device);
413 AST_STRING_FIELD(autocontext);
415 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
416 struct ast_dial *dial;
417 /*! Ring timeout for this station, for any trunk. If a ring timeout
418 * is set for a specific trunk on this station, that will take
419 * priority over this value. */
420 unsigned int ring_timeout;
421 /*! Ring delay for this station, for any trunk. If a ring delay
422 * is set for a specific trunk on this station, that will take
423 * priority over this value. */
424 unsigned int ring_delay;
425 /*! This option uses the values in the sla_hold_access enum and sets the
426 * access control type for hold on this station. */
427 unsigned int hold_access:1;
428 /*! Use count for inside sla_station_exec */
429 unsigned int ref_count;
432 struct sla_station_ref {
433 AST_LIST_ENTRY(sla_station_ref) entry;
434 struct sla_station *station;
438 AST_RWLIST_ENTRY(sla_trunk) entry;
439 AST_DECLARE_STRING_FIELDS(
440 AST_STRING_FIELD(name);
441 AST_STRING_FIELD(device);
442 AST_STRING_FIELD(autocontext);
444 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
445 /*! Number of stations that use this trunk */
446 unsigned int num_stations;
447 /*! Number of stations currently on a call with this trunk */
448 unsigned int active_stations;
449 /*! Number of stations that have this trunk on hold. */
450 unsigned int hold_stations;
451 struct ast_channel *chan;
452 unsigned int ring_timeout;
453 /*! If set to 1, no station will be able to join an active call with
455 unsigned int barge_disabled:1;
456 /*! This option uses the values in the sla_hold_access enum and sets the
457 * access control type for hold on this trunk. */
458 unsigned int hold_access:1;
459 /*! Whether this trunk is currently on hold, meaning that once a station
460 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
461 unsigned int on_hold:1;
462 /*! Use count for inside sla_trunk_exec */
463 unsigned int ref_count;
466 struct sla_trunk_ref {
467 AST_LIST_ENTRY(sla_trunk_ref) entry;
468 struct sla_trunk *trunk;
469 enum sla_trunk_state state;
470 struct ast_channel *chan;
471 /*! Ring timeout to use when this trunk is ringing on this specific
472 * station. This takes higher priority than a ring timeout set at
473 * the station level. */
474 unsigned int ring_timeout;
475 /*! Ring delay to use when this trunk is ringing on this specific
476 * station. This takes higher priority than a ring delay set at
477 * the station level. */
478 unsigned int ring_delay;
481 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
482 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
484 static const char sla_registrar[] = "SLA";
486 /*! \brief Event types that can be queued up for the SLA thread */
487 enum sla_event_type {
488 /*! A station has put the call on hold */
490 /*! The state of a dial has changed */
491 SLA_EVENT_DIAL_STATE,
492 /*! The state of a ringing trunk has changed */
493 SLA_EVENT_RINGING_TRUNK,
494 /*! A reload of configuration has been requested */
496 /*! Poke the SLA thread so it can check if it can perform a reload */
497 SLA_EVENT_CHECK_RELOAD,
501 enum sla_event_type type;
502 struct sla_station *station;
503 struct sla_trunk_ref *trunk_ref;
504 AST_LIST_ENTRY(sla_event) entry;
507 /*! \brief A station that failed to be dialed
508 * \note Only used by the SLA thread. */
509 struct sla_failed_station {
510 struct sla_station *station;
511 struct timeval last_try;
512 AST_LIST_ENTRY(sla_failed_station) entry;
515 /*! \brief A trunk that is ringing */
516 struct sla_ringing_trunk {
517 struct sla_trunk *trunk;
518 /*! The time that this trunk started ringing */
519 struct timeval ring_begin;
520 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
521 AST_LIST_ENTRY(sla_ringing_trunk) entry;
524 enum sla_station_hangup {
525 SLA_STATION_HANGUP_NORMAL,
526 SLA_STATION_HANGUP_TIMEOUT,
529 /*! \brief A station that is ringing */
530 struct sla_ringing_station {
531 struct sla_station *station;
532 /*! The time that this station started ringing */
533 struct timeval ring_begin;
534 AST_LIST_ENTRY(sla_ringing_station) entry;
538 * \brief A structure for data used by the sla thread
541 /*! The SLA thread ID */
545 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
546 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
547 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
548 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
550 /*! Attempt to handle CallerID, even though it is known not to work
551 * properly in some situations. */
552 unsigned int attempt_callerid:1;
553 /*! A reload has been requested */
554 unsigned int reload:1;
556 .thread = AST_PTHREADT_NULL,
559 /*! The number of audio buffers to be allocated on pseudo channels
560 * when in a conference */
561 static int audio_buffers;
563 /*! Map 'volume' levels from -5 through +5 into
564 * decibel (dB) settings for channel drivers
565 * Note: these are not a straight linear-to-dB
566 * conversion... the numbers have been modified
567 * to give the user a better level of adjustability
569 static char const gain_map[] = {
584 static int admin_exec(struct ast_channel *chan, void *data);
585 static void *recordthread(void *args);
587 static char *istalking(int x)
592 return "(unmonitored)";
594 return "(not talking)";
597 static int careful_write(int fd, unsigned char *data, int len, int block)
604 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
605 res = ioctl(fd, ZT_IOMUX, &x);
609 res = write(fd, data, len);
611 if (errno != EAGAIN) {
612 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
624 static int set_talk_volume(struct ast_conf_user *user, int volume)
628 /* attempt to make the adjustment in the channel driver;
629 if successful, don't adjust in the frame reading routine
631 gain_adjust = gain_map[volume + 5];
633 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
636 static int set_listen_volume(struct ast_conf_user *user, int volume)
640 /* attempt to make the adjustment in the channel driver;
641 if successful, don't adjust in the frame reading routine
643 gain_adjust = gain_map[volume + 5];
645 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
648 static void tweak_volume(struct volume *vol, enum volume_action action)
652 switch (vol->desired) {
667 switch (vol->desired) {
683 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
685 tweak_volume(&user->talk, action);
686 /* attempt to make the adjustment in the channel driver;
687 if successful, don't adjust in the frame reading routine
689 if (!set_talk_volume(user, user->talk.desired))
690 user->talk.actual = 0;
692 user->talk.actual = user->talk.desired;
695 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
697 tweak_volume(&user->listen, action);
698 /* attempt to make the adjustment in the channel driver;
699 if successful, don't adjust in the frame reading routine
701 if (!set_listen_volume(user, user->listen.desired))
702 user->listen.actual = 0;
704 user->listen.actual = user->listen.desired;
707 static void reset_volumes(struct ast_conf_user *user)
709 signed char zero_volume = 0;
711 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
712 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
715 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
721 if (!chan->_softhangup)
722 res = ast_autoservice_start(chan);
724 AST_LIST_LOCK(&confs);
740 careful_write(conf->fd, data, len, 1);
743 AST_LIST_UNLOCK(&confs);
746 ast_autoservice_stop(chan);
750 * \brief Find or create a conference
752 * \param confno The conference name/number
753 * \param pin The regular user pin
754 * \param pinadmin The admin pin
755 * \param make Make the conf if it doesn't exist
756 * \param dynamic Mark the newly created conference as dynamic
757 * \param refcount How many references to mark on the conference
758 * \param chan The asterisk channel
760 * \return A pointer to the conference struct, or NULL if it wasn't found and
761 * make or dynamic were not set.
763 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
765 struct ast_conference *cnf;
766 struct zt_confinfo ztc = { 0, };
769 AST_LIST_LOCK(&confs);
771 AST_LIST_TRAVERSE(&confs, cnf, list) {
772 if (!strcmp(confno, cnf->confno))
776 if (cnf || (!make && !dynamic))
780 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
783 ast_mutex_init(&cnf->playlock);
784 ast_mutex_init(&cnf->listenlock);
785 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
786 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
787 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
788 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
789 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
791 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
792 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
793 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
795 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
796 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
798 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
805 /* Setup a new zap conference */
807 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
808 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
809 ast_log(LOG_WARNING, "Error setting conference\n");
811 ast_hangup(cnf->chan);
818 /* Fill the conference struct */
819 cnf->start = time(NULL);
820 cnf->zapconf = ztc.confno;
821 cnf->isdynamic = dynamic ? 1 : 0;
822 if (option_verbose > 2)
823 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
824 AST_LIST_INSERT_HEAD(&confs, cnf, list);
826 /* Reserve conference number in map */
827 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
828 conf_map[confno_int] = 1;
832 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
834 AST_LIST_UNLOCK(&confs);
839 static int meetme_cmd(int fd, int argc, char **argv)
841 /* Process the command */
842 struct ast_conference *cnf;
843 struct ast_conf_user *user;
845 int i = 0, total = 0;
847 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
848 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
849 char cmdline[1024] = "";
852 ast_cli(fd, "Invalid Arguments.\n");
853 /* Check for length so no buffer will overflow... */
854 for (i = 0; i < argc; i++) {
855 if (strlen(argv[i]) > 100)
856 ast_cli(fd, "Invalid Arguments.\n");
859 /* 'MeetMe': List all the conferences */
861 AST_LIST_LOCK(&confs);
862 if (AST_LIST_EMPTY(&confs)) {
863 ast_cli(fd, "No active MeetMe conferences.\n");
864 AST_LIST_UNLOCK(&confs);
865 return RESULT_SUCCESS;
867 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
868 AST_LIST_TRAVERSE(&confs, cnf, list) {
869 if (cnf->markedusers == 0)
870 strcpy(cmdline, "N/A ");
872 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
873 hr = (now - cnf->start) / 3600;
874 min = ((now - cnf->start) % 3600) / 60;
875 sec = (now - cnf->start) % 60;
877 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
881 AST_LIST_UNLOCK(&confs);
882 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
883 return RESULT_SUCCESS;
886 return RESULT_SHOWUSAGE;
887 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
888 if (strstr(argv[1], "lock")) {
889 if (strcmp(argv[1], "lock") == 0) {
891 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
894 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
896 } else if (strstr(argv[1], "mute")) {
898 return RESULT_SHOWUSAGE;
899 if (strcmp(argv[1], "mute") == 0) {
901 if (strcmp(argv[3], "all") == 0) {
902 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
904 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
905 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
909 if (strcmp(argv[3], "all") == 0) {
910 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
912 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
913 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
916 } else if (strcmp(argv[1], "kick") == 0) {
918 return RESULT_SHOWUSAGE;
919 if (strcmp(argv[3], "all") == 0) {
921 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
923 /* Kick a single user */
924 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
925 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
927 } else if(strcmp(argv[1], "list") == 0) {
928 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
929 /* List all the users in a conference */
930 if (AST_LIST_EMPTY(&confs)) {
932 ast_cli(fd, "No active conferences.\n");
933 return RESULT_SUCCESS;
935 /* Find the right conference */
936 AST_LIST_LOCK(&confs);
937 AST_LIST_TRAVERSE(&confs, cnf, list) {
938 if (strcmp(cnf->confno, argv[2]) == 0)
943 ast_cli(fd, "No such conference: %s.\n",argv[2]);
944 AST_LIST_UNLOCK(&confs);
945 return RESULT_SUCCESS;
947 /* Show all the users */
949 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
950 hr = (now - user->jointime) / 3600;
951 min = ((now - user->jointime) % 3600) / 60;
952 sec = (now - user->jointime) % 60;
954 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
956 S_OR(user->chan->cid.cid_num, "<unknown>"),
957 S_OR(user->chan->cid.cid_name, "<no name>"),
959 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
960 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
961 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
962 istalking(user->talking), hr, min, sec);
964 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
966 S_OR(user->chan->cid.cid_num, ""),
967 S_OR(user->chan->cid.cid_name, ""),
969 user->userflags & CONFFLAG_ADMIN ? "1" : "",
970 user->userflags & CONFFLAG_MONITOR ? "1" : "",
971 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
972 user->talking, hr, min, sec);
976 ast_cli(fd,"%d users in that conference.\n",cnf->users);
977 AST_LIST_UNLOCK(&confs);
978 return RESULT_SUCCESS;
980 return RESULT_SHOWUSAGE;
982 ast_debug(1, "Cmdline: %s\n", cmdline);
984 admin_exec(NULL, cmdline);
989 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
991 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
993 int len = strlen(word);
995 struct ast_conference *cnf = NULL;
996 struct ast_conf_user *usr = NULL;
999 char *myline, *ret = NULL;
1001 if (pos == 1) { /* Command */
1002 return ast_cli_complete(word, cmds, state);
1003 } else if (pos == 2) { /* Conference Number */
1004 AST_LIST_LOCK(&confs);
1005 AST_LIST_TRAVERSE(&confs, cnf, list) {
1006 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1011 ret = ast_strdup(ret); /* dup before releasing the lock */
1012 AST_LIST_UNLOCK(&confs);
1014 } else if (pos == 3) {
1015 /* User Number || Conf Command option*/
1016 if (strstr(line, "mute") || strstr(line, "kick")) {
1017 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
1018 return ast_strdup("all");
1020 AST_LIST_LOCK(&confs);
1022 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1023 myline = ast_strdupa(line);
1024 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1025 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1029 AST_LIST_TRAVERSE(&confs, cnf, list) {
1030 if (!strcmp(confno, cnf->confno))
1035 /* Search for the user */
1036 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1037 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1038 if (!strncasecmp(word, usrno, len) && ++which > state)
1042 AST_LIST_UNLOCK(&confs);
1043 return usr ? ast_strdup(usrno) : NULL;
1044 } else if ( strstr(line, "list") && ( 0 == state ) )
1045 return ast_strdup("concise");
1051 static const char meetme_usage[] =
1052 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1053 " Executes a command for the conference or on a conferee\n";
1055 static const char *sla_hold_str(unsigned int hold_access)
1057 const char *hold = "Unknown";
1059 switch (hold_access) {
1063 case SLA_HOLD_PRIVATE:
1072 static int sla_show_trunks(int fd, int argc, char **argv)
1074 const struct sla_trunk *trunk;
1077 "=============================================================\n"
1078 "=== Configured SLA Trunks ===================================\n"
1079 "=============================================================\n"
1081 AST_RWLIST_RDLOCK(&sla_trunks);
1082 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1083 struct sla_station_ref *station_ref;
1084 char ring_timeout[16] = "(none)";
1085 if (trunk->ring_timeout)
1086 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1087 ast_cli(fd, "=== ---------------------------------------------------------\n"
1088 "=== Trunk Name: %s\n"
1089 "=== ==> Device: %s\n"
1090 "=== ==> AutoContext: %s\n"
1091 "=== ==> RingTimeout: %s\n"
1092 "=== ==> BargeAllowed: %s\n"
1093 "=== ==> HoldAccess: %s\n"
1094 "=== ==> Stations ...\n",
1095 trunk->name, trunk->device,
1096 S_OR(trunk->autocontext, "(none)"),
1098 trunk->barge_disabled ? "No" : "Yes",
1099 sla_hold_str(trunk->hold_access));
1100 AST_RWLIST_RDLOCK(&sla_stations);
1101 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1102 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1103 AST_RWLIST_UNLOCK(&sla_stations);
1104 ast_cli(fd, "=== ---------------------------------------------------------\n"
1107 AST_RWLIST_UNLOCK(&sla_trunks);
1108 ast_cli(fd, "=============================================================\n"
1111 return RESULT_SUCCESS;
1114 static const char *trunkstate2str(enum sla_trunk_state state)
1116 #define S(e) case e: return # e;
1118 S(SLA_TRUNK_STATE_IDLE)
1119 S(SLA_TRUNK_STATE_RINGING)
1120 S(SLA_TRUNK_STATE_UP)
1121 S(SLA_TRUNK_STATE_ONHOLD)
1122 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1124 return "Uknown State";
1128 static const char sla_show_trunks_usage[] =
1129 "Usage: sla show trunks\n"
1130 " This will list all trunks defined in sla.conf\n";
1132 static int sla_show_stations(int fd, int argc, char **argv)
1134 const struct sla_station *station;
1137 "=============================================================\n"
1138 "=== Configured SLA Stations =================================\n"
1139 "=============================================================\n"
1141 AST_RWLIST_RDLOCK(&sla_stations);
1142 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1143 struct sla_trunk_ref *trunk_ref;
1144 char ring_timeout[16] = "(none)";
1145 char ring_delay[16] = "(none)";
1146 if (station->ring_timeout) {
1147 snprintf(ring_timeout, sizeof(ring_timeout),
1148 "%u", station->ring_timeout);
1150 if (station->ring_delay) {
1151 snprintf(ring_delay, sizeof(ring_delay),
1152 "%u", station->ring_delay);
1154 ast_cli(fd, "=== ---------------------------------------------------------\n"
1155 "=== Station Name: %s\n"
1156 "=== ==> Device: %s\n"
1157 "=== ==> AutoContext: %s\n"
1158 "=== ==> RingTimeout: %s\n"
1159 "=== ==> RingDelay: %s\n"
1160 "=== ==> HoldAccess: %s\n"
1161 "=== ==> Trunks ...\n",
1162 station->name, station->device,
1163 S_OR(station->autocontext, "(none)"),
1164 ring_timeout, ring_delay,
1165 sla_hold_str(station->hold_access));
1166 AST_RWLIST_RDLOCK(&sla_trunks);
1167 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1168 if (trunk_ref->ring_timeout) {
1169 snprintf(ring_timeout, sizeof(ring_timeout),
1170 "%u", trunk_ref->ring_timeout);
1172 strcpy(ring_timeout, "(none)");
1173 if (trunk_ref->ring_delay) {
1174 snprintf(ring_delay, sizeof(ring_delay),
1175 "%u", trunk_ref->ring_delay);
1177 strcpy(ring_delay, "(none)");
1178 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1179 "=== ==> State: %s\n"
1180 "=== ==> RingTimeout: %s\n"
1181 "=== ==> RingDelay: %s\n",
1182 trunk_ref->trunk->name,
1183 trunkstate2str(trunk_ref->state),
1184 ring_timeout, ring_delay);
1186 AST_RWLIST_UNLOCK(&sla_trunks);
1187 ast_cli(fd, "=== ---------------------------------------------------------\n"
1190 AST_RWLIST_UNLOCK(&sla_stations);
1191 ast_cli(fd, "============================================================\n"
1194 return RESULT_SUCCESS;
1197 static const char sla_show_stations_usage[] =
1198 "Usage: sla show stations\n"
1199 " This will list all stations defined in sla.conf\n";
1201 static struct ast_cli_entry cli_meetme[] = {
1202 { { "meetme", NULL, NULL },
1203 meetme_cmd, "Execute a command on a conference or conferee",
1204 meetme_usage, complete_meetmecmd },
1206 { { "sla", "show", "trunks", NULL },
1207 sla_show_trunks, "Show SLA Trunks",
1208 sla_show_trunks_usage, NULL },
1210 { { "sla", "show", "stations", NULL },
1211 sla_show_stations, "Show SLA Stations",
1212 sla_show_stations_usage, NULL },
1215 static void conf_flush(int fd, struct ast_channel *chan)
1219 /* read any frames that may be waiting on the channel
1223 struct ast_frame *f;
1225 /* when no frames are available, this will wait
1226 for 1 millisecond maximum
1228 while (ast_waitfor(chan, 1)) {
1232 else /* channel was hung up or something else happened */
1237 /* flush any data sitting in the pseudo channel */
1239 if (ioctl(fd, ZT_FLUSH, &x))
1240 ast_log(LOG_WARNING, "Error flushing channel\n");
1244 /* Remove the conference from the list and free it.
1245 We assume that this was called while holding conflock. */
1246 static int conf_free(struct ast_conference *conf)
1250 AST_LIST_REMOVE(&confs, conf, list);
1251 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1253 if (conf->recording == MEETME_RECORD_ACTIVE) {
1254 conf->recording = MEETME_RECORD_TERMINATE;
1255 AST_LIST_UNLOCK(&confs);
1258 AST_LIST_LOCK(&confs);
1259 if (conf->recording == MEETME_RECORD_OFF)
1261 AST_LIST_UNLOCK(&confs);
1265 for (x=0;x<AST_FRAME_BITS;x++) {
1266 if (conf->transframe[x])
1267 ast_frfree(conf->transframe[x]);
1268 if (conf->transpath[x])
1269 ast_translator_free_path(conf->transpath[x]);
1271 if (conf->origframe)
1272 ast_frfree(conf->origframe);
1274 ast_hangup(conf->lchan);
1276 ast_hangup(conf->chan);
1285 static void conf_queue_dtmf(const struct ast_conference *conf,
1286 const struct ast_conf_user *sender, struct ast_frame *f)
1288 struct ast_conf_user *user;
1290 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1293 if (ast_write(user->chan, f) < 0)
1294 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1298 static void sla_queue_event_full(enum sla_event_type type,
1299 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1301 struct sla_event *event;
1303 if (!(event = ast_calloc(1, sizeof(*event))))
1307 event->trunk_ref = trunk_ref;
1308 event->station = station;
1311 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1315 ast_mutex_lock(&sla.lock);
1316 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1317 ast_cond_signal(&sla.cond);
1318 ast_mutex_unlock(&sla.lock);
1321 static void sla_queue_event_nolock(enum sla_event_type type)
1323 sla_queue_event_full(type, NULL, NULL, 0);
1326 static void sla_queue_event(enum sla_event_type type)
1328 sla_queue_event_full(type, NULL, NULL, 1);
1331 /*! \brief Queue a SLA event from the conference */
1332 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1333 struct ast_conference *conf)
1335 struct sla_station *station;
1336 struct sla_trunk_ref *trunk_ref = NULL;
1339 trunk_name = ast_strdupa(conf->confno);
1340 strsep(&trunk_name, "_");
1341 if (ast_strlen_zero(trunk_name)) {
1342 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1346 AST_RWLIST_RDLOCK(&sla_stations);
1347 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1348 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1349 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1355 AST_RWLIST_UNLOCK(&sla_stations);
1358 ast_debug(1, "Trunk not found for event!\n");
1362 sla_queue_event_full(type, trunk_ref, station, 1);
1365 /* Decrement reference counts, as incremented by find_conf() */
1366 static int dispose_conf(struct ast_conference *conf)
1371 AST_LIST_LOCK(&confs);
1372 if (ast_atomic_dec_and_test(&conf->refcount)) {
1373 /* Take the conference room number out of an inuse state */
1374 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1375 conf_map[confno_int] = 0;
1379 AST_LIST_UNLOCK(&confs);
1385 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1387 struct ast_conf_user *user = NULL;
1388 struct ast_conf_user *usr = NULL;
1390 struct zt_confinfo ztc, ztc_empty;
1391 struct ast_frame *f;
1392 struct ast_channel *c;
1393 struct ast_frame fr;
1401 int musiconhold = 0;
1404 int currentmarked = 0;
1407 int menu_active = 0;
1408 int using_pseudo = 0;
1413 struct ast_dsp *dsp=NULL;
1414 struct ast_app *app;
1415 const char *agifile;
1416 const char *agifiledefault = "conf-background.agi";
1417 char meetmesecs[30] = "";
1418 char exitcontext[AST_MAX_CONTEXT] = "";
1419 char recordingtmp[AST_MAX_EXTENSION] = "";
1420 char members[10] = "";
1421 int dtmf, opt_waitmarked_timeout = 0;
1424 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1425 char *buf = __buf + AST_FRIENDLY_OFFSET;
1426 char *exitkeys = NULL;
1428 if (!(user = ast_calloc(1, sizeof(*user))))
1431 /* Possible timeout waiting for marked user */
1432 if ((confflags & CONFFLAG_WAITMARKED) &&
1433 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1434 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1435 (opt_waitmarked_timeout > 0)) {
1436 timeout = time(NULL) + opt_waitmarked_timeout;
1440 if ((confflags & CONFFLAG_KEYEXIT)) {
1441 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1442 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1444 exitkeys = ast_strdupa("#"); /* Default */
1447 if (confflags & CONFFLAG_RECORDCONF) {
1448 if (!conf->recordingfilename) {
1449 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1450 if (!conf->recordingfilename) {
1451 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1452 conf->recordingfilename = ast_strdupa(recordingtmp);
1454 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1455 if (!conf->recordingformat) {
1456 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1457 conf->recordingformat = ast_strdupa(recordingtmp);
1459 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1460 conf->confno, conf->recordingfilename, conf->recordingformat);
1464 if ((conf->recording == MEETME_RECORD_OFF) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1465 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1466 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1468 ztc.confno = conf->zapconf;
1469 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1470 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1471 ast_log(LOG_WARNING, "Error starting listen channel\n");
1472 ast_hangup(conf->lchan);
1475 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1479 time(&user->jointime);
1481 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1482 /* Sorry, but this conference is locked! */
1483 if (!ast_streamfile(chan, "conf-locked", chan->language))
1484 ast_waitstream(chan, "");
1488 if (confflags & CONFFLAG_MARKEDUSER)
1489 conf->markedusers++;
1491 ast_mutex_lock(&conf->playlock);
1493 if (AST_LIST_EMPTY(&conf->userlist))
1496 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1498 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1501 user->userflags = confflags;
1502 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1506 snprintf(members, sizeof(members), "%d", conf->users);
1507 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1509 /* This device changed state now - if this is the first user */
1510 if (conf->users == 1)
1511 ast_device_state_changed("meetme:%s", conf->confno);
1513 ast_mutex_unlock(&conf->playlock);
1515 /* return the unique ID of the conference */
1516 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1518 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1519 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1520 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1521 else if (!ast_strlen_zero(chan->macrocontext))
1522 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1524 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1527 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1528 snprintf(user->namerecloc, sizeof(user->namerecloc),
1529 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1530 conf->confno, user->user_no);
1531 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1532 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1534 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1539 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1540 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1541 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1542 ast_waitstream(chan, "");
1543 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1544 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1545 ast_waitstream(chan, "");
1548 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1549 int keepplaying = 1;
1551 if (conf->users == 2) {
1552 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1553 res = ast_waitstream(chan, AST_DIGIT_ANY);
1554 ast_stopstream(chan);
1561 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1562 res = ast_waitstream(chan, AST_DIGIT_ANY);
1563 ast_stopstream(chan);
1570 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1576 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1577 res = ast_waitstream(chan, AST_DIGIT_ANY);
1578 ast_stopstream(chan);
1587 ast_indicate(chan, -1);
1589 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1590 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1594 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1595 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1599 retryzap = strcasecmp(chan->tech->type, "Zap");
1600 user->zapchannel = !retryzap;
1603 origfd = chan->fds[0];
1605 fd = open("/dev/zap/pseudo", O_RDWR);
1607 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1611 /* Make non-blocking */
1612 flags = fcntl(fd, F_GETFL);
1614 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1618 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1619 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1623 /* Setup buffering information */
1624 memset(&bi, 0, sizeof(bi));
1625 bi.bufsize = CONF_SIZE/2;
1626 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1627 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1628 bi.numbufs = audio_buffers;
1629 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1630 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1635 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1636 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1642 /* XXX Make sure we're not running on a pseudo channel XXX */
1646 memset(&ztc, 0, sizeof(ztc));
1647 memset(&ztc_empty, 0, sizeof(ztc_empty));
1648 /* Check to see if we're in a conference... */
1650 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1651 ast_log(LOG_WARNING, "Error getting conference\n");
1656 /* Whoa, already in a conference... Retry... */
1658 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1663 memset(&ztc, 0, sizeof(ztc));
1664 /* Add us to the conference */
1666 ztc.confno = conf->zapconf;
1668 ast_mutex_lock(&conf->playlock);
1670 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1671 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1672 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1673 ast_waitstream(conf->chan, "");
1674 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1675 ast_waitstream(conf->chan, "");
1679 if (confflags & CONFFLAG_MONITOR)
1680 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1681 else if (confflags & CONFFLAG_TALKER)
1682 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1684 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1686 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1687 ast_log(LOG_WARNING, "Error setting conference\n");
1689 ast_mutex_unlock(&conf->playlock);
1692 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1695 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1700 chan->name, chan->uniqueid, conf->confno, user->user_no);
1704 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1706 if (!(confflags & CONFFLAG_QUIET))
1707 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1708 conf_play(chan, conf, ENTER);
1711 ast_mutex_unlock(&conf->playlock);
1713 conf_flush(fd, chan);
1715 if (confflags & CONFFLAG_AGI) {
1716 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1717 or use default filename of conf-background.agi */
1719 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1721 agifile = agifiledefault;
1723 if (user->zapchannel) {
1724 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1726 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1728 /* Find a pointer to the agi app and execute the script */
1729 app = pbx_findapp("agi");
1731 char *s = ast_strdupa(agifile);
1732 ret = pbx_exec(chan, app, s);
1734 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1737 if (user->zapchannel) {
1738 /* Remove CONFMUTE mode on Zap channel */
1740 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1743 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1744 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1746 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1748 if (!(dsp = ast_dsp_new())) {
1749 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1753 int menu_was_active = 0;
1758 if (timeout && time(NULL) >= timeout)
1761 /* if we have just exited from the menu, and the user had a channel-driver
1762 volume adjustment, restore it
1764 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1765 set_talk_volume(user, user->listen.desired);
1767 menu_was_active = menu_active;
1769 currentmarked = conf->markedusers;
1770 if (!(confflags & CONFFLAG_QUIET) &&
1771 (confflags & CONFFLAG_MARKEDUSER) &&
1772 (confflags & CONFFLAG_WAITMARKED) &&
1774 if (currentmarked == 1 && conf->users > 1) {
1775 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1776 if (conf->users - 1 == 1) {
1777 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1778 ast_waitstream(chan, "");
1780 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1781 ast_waitstream(chan, "");
1784 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1785 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1786 ast_waitstream(chan, "");
1789 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1792 /* Update the struct with the actual confflags */
1793 user->userflags = confflags;
1795 if (confflags & CONFFLAG_WAITMARKED) {
1796 if(currentmarked == 0) {
1797 if (lastmarked != 0) {
1798 if (!(confflags & CONFFLAG_QUIET))
1799 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1800 ast_waitstream(chan, "");
1801 if (confflags & CONFFLAG_MARKEDEXIT) {
1802 if (confflags & CONFFLAG_KICK_CONTINUE)
1806 ztc.confmode = ZT_CONF_CONF;
1807 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1808 ast_log(LOG_WARNING, "Error setting conference\n");
1814 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1815 ast_moh_start(chan, NULL, NULL);
1818 ztc.confmode = ZT_CONF_CONF;
1819 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1820 ast_log(LOG_WARNING, "Error setting conference\n");
1825 } else if(currentmarked >= 1 && lastmarked == 0) {
1826 /* Marked user entered, so cancel timeout */
1828 if (confflags & CONFFLAG_MONITOR)
1829 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1830 else if (confflags & CONFFLAG_TALKER)
1831 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1833 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1834 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1835 ast_log(LOG_WARNING, "Error setting conference\n");
1839 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1843 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1844 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1845 ast_waitstream(chan, "");
1846 conf_play(chan, conf, ENTER);
1851 /* trying to add moh for single person conf */
1852 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1853 if (conf->users == 1) {
1854 if (musiconhold == 0) {
1855 ast_moh_start(chan, NULL, NULL);
1866 /* Leave if the last marked user left */
1867 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1868 if (confflags & CONFFLAG_KICK_CONTINUE)
1875 /* Check if my modes have changed */
1877 /* If I should be muted but am still talker, mute me */
1878 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1879 ztc.confmode ^= ZT_CONF_TALKER;
1880 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1881 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1886 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1892 chan->name, chan->uniqueid, conf->confno, user->user_no);
1895 /* If I should be un-muted but am not talker, un-mute me */
1896 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1897 ztc.confmode |= ZT_CONF_TALKER;
1898 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1899 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1904 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1910 chan->name, chan->uniqueid, conf->confno, user->user_no);
1913 /* If I have been kicked, exit the conference */
1914 if (user->adminflags & ADMINFLAG_KICKME) {
1915 //You have been kicked.
1916 if (!(confflags & CONFFLAG_QUIET) &&
1917 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1918 ast_waitstream(chan, "");
1924 /* Perform an extra hangup check just in case */
1925 if (ast_check_hangup(chan))
1929 if (c->fds[0] != origfd) {
1931 /* Kill old pseudo */
1935 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
1936 retryzap = strcasecmp(c->tech->type, "Zap");
1937 user->zapchannel = !retryzap;
1940 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1941 f = ast_read_noaudio(c);
1946 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1947 if (user->talk.actual)
1948 ast_frame_adjust_volume(f, user->talk.actual);
1953 if (user->talking == -1)
1956 res = ast_dsp_silence(dsp, f, &totalsilence);
1957 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1959 if (confflags & CONFFLAG_MONITORTALKER)
1960 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1966 chan->name, chan->uniqueid, conf->confno, user->user_no);
1968 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1970 if (confflags & CONFFLAG_MONITORTALKER)
1971 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1977 chan->name, chan->uniqueid, conf->confno, user->user_no);
1981 /* Absolutely do _not_ use careful_write here...
1982 it is important that we read data from the channel
1983 as fast as it arrives, and feed it into the conference.
1984 The buffering in the pseudo channel will take care of any
1985 timing differences, unless they are so drastic as to lose
1986 audio frames (in which case carefully writing would only
1987 have delayed the audio even further).
1989 /* As it turns out, we do want to use careful write. We just
1990 don't want to block, but we do want to at least *try*
1991 to write out all the samples.
1994 careful_write(fd, f->data, f->datalen, 0);
1996 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1999 tmp[0] = f->subclass;
2001 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2002 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2007 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2009 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2012 exitkey[0] = f->subclass;
2015 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2020 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2021 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2022 ast_log(LOG_WARNING, "Error setting conference\n");
2028 /* if we are entering the menu, and the user has a channel-driver
2029 volume adjustment, clear it
2031 if (!menu_active && user->talk.desired && !user->talk.actual)
2032 set_talk_volume(user, 0);
2037 if ((confflags & CONFFLAG_ADMIN)) {
2041 /* Record this sound! */
2042 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2043 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2044 ast_stopstream(chan);
2051 case '1': /* Un/Mute */
2054 /* for admin, change both admin and use flags */
2055 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2056 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2058 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2060 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2061 if (!ast_streamfile(chan, "conf-muted", chan->language))
2062 ast_waitstream(chan, "");
2064 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2065 ast_waitstream(chan, "");
2068 case '2': /* Un/Lock the Conference */
2072 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2073 ast_waitstream(chan, "");
2076 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2077 ast_waitstream(chan, "");
2080 case '3': /* Eject last user */
2082 usr = AST_LIST_LAST(&conf->userlist);
2083 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2084 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2085 ast_waitstream(chan, "");
2087 usr->adminflags |= ADMINFLAG_KICKME;
2088 ast_stopstream(chan);
2091 tweak_listen_volume(user, VOL_DOWN);
2094 tweak_listen_volume(user, VOL_UP);
2097 tweak_talk_volume(user, VOL_DOWN);
2103 tweak_talk_volume(user, VOL_UP);
2107 /* Play an error message! */
2108 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2109 ast_waitstream(chan, "");
2117 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2118 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2119 ast_stopstream(chan);
2126 case '1': /* Un/Mute */
2129 /* user can only toggle the self-muted state */
2130 user->adminflags ^= ADMINFLAG_SELFMUTED;
2132 /* they can't override the admin mute state */
2133 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2134 if (!ast_streamfile(chan, "conf-muted", chan->language))
2135 ast_waitstream(chan, "");
2137 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2138 ast_waitstream(chan, "");
2142 tweak_listen_volume(user, VOL_DOWN);
2145 tweak_listen_volume(user, VOL_UP);
2148 tweak_talk_volume(user, VOL_DOWN);
2154 tweak_talk_volume(user, VOL_UP);
2158 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2159 ast_waitstream(chan, "");
2165 ast_moh_start(chan, NULL, NULL);
2167 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2168 ast_log(LOG_WARNING, "Error setting conference\n");
2174 conf_flush(fd, chan);
2175 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2176 && confflags & CONFFLAG_PASS_DTMF) {
2177 conf_queue_dtmf(conf, user, f);
2178 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2179 switch (f->subclass) {
2180 case AST_CONTROL_HOLD:
2181 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2186 } else if (f->frametype == AST_FRAME_NULL) {
2187 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2190 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2191 chan->name, f->frametype, f->subclass);
2194 } else if (outfd > -1) {
2195 res = read(outfd, buf, CONF_SIZE);
2197 memset(&fr, 0, sizeof(fr));
2198 fr.frametype = AST_FRAME_VOICE;
2199 fr.subclass = AST_FORMAT_SLINEAR;
2203 fr.offset = AST_FRIENDLY_OFFSET;
2204 if ( !user->listen.actual &&
2205 ((confflags & CONFFLAG_MONITOR) ||
2206 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2207 (!user->talking)) ) {
2209 for (index=0;index<AST_FRAME_BITS;index++)
2210 if (chan->rawwriteformat & (1 << index))
2212 if (index >= AST_FRAME_BITS)
2213 goto bailoutandtrynormal;
2214 ast_mutex_lock(&conf->listenlock);
2215 if (!conf->transframe[index]) {
2216 if (conf->origframe) {
2217 if (!conf->transpath[index])
2218 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2219 if (conf->transpath[index]) {
2220 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2221 if (!conf->transframe[index])
2222 conf->transframe[index] = &ast_null_frame;
2226 if (conf->transframe[index]) {
2227 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2228 if (ast_write(chan, conf->transframe[index]))
2229 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2232 ast_mutex_unlock(&conf->listenlock);
2233 goto bailoutandtrynormal;
2235 ast_mutex_unlock(&conf->listenlock);
2237 bailoutandtrynormal:
2238 if (user->listen.actual)
2239 ast_frame_adjust_volume(&fr, user->listen.actual);
2240 if (ast_write(chan, &fr) < 0) {
2241 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2245 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2247 lastmarked = currentmarked;
2257 /* Take out of conference */
2261 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2262 ast_log(LOG_WARNING, "Error setting conference\n");
2266 reset_volumes(user);
2268 AST_LIST_LOCK(&confs);
2269 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2270 conf_play(chan, conf, LEAVE);
2272 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2273 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2274 if ((conf->chan) && (conf->users > 1)) {
2275 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2276 ast_waitstream(conf->chan, "");
2277 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2278 ast_waitstream(conf->chan, "");
2280 ast_filedelete(user->namerecloc, NULL);
2283 AST_LIST_UNLOCK(&confs);
2286 AST_LIST_LOCK(&confs);
2291 if (user->user_no) { /* Only cleanup users who really joined! */
2293 hr = (now - user->jointime) / 3600;
2294 min = ((now - user->jointime) % 3600) / 60;
2295 sec = (now - user->jointime) % 60;
2298 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2303 "CallerIDNum: %s\r\n"
2304 "CallerIDName: %s\r\n"
2305 "Duration: %ld\r\n",
2306 chan->name, chan->uniqueid, conf->confno,
2308 S_OR(user->chan->cid.cid_num, "<unknown>"),
2309 S_OR(user->chan->cid.cid_name, "<unknown>"),
2310 (long)(now - user->jointime));
2315 snprintf(members, sizeof(members), "%d", conf->users);
2316 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2317 if (confflags & CONFFLAG_MARKEDUSER)
2318 conf->markedusers--;
2319 /* Remove ourselves from the list */
2320 AST_LIST_REMOVE(&conf->userlist, user, list);
2322 /* Change any states */
2324 ast_device_state_changed("meetme:%s", conf->confno);
2326 /* Return the number of seconds the user was in the conf */
2327 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2328 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2331 AST_LIST_UNLOCK(&confs);
2336 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2337 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2339 struct ast_variable *var;
2340 struct ast_conference *cnf;
2342 /* Check first in the conference list */
2343 AST_LIST_LOCK(&confs);
2344 AST_LIST_TRAVERSE(&confs, cnf, list) {
2345 if (!strcmp(confno, cnf->confno))
2349 cnf->refcount += refcount;
2351 AST_LIST_UNLOCK(&confs);
2354 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2356 var = ast_load_realtime("meetme", "confno", confno, NULL);
2362 if (!strcasecmp(var->name, "pin")) {
2363 pin = ast_strdupa(var->value);
2364 } else if (!strcasecmp(var->name, "adminpin")) {
2365 pinadmin = ast_strdupa(var->value);
2369 ast_variables_destroy(var);
2371 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2375 if (confflags && !cnf->chan &&
2376 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2377 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2378 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2379 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2382 if (confflags && !cnf->chan &&
2383 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2384 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2385 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2393 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2394 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2396 struct ast_config *cfg;
2397 struct ast_variable *var;
2398 struct ast_conference *cnf;
2400 AST_DECLARE_APP_ARGS(args,
2401 AST_APP_ARG(confno);
2403 AST_APP_ARG(pinadmin);
2406 /* Check first in the conference list */
2407 AST_LIST_LOCK(&confs);
2408 AST_LIST_TRAVERSE(&confs, cnf, list) {
2409 if (!strcmp(confno, cnf->confno))
2413 cnf->refcount += refcount;
2415 AST_LIST_UNLOCK(&confs);
2419 /* No need to parse meetme.conf */
2420 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2422 if (dynamic_pin[0] == 'q') {
2423 /* Query the user to enter a PIN */
2424 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2427 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2429 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2432 /* Check the config */
2433 cfg = ast_config_load(CONFIG_FILE_NAME);
2435 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2438 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2439 if (strcasecmp(var->name, "conf"))
2442 if (!(parse = ast_strdupa(var->value)))
2445 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2446 if (!strcasecmp(args.confno, confno)) {
2447 /* Bingo it's a valid conference */
2448 cnf = build_conf(args.confno,
2450 S_OR(args.pinadmin, ""),
2451 make, dynamic, refcount, chan);
2456 ast_debug(1, "%s isn't a valid conference\n", confno);
2458 ast_config_destroy(cfg);
2460 } else if (dynamic_pin) {
2461 /* Correct for the user selecting 'D' instead of 'd' to have
2462 someone join into a conference that has already been created
2464 if (dynamic_pin[0] == 'q')
2465 dynamic_pin[0] = '\0';
2469 if (confflags && !cnf->chan &&
2470 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2471 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2472 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2473 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2476 if (confflags && !cnf->chan &&
2477 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2478 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2479 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2486 /*! \brief The MeetmeCount application */
2487 static int count_exec(struct ast_channel *chan, void *data)
2490 struct ast_conference *conf;
2494 AST_DECLARE_APP_ARGS(args,
2495 AST_APP_ARG(confno);
2496 AST_APP_ARG(varname);
2499 if (ast_strlen_zero(data)) {
2500 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2504 if (!(localdata = ast_strdupa(data)))
2507 AST_STANDARD_APP_ARGS(args, localdata);
2509 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2512 count = conf->users;
2518 if (!ast_strlen_zero(args.varname)){
2519 /* have var so load it and exit */
2520 snprintf(val, sizeof(val), "%d",count);
2521 pbx_builtin_setvar_helper(chan, args.varname, val);
2523 if (chan->_state != AST_STATE_UP)
2525 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2531 /*! \brief The meetme() application */
2532 static int conf_exec(struct ast_channel *chan, void *data)
2535 char confno[MAX_CONFNUM] = "";
2538 struct ast_conference *cnf = NULL;
2539 struct ast_flags confflags = {0};
2541 int empty = 0, empty_no_pin = 0;
2542 int always_prompt = 0;
2543 char *notdata, *info, the_pin[MAX_PIN] = "";
2544 AST_DECLARE_APP_ARGS(args,
2545 AST_APP_ARG(confno);
2546 AST_APP_ARG(options);
2549 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2551 if (ast_strlen_zero(data)) {
2558 if (chan->_state != AST_STATE_UP)
2561 info = ast_strdupa(notdata);
2563 AST_STANDARD_APP_ARGS(args, info);
2566 ast_copy_string(confno, args.confno, sizeof(confno));
2567 if (ast_strlen_zero(confno)) {
2573 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2576 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2577 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2578 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2579 strcpy(the_pin, "q");
2581 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2582 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2583 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2591 struct ast_config *cfg;
2592 struct ast_variable *var;
2595 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2596 if ((empty_no_pin) || (!dynamic)) {
2597 cfg = ast_config_load(CONFIG_FILE_NAME);
2599 var = ast_variable_browse(cfg, "rooms");
2601 if (!strcasecmp(var->name, "conf")) {
2602 char *stringp = ast_strdupa(var->value);
2604 char *confno_tmp = strsep(&stringp, "|,");
2607 /* For static: run through the list and see if this conference is empty */
2608 AST_LIST_LOCK(&confs);
2609 AST_LIST_TRAVERSE(&confs, cnf, list) {
2610 if (!strcmp(confno_tmp, cnf->confno)) {
2611 /* The conference exists, therefore it's not empty */
2616 AST_LIST_UNLOCK(&confs);
2618 /* At this point, we have a confno_tmp (static conference) that is empty */
2619 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2620 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2621 * Case 2: empty_no_pin and pin is blank (but not NULL)
2622 * Case 3: not empty_no_pin
2624 ast_copy_string(confno, confno_tmp, sizeof(confno));
2626 /* XXX the map is not complete (but we do have a confno) */
2634 ast_config_destroy(cfg);
2638 /* Select first conference number not in use */
2639 if (ast_strlen_zero(confno) && dynamic) {
2640 AST_LIST_LOCK(&confs);
2641 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2643 snprintf(confno, sizeof(confno), "%d", i);
2648 AST_LIST_UNLOCK(&confs);
2652 if (ast_strlen_zero(confno)) {
2653 res = ast_streamfile(chan, "conf-noempty", chan->language);
2655 ast_waitstream(chan, "");
2657 if (sscanf(confno, "%d", &confno_int) == 1) {
2658 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2659 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2661 ast_waitstream(chan, "");
2662 res = ast_say_digits(chan, confno_int, "", chan->language);
2666 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2671 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2672 /* Prompt user for conference number */
2673 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2675 /* Don't try to validate when we catch an error */
2681 if (!ast_strlen_zero(confno)) {
2682 /* Check the validity of the conference */
2683 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2684 sizeof(the_pin), 1, &confflags);
2686 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2687 the_pin, sizeof(the_pin), 1, &confflags);
2691 res = ast_streamfile(chan, "conf-invalid", chan->language);
2693 ast_waitstream(chan, "");
2698 if ((!ast_strlen_zero(cnf->pin) &&
2699 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2700 (!ast_strlen_zero(cnf->pinadmin) &&
2701 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2702 char pin[MAX_PIN] = "";
2705 /* Allow the pin to be retried up to 3 times */
2706 for (j = 0; j < 3; j++) {
2707 if (*the_pin && (always_prompt == 0)) {
2708 ast_copy_string(pin, the_pin, sizeof(pin));
2711 /* Prompt user for pin if pin is required */
2712 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2715 if (!strcasecmp(pin, cnf->pin) ||
2716 (!ast_strlen_zero(cnf->pinadmin) &&
2717 !strcasecmp(pin, cnf->pinadmin))) {
2720 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2721 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2722 /* Run the conference */
2723 res = conf_run(chan, cnf, confflags.flags, optargs);
2727 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2728 res = ast_waitstream(chan, AST_DIGIT_ANY);
2729 ast_stopstream(chan);