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
79 /*! String format for scheduled conferences */
80 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
83 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
84 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
85 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
86 /*! User has requested to speak */
87 ADMINFLAG_T_REQUEST = (1 << 4),
90 #define MEETME_DELAYDETECTTALK 300
91 #define MEETME_DELAYDETECTENDTALK 1000
93 #define AST_FRAME_BITS 32
100 enum entrance_sound {
105 enum recording_state {
107 MEETME_RECORD_STARTED,
108 MEETME_RECORD_ACTIVE,
109 MEETME_RECORD_TERMINATE
112 #define CONF_SIZE 320
115 /*! user has admin access on the conference */
116 CONFFLAG_ADMIN = (1 << 0),
117 /*! If set the user can only receive audio from the conference */
118 CONFFLAG_MONITOR = (1 << 1),
119 /*! If set asterisk will exit conference when key defined in p() option is pressed */
120 CONFFLAG_KEYEXIT = (1 << 2),
121 /*! If set asterisk will provide a menu to the user when '*' is pressed */
122 CONFFLAG_STARMENU = (1 << 3),
123 /*! If set the use can only send audio to the conference */
124 CONFFLAG_TALKER = (1 << 4),
125 /*! If set there will be no enter or leave sounds */
126 CONFFLAG_QUIET = (1 << 5),
127 /*! If set, when user joins the conference, they will be told the number
128 * of users that are already in */
129 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
130 /*! Set to run AGI Script in Background */
131 CONFFLAG_AGI = (1 << 7),
132 /*! Set to have music on hold when user is alone in conference */
133 CONFFLAG_MOH = (1 << 8),
134 /*! If set the MeetMe will return if all marked with this flag left */
135 CONFFLAG_MARKEDEXIT = (1 << 9),
136 /*! If set, the MeetMe will wait until a marked user enters */
137 CONFFLAG_WAITMARKED = (1 << 10),
138 /*! If set, the MeetMe will exit to the specified context */
139 CONFFLAG_EXIT_CONTEXT = (1 << 11),
140 /*! If set, the user will be marked */
141 CONFFLAG_MARKEDUSER = (1 << 12),
142 /*! If set, user will be ask record name on entry of conference */
143 CONFFLAG_INTROUSER = (1 << 13),
144 /*! If set, the MeetMe will be recorded */
145 CONFFLAG_RECORDCONF = (1<< 14),
146 /*! If set, the user will be monitored if the user is talking or not */
147 CONFFLAG_MONITORTALKER = (1 << 15),
148 CONFFLAG_DYNAMIC = (1 << 16),
149 CONFFLAG_DYNAMICPIN = (1 << 17),
150 CONFFLAG_EMPTY = (1 << 18),
151 CONFFLAG_EMPTYNOPIN = (1 << 19),
152 CONFFLAG_ALWAYSPROMPT = (1 << 20),
153 /*! If set, won't speak the extra prompt when the first person
154 * enters the conference */
155 CONFFLAG_NOONLYPERSON = (1 << 22),
156 /*! If set, user will be asked to record name on entry of conference
158 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
159 /*! If set, the user will be initially self-muted */
160 CONFFLAG_STARTMUTED = (1 << 24),
161 /*! Pass DTMF through the conference */
162 CONFFLAG_PASS_DTMF = (1 << 25),
163 CONFFLAG_SLA_STATION = (1 << 26),
164 CONFFLAG_SLA_TRUNK = (1 << 27),
165 /*! If set, the user should continue in the dialplan if kicked out */
166 CONFFLAG_KICK_CONTINUE = (1 << 28)
170 OPT_ARG_WAITMARKED = 0,
171 OPT_ARG_EXITKEYS = 1,
172 OPT_ARG_ARRAY_SIZE = 2,
175 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
176 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
177 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
178 AST_APP_OPTION('b', CONFFLAG_AGI ),
179 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
180 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
181 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
182 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
183 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
184 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
185 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
186 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
187 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
188 AST_APP_OPTION('M', CONFFLAG_MOH ),
189 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
190 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
191 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
192 AST_APP_OPTION('q', CONFFLAG_QUIET ),
193 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
194 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
195 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
196 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
197 AST_APP_OPTION('t', CONFFLAG_TALKER ),
198 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
199 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
200 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
201 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
204 static const char *app = "MeetMe";
205 static const char *app2 = "MeetMeCount";
206 static const char *app3 = "MeetMeAdmin";
207 static const char *app4 = "MeetMeChannelAdmin";
208 static const char *slastation_app = "SLAStation";
209 static const char *slatrunk_app = "SLATrunk";
211 static const char *synopsis = "MeetMe conference bridge";
212 static const char *synopsis2 = "MeetMe participant count";
213 static const char *synopsis3 = "MeetMe conference Administration";
214 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
215 static const char *slastation_synopsis = "Shared Line Appearance Station";
216 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
218 /* Lookup RealTime conferences based on confno and current time */
219 static int rt_schedule;
220 static int fuzzystart;
221 static int earlyalert;
224 /* Log participant count to the RealTime backend */
225 static int rt_log_members;
227 static const char *descrip =
228 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
229 "conference. If the conference number is omitted, the user will be prompted\n"
230 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
231 "is specified, by pressing '#'.\n"
232 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
233 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
234 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
235 "The option string may contain zero or more of the following characters:\n"
236 " 'a' -- set admin mode\n"
237 " 'A' -- set marked mode\n"
238 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
239 " Default: conf-background.agi (Note: This does not work with\n"
240 " non-Zap channels in the same conference)\n"
241 " 'c' -- announce user(s) count on joining a conference\n"
242 " 'C' -- continue in dialplan when kicked out of conference\n"
243 " 'd' -- dynamically add conference\n"
244 " 'D' -- dynamically add conference, prompting for a PIN\n"
245 " 'e' -- select an empty conference\n"
246 " 'E' -- select an empty pinless conference\n"
247 " 'F' -- Pass DTMF through the conference.\n"
248 " 'i' -- announce user join/leave with review\n"
249 " 'I' -- announce user join/leave without review\n"
250 " 'l' -- set listen only mode (Listen only, no talking)\n"
251 " 'm' -- set initially muted\n"
252 " 'M' -- enable music on hold when the conference has a single caller\n"
253 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
254 " being muted, meaning (a) No encode is done on transmission and\n"
255 " (b) Received audio that is not registered as talking is omitted\n"
256 " causing no buildup in background noise\n"
258 " -- allow user to exit the conference by pressing '#' (default)\n"
259 " or any of the defined keys. If keys contain '*' this will override\n"
260 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
261 " 'P' -- always prompt for the pin even if it is specified\n"
262 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
263 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
264 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
265 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
267 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
268 " 't' -- set talk only mode. (Talk only, no listening)\n"
269 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
271 " -- wait until the marked user enters the conference\n"
272 " 'x' -- close the conference when last marked user exits\n"
273 " 'X' -- allow user to exit the conference by entering a valid single\n"
274 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
275 " if that variable is not defined.\n"
276 " '1' -- do not play message when first person enters\n";
278 static const char *descrip2 =
279 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
280 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
281 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
282 "the channel, unless priority n+1 exists, in which case priority progress will\n"
284 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
286 static const char *descrip3 =
287 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
288 " 'e' -- Eject last user that joined\n"
289 " 'k' -- Kick one user out of conference\n"
290 " 'K' -- Kick all users out of conference\n"
291 " 'l' -- Unlock conference\n"
292 " 'L' -- Lock conference\n"
293 " 'm' -- Unmute one user\n"
294 " 'M' -- Mute one user\n"
295 " 'n' -- Unmute all users in the conference\n"
296 " 'N' -- Mute all non-admin users in the conference\n"
297 " 'r' -- Reset one user's volume settings\n"
298 " 'R' -- Reset all users volume settings\n"
299 " 's' -- Lower entire conference speaking volume\n"
300 " 'S' -- Raise entire conference speaking volume\n"
301 " 't' -- Lower one user's talk volume\n"
302 " 'T' -- Raise one user's talk volume\n"
303 " 'u' -- Lower one user's listen volume\n"
304 " 'U' -- Raise one user's listen volume\n"
305 " 'v' -- Lower entire conference listening volume\n"
306 " 'V' -- Raise entire conference listening volume\n"
309 static const char *descrip4 =
310 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
311 "channel in any coference.\n"
312 " 'k' -- Kick the specified user out of the conference he is in\n"
313 " 'm' -- Unmute the specified user\n"
314 " 'M' -- Mute the specified user\n"
317 static const char *slastation_desc =
318 " SLAStation(station):\n"
319 "This application should be executed by an SLA station. The argument depends\n"
320 "on how the call was initiated. If the phone was just taken off hook, then\n"
321 "the argument \"station\" should be just the station name. If the call was\n"
322 "initiated by pressing a line key, then the station name should be preceded\n"
323 "by an underscore and the trunk name associated with that line button.\n"
324 "For example: \"station1_line1\"."
325 " On exit, this application will set the variable SLASTATION_STATUS to\n"
326 "one of the following values:\n"
327 " FAILURE | CONGESTION | SUCCESS\n"
330 static const char *slatrunk_desc =
331 " SLATrunk(trunk):\n"
332 "This application should be executed by an SLA trunk on an inbound call.\n"
333 "The channel calling this application should correspond to the SLA trunk\n"
334 "with the name \"trunk\" that is being passed as an argument.\n"
335 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
336 "one of the following values:\n"
337 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
340 #define MAX_CONFNUM 80
343 /*! \brief The MeetMe Conference object */
344 struct ast_conference {
345 ast_mutex_t playlock; /*!< Conference specific lock (players) */
346 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
347 char confno[MAX_CONFNUM]; /*!< Conference */
348 struct ast_channel *chan; /*!< Announcements channel */
349 struct ast_channel *lchan; /*!< Listen/Record channel */
350 int fd; /*!< Announcements fd */
351 int zapconf; /*!< Zaptel Conf # */
352 int users; /*!< Number of active users */
353 int markedusers; /*!< Number of marked users */
354 int maxusers; /*!< Participant limit if scheduled */
355 int endalert; /*!< When to play conf ending message */
356 time_t start; /*!< Start time (s) */
357 int refcount; /*!< reference count of usage */
358 enum recording_state recording:2; /*!< recording status */
359 unsigned int isdynamic:1; /*!< Created on the fly? */
360 unsigned int locked:1; /*!< Is the conference locked? */
361 pthread_t recordthread; /*!< thread for recording */
362 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
363 pthread_attr_t attr; /*!< thread attribute */
364 const char *recordingfilename; /*!< Filename to record the Conference into */
365 const char *recordingformat; /*!< Format to record the Conference in */
366 char pin[MAX_PIN]; /*!< If protected by a PIN */
367 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
369 char endtime[19]; /*!< When to end the conf if scheduled */
370 struct ast_frame *transframe[32];
371 struct ast_frame *origframe;
372 struct ast_trans_pvt *transpath[32];
373 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
374 AST_LIST_ENTRY(ast_conference) list;
377 static AST_LIST_HEAD_STATIC(confs, ast_conference);
379 static unsigned int conf_map[1024] = {0, };
382 int desired; /*!< Desired volume adjustment */
383 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
386 /*! \brief The MeetMe User object */
387 struct ast_conf_user {
388 int user_no; /*!< User Number */
389 int userflags; /*!< Flags as set in the conference */
390 int adminflags; /*!< Flags set by the Admin */
391 struct ast_channel *chan; /*!< Connected channel */
392 int talking; /*!< Is user talking */
393 int zapchannel; /*!< Is a Zaptel channel */
394 char usrvalue[50]; /*!< Custom User Value */
395 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
396 time_t jointime; /*!< Time the user joined the conference */
398 struct volume listen;
399 AST_LIST_ENTRY(ast_conf_user) list;
402 enum sla_which_trunk_refs {
407 enum sla_trunk_state {
408 SLA_TRUNK_STATE_IDLE,
409 SLA_TRUNK_STATE_RINGING,
411 SLA_TRUNK_STATE_ONHOLD,
412 SLA_TRUNK_STATE_ONHOLD_BYME,
415 enum sla_hold_access {
416 /*! This means that any station can put it on hold, and any station
417 * can retrieve the call from hold. */
419 /*! This means that only the station that put the call on hold may
420 * retrieve it from hold. */
424 struct sla_trunk_ref;
427 AST_RWLIST_ENTRY(sla_station) entry;
428 AST_DECLARE_STRING_FIELDS(
429 AST_STRING_FIELD(name);
430 AST_STRING_FIELD(device);
431 AST_STRING_FIELD(autocontext);
433 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
434 struct ast_dial *dial;
435 /*! Ring timeout for this station, for any trunk. If a ring timeout
436 * is set for a specific trunk on this station, that will take
437 * priority over this value. */
438 unsigned int ring_timeout;
439 /*! Ring delay for this station, for any trunk. If a ring delay
440 * is set for a specific trunk on this station, that will take
441 * priority over this value. */
442 unsigned int ring_delay;
443 /*! This option uses the values in the sla_hold_access enum and sets the
444 * access control type for hold on this station. */
445 unsigned int hold_access:1;
446 /*! Use count for inside sla_station_exec */
447 unsigned int ref_count;
450 struct sla_station_ref {
451 AST_LIST_ENTRY(sla_station_ref) entry;
452 struct sla_station *station;
456 AST_RWLIST_ENTRY(sla_trunk) entry;
457 AST_DECLARE_STRING_FIELDS(
458 AST_STRING_FIELD(name);
459 AST_STRING_FIELD(device);
460 AST_STRING_FIELD(autocontext);
462 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
463 /*! Number of stations that use this trunk */
464 unsigned int num_stations;
465 /*! Number of stations currently on a call with this trunk */
466 unsigned int active_stations;
467 /*! Number of stations that have this trunk on hold. */
468 unsigned int hold_stations;
469 struct ast_channel *chan;
470 unsigned int ring_timeout;
471 /*! If set to 1, no station will be able to join an active call with
473 unsigned int barge_disabled:1;
474 /*! This option uses the values in the sla_hold_access enum and sets the
475 * access control type for hold on this trunk. */
476 unsigned int hold_access:1;
477 /*! Whether this trunk is currently on hold, meaning that once a station
478 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
479 unsigned int on_hold:1;
480 /*! Use count for inside sla_trunk_exec */
481 unsigned int ref_count;
484 struct sla_trunk_ref {
485 AST_LIST_ENTRY(sla_trunk_ref) entry;
486 struct sla_trunk *trunk;
487 enum sla_trunk_state state;
488 struct ast_channel *chan;
489 /*! Ring timeout to use when this trunk is ringing on this specific
490 * station. This takes higher priority than a ring timeout set at
491 * the station level. */
492 unsigned int ring_timeout;
493 /*! Ring delay to use when this trunk is ringing on this specific
494 * station. This takes higher priority than a ring delay set at
495 * the station level. */
496 unsigned int ring_delay;
499 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
500 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
502 static const char sla_registrar[] = "SLA";
504 /*! \brief Event types that can be queued up for the SLA thread */
505 enum sla_event_type {
506 /*! A station has put the call on hold */
508 /*! The state of a dial has changed */
509 SLA_EVENT_DIAL_STATE,
510 /*! The state of a ringing trunk has changed */
511 SLA_EVENT_RINGING_TRUNK,
512 /*! A reload of configuration has been requested */
514 /*! Poke the SLA thread so it can check if it can perform a reload */
515 SLA_EVENT_CHECK_RELOAD,
519 enum sla_event_type type;
520 struct sla_station *station;
521 struct sla_trunk_ref *trunk_ref;
522 AST_LIST_ENTRY(sla_event) entry;
525 /*! \brief A station that failed to be dialed
526 * \note Only used by the SLA thread. */
527 struct sla_failed_station {
528 struct sla_station *station;
529 struct timeval last_try;
530 AST_LIST_ENTRY(sla_failed_station) entry;
533 /*! \brief A trunk that is ringing */
534 struct sla_ringing_trunk {
535 struct sla_trunk *trunk;
536 /*! The time that this trunk started ringing */
537 struct timeval ring_begin;
538 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
539 AST_LIST_ENTRY(sla_ringing_trunk) entry;
542 enum sla_station_hangup {
543 SLA_STATION_HANGUP_NORMAL,
544 SLA_STATION_HANGUP_TIMEOUT,
547 /*! \brief A station that is ringing */
548 struct sla_ringing_station {
549 struct sla_station *station;
550 /*! The time that this station started ringing */
551 struct timeval ring_begin;
552 AST_LIST_ENTRY(sla_ringing_station) entry;
556 * \brief A structure for data used by the sla thread
559 /*! The SLA thread ID */
563 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
564 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
565 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
566 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
568 /*! Attempt to handle CallerID, even though it is known not to work
569 * properly in some situations. */
570 unsigned int attempt_callerid:1;
571 /*! A reload has been requested */
572 unsigned int reload:1;
574 .thread = AST_PTHREADT_NULL,
577 /*! The number of audio buffers to be allocated on pseudo channels
578 * when in a conference */
579 static int audio_buffers;
581 /*! Map 'volume' levels from -5 through +5 into
582 * decibel (dB) settings for channel drivers
583 * Note: these are not a straight linear-to-dB
584 * conversion... the numbers have been modified
585 * to give the user a better level of adjustability
587 static char const gain_map[] = {
602 static int admin_exec(struct ast_channel *chan, void *data);
603 static void *recordthread(void *args);
605 static char *istalking(int x)
610 return "(unmonitored)";
612 return "(not talking)";
615 static int careful_write(int fd, unsigned char *data, int len, int block)
622 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
623 res = ioctl(fd, ZT_IOMUX, &x);
627 res = write(fd, data, len);
629 if (errno != EAGAIN) {
630 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
642 static int set_talk_volume(struct ast_conf_user *user, int volume)
646 /* attempt to make the adjustment in the channel driver;
647 if successful, don't adjust in the frame reading routine
649 gain_adjust = gain_map[volume + 5];
651 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
654 static int set_listen_volume(struct ast_conf_user *user, int volume)
658 /* attempt to make the adjustment in the channel driver;
659 if successful, don't adjust in the frame reading routine
661 gain_adjust = gain_map[volume + 5];
663 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
666 static void tweak_volume(struct volume *vol, enum volume_action action)
670 switch (vol->desired) {
685 switch (vol->desired) {
701 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
703 tweak_volume(&user->talk, action);
704 /* attempt to make the adjustment in the channel driver;
705 if successful, don't adjust in the frame reading routine
707 if (!set_talk_volume(user, user->talk.desired))
708 user->talk.actual = 0;
710 user->talk.actual = user->talk.desired;
713 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
715 tweak_volume(&user->listen, action);
716 /* attempt to make the adjustment in the channel driver;
717 if successful, don't adjust in the frame reading routine
719 if (!set_listen_volume(user, user->listen.desired))
720 user->listen.actual = 0;
722 user->listen.actual = user->listen.desired;
725 static void reset_volumes(struct ast_conf_user *user)
727 signed char zero_volume = 0;
729 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
730 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
733 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
739 if (!ast_check_hangup(chan))
740 res = ast_autoservice_start(chan);
742 AST_LIST_LOCK(&confs);
758 careful_write(conf->fd, data, len, 1);
761 AST_LIST_UNLOCK(&confs);
764 ast_autoservice_stop(chan);
768 * \brief Find or create a conference
770 * \param confno The conference name/number
771 * \param pin The regular user pin
772 * \param pinadmin The admin pin
773 * \param make Make the conf if it doesn't exist
774 * \param dynamic Mark the newly created conference as dynamic
775 * \param refcount How many references to mark on the conference
776 * \param chan The asterisk channel
778 * \return A pointer to the conference struct, or NULL if it wasn't found and
779 * make or dynamic were not set.
781 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
783 struct ast_conference *cnf;
784 struct zt_confinfo ztc = { 0, };
787 AST_LIST_LOCK(&confs);
789 AST_LIST_TRAVERSE(&confs, cnf, list) {
790 if (!strcmp(confno, cnf->confno))
794 if (cnf || (!make && !dynamic))
798 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
801 ast_mutex_init(&cnf->playlock);
802 ast_mutex_init(&cnf->listenlock);
803 cnf->recordthread = AST_PTHREADT_NULL;
804 ast_mutex_init(&cnf->recordthreadlock);
805 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
806 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
807 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
808 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
810 /* Setup a new zap conference */
812 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
813 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
814 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
815 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
823 cnf->zapconf = ztc.confno;
825 /* Setup a new channel for playback of audio files */
826 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
828 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
829 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
831 ztc.confno = cnf->zapconf;
832 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
833 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
834 ast_log(LOG_WARNING, "Error setting conference\n");
836 ast_hangup(cnf->chan);
846 /* Fill the conference struct */
847 cnf->start = time(NULL);
848 cnf->maxusers = 0x7fffffff;
849 cnf->isdynamic = dynamic ? 1 : 0;
850 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
851 AST_LIST_INSERT_HEAD(&confs, cnf, list);
853 /* Reserve conference number in map */
854 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
855 conf_map[confno_int] = 1;
859 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
861 AST_LIST_UNLOCK(&confs);
867 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
869 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
871 int len = strlen(word);
873 struct ast_conference *cnf = NULL;
874 struct ast_conf_user *usr = NULL;
877 char *myline, *ret = NULL;
879 if (pos == 1) { /* Command */
880 return ast_cli_complete(word, cmds, state);
881 } else if (pos == 2) { /* Conference Number */
882 AST_LIST_LOCK(&confs);
883 AST_LIST_TRAVERSE(&confs, cnf, list) {
884 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
889 ret = ast_strdup(ret); /* dup before releasing the lock */
890 AST_LIST_UNLOCK(&confs);
892 } else if (pos == 3) {
893 /* User Number || Conf Command option*/
894 if (strstr(line, "mute") || strstr(line, "kick")) {
895 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
896 return ast_strdup("all");
898 AST_LIST_LOCK(&confs);
900 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
901 myline = ast_strdupa(line);
902 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
903 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
907 AST_LIST_TRAVERSE(&confs, cnf, list) {
908 if (!strcmp(confno, cnf->confno))
913 /* Search for the user */
914 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
915 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
916 if (!strncasecmp(word, usrno, len) && ++which > state)
920 AST_LIST_UNLOCK(&confs);
921 return usr ? ast_strdup(usrno) : NULL;
922 } else if ( strstr(line, "list") && ( 0 == state ) )
923 return ast_strdup("concise");
929 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
931 /* Process the command */
932 struct ast_conference *cnf;
933 struct ast_conf_user *user;
935 int i = 0, total = 0;
937 char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
938 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
939 char cmdline[1024] = "";
943 e->command = "meetme";
945 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
946 " Executes a command for the conference or on a conferee\n";
949 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
953 ast_cli(a->fd, "Invalid Arguments.\n");
954 /* Check for length so no buffer will overflow... */
955 for (i = 0; i < a->argc; i++) {
956 if (strlen(a->argv[i]) > 100)
957 ast_cli(a->fd, "Invalid Arguments.\n");
960 /* 'MeetMe': List all the conferences */
962 AST_LIST_LOCK(&confs);
963 if (AST_LIST_EMPTY(&confs)) {
964 ast_cli(a->fd, "No active MeetMe conferences.\n");
965 AST_LIST_UNLOCK(&confs);
968 ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
969 AST_LIST_TRAVERSE(&confs, cnf, list) {
970 if (cnf->markedusers == 0)
971 strcpy(cmdline, "N/A ");
973 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
974 hr = (now - cnf->start) / 3600;
975 min = ((now - cnf->start) % 3600) / 60;
976 sec = (now - cnf->start) % 60;
978 ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
982 AST_LIST_UNLOCK(&confs);
983 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
987 return CLI_SHOWUSAGE;
988 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
989 if (strstr(a->argv[1], "lock")) {
990 if (strcmp(a->argv[1], "lock") == 0) {
992 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
995 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
997 } else if (strstr(a->argv[1], "mute")) {
999 return CLI_SHOWUSAGE;
1000 if (strcmp(a->argv[1], "mute") == 0) {
1002 if (strcmp(a->argv[3], "all") == 0) {
1003 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
1005 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
1006 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1010 if (strcmp(a->argv[3], "all") == 0) {
1011 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
1013 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
1014 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1017 } else if (strcmp(a->argv[1], "kick") == 0) {
1019 return CLI_SHOWUSAGE;
1020 if (strcmp(a->argv[3], "all") == 0) {
1022 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1024 /* Kick a single user */
1025 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1026 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1028 } else if(strcmp(a->argv[1], "list") == 0) {
1029 int concise = ( 4 == a->argc && ( !strcasecmp(a->argv[3], "concise") ) );
1030 /* List all the users in a conference */
1031 if (AST_LIST_EMPTY(&confs)) {
1033 ast_cli(a->fd, "No active conferences.\n");
1036 /* Find the right conference */
1037 AST_LIST_LOCK(&confs);
1038 AST_LIST_TRAVERSE(&confs, cnf, list) {
1039 if (strcmp(cnf->confno, a->argv[2]) == 0)
1044 ast_cli(a->fd, "No such conference: %s.\n",a->argv[2]);
1045 AST_LIST_UNLOCK(&confs);
1048 /* Show all the users */
1050 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1051 hr = (now - user->jointime) / 3600;
1052 min = ((now - user->jointime) % 3600) / 60;
1053 sec = (now - user->jointime) % 60;
1055 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1057 S_OR(user->chan->cid.cid_num, "<unknown>"),
1058 S_OR(user->chan->cid.cid_name, "<no name>"),
1060 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1061 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1062 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1063 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1064 istalking(user->talking), hr, min, sec);
1066 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1068 S_OR(user->chan->cid.cid_num, ""),
1069 S_OR(user->chan->cid.cid_name, ""),
1071 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1072 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1073 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1074 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1075 user->talking, hr, min, sec);
1079 ast_cli(a->fd,"%d users in that conference.\n",cnf->users);
1080 AST_LIST_UNLOCK(&confs);
1083 return CLI_SHOWUSAGE;
1085 ast_debug(1, "Cmdline: %s\n", cmdline);
1087 admin_exec(NULL, cmdline);
1092 static const char *sla_hold_str(unsigned int hold_access)
1094 const char *hold = "Unknown";
1096 switch (hold_access) {
1100 case SLA_HOLD_PRIVATE:
1109 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1111 const struct sla_trunk *trunk;
1115 e->command = "sla show trunks";
1117 "Usage: sla show trunks\n"
1118 " This will list all trunks defined in sla.conf\n";
1125 "=============================================================\n"
1126 "=== Configured SLA Trunks ===================================\n"
1127 "=============================================================\n"
1129 AST_RWLIST_RDLOCK(&sla_trunks);
1130 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1131 struct sla_station_ref *station_ref;
1132 char ring_timeout[16] = "(none)";
1133 if (trunk->ring_timeout)
1134 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1135 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1136 "=== Trunk Name: %s\n"
1137 "=== ==> Device: %s\n"
1138 "=== ==> AutoContext: %s\n"
1139 "=== ==> RingTimeout: %s\n"
1140 "=== ==> BargeAllowed: %s\n"
1141 "=== ==> HoldAccess: %s\n"
1142 "=== ==> Stations ...\n",
1143 trunk->name, trunk->device,
1144 S_OR(trunk->autocontext, "(none)"),
1146 trunk->barge_disabled ? "No" : "Yes",
1147 sla_hold_str(trunk->hold_access));
1148 AST_RWLIST_RDLOCK(&sla_stations);
1149 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1150 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1151 AST_RWLIST_UNLOCK(&sla_stations);
1152 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1154 AST_RWLIST_UNLOCK(&sla_trunks);
1155 ast_cli(a->fd, "=============================================================\n\n");
1160 static const char *trunkstate2str(enum sla_trunk_state state)
1162 #define S(e) case e: return # e;
1164 S(SLA_TRUNK_STATE_IDLE)
1165 S(SLA_TRUNK_STATE_RINGING)
1166 S(SLA_TRUNK_STATE_UP)
1167 S(SLA_TRUNK_STATE_ONHOLD)
1168 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1170 return "Uknown State";
1174 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1176 const struct sla_station *station;
1180 e->command = "sla show stations";
1182 "Usage: sla show stations\n"
1183 " This will list all stations defined in sla.conf\n";
1190 "=============================================================\n"
1191 "=== Configured SLA Stations =================================\n"
1192 "=============================================================\n"
1194 AST_RWLIST_RDLOCK(&sla_stations);
1195 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1196 struct sla_trunk_ref *trunk_ref;
1197 char ring_timeout[16] = "(none)";
1198 char ring_delay[16] = "(none)";
1199 if (station->ring_timeout) {
1200 snprintf(ring_timeout, sizeof(ring_timeout),
1201 "%u", station->ring_timeout);
1203 if (station->ring_delay) {
1204 snprintf(ring_delay, sizeof(ring_delay),
1205 "%u", station->ring_delay);
1207 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1208 "=== Station Name: %s\n"
1209 "=== ==> Device: %s\n"
1210 "=== ==> AutoContext: %s\n"
1211 "=== ==> RingTimeout: %s\n"
1212 "=== ==> RingDelay: %s\n"
1213 "=== ==> HoldAccess: %s\n"
1214 "=== ==> Trunks ...\n",
1215 station->name, station->device,
1216 S_OR(station->autocontext, "(none)"),
1217 ring_timeout, ring_delay,
1218 sla_hold_str(station->hold_access));
1219 AST_RWLIST_RDLOCK(&sla_trunks);
1220 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1221 if (trunk_ref->ring_timeout) {
1222 snprintf(ring_timeout, sizeof(ring_timeout),
1223 "%u", trunk_ref->ring_timeout);
1225 strcpy(ring_timeout, "(none)");
1226 if (trunk_ref->ring_delay) {
1227 snprintf(ring_delay, sizeof(ring_delay),
1228 "%u", trunk_ref->ring_delay);
1230 strcpy(ring_delay, "(none)");
1231 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1232 "=== ==> State: %s\n"
1233 "=== ==> RingTimeout: %s\n"
1234 "=== ==> RingDelay: %s\n",
1235 trunk_ref->trunk->name,
1236 trunkstate2str(trunk_ref->state),
1237 ring_timeout, ring_delay);
1239 AST_RWLIST_UNLOCK(&sla_trunks);
1240 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1243 AST_RWLIST_UNLOCK(&sla_stations);
1244 ast_cli(a->fd, "============================================================\n"
1250 static struct ast_cli_entry cli_meetme[] = {
1251 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1252 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1253 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1256 static void conf_flush(int fd, struct ast_channel *chan)
1260 /* read any frames that may be waiting on the channel
1264 struct ast_frame *f;
1266 /* when no frames are available, this will wait
1267 for 1 millisecond maximum
1269 while (ast_waitfor(chan, 1)) {
1273 else /* channel was hung up or something else happened */
1278 /* flush any data sitting in the pseudo channel */
1280 if (ioctl(fd, ZT_FLUSH, &x))
1281 ast_log(LOG_WARNING, "Error flushing channel\n");
1285 /* Remove the conference from the list and free it.
1286 We assume that this was called while holding conflock. */
1287 static int conf_free(struct ast_conference *conf)
1291 AST_LIST_REMOVE(&confs, conf, list);
1292 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1294 if (conf->recording == MEETME_RECORD_ACTIVE) {
1295 conf->recording = MEETME_RECORD_TERMINATE;
1296 AST_LIST_UNLOCK(&confs);
1299 AST_LIST_LOCK(&confs);
1300 if (conf->recording == MEETME_RECORD_OFF)
1302 AST_LIST_UNLOCK(&confs);
1306 for (x=0;x<AST_FRAME_BITS;x++) {
1307 if (conf->transframe[x])
1308 ast_frfree(conf->transframe[x]);
1309 if (conf->transpath[x])
1310 ast_translator_free_path(conf->transpath[x]);
1312 if (conf->origframe)
1313 ast_frfree(conf->origframe);
1315 ast_hangup(conf->lchan);
1317 ast_hangup(conf->chan);
1321 ast_mutex_destroy(&conf->playlock);
1322 ast_mutex_destroy(&conf->listenlock);
1323 ast_mutex_destroy(&conf->recordthreadlock);
1329 static void conf_queue_dtmf(const struct ast_conference *conf,
1330 const struct ast_conf_user *sender, struct ast_frame *f)
1332 struct ast_conf_user *user;
1334 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1337 if (ast_write(user->chan, f) < 0)
1338 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1342 static void sla_queue_event_full(enum sla_event_type type,
1343 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1345 struct sla_event *event;
1347 if (!(event = ast_calloc(1, sizeof(*event))))
1351 event->trunk_ref = trunk_ref;
1352 event->station = station;
1355 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1359 ast_mutex_lock(&sla.lock);
1360 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1361 ast_cond_signal(&sla.cond);
1362 ast_mutex_unlock(&sla.lock);
1365 static void sla_queue_event_nolock(enum sla_event_type type)
1367 sla_queue_event_full(type, NULL, NULL, 0);
1370 static void sla_queue_event(enum sla_event_type type)
1372 sla_queue_event_full(type, NULL, NULL, 1);
1375 /*! \brief Queue a SLA event from the conference */
1376 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1377 struct ast_conference *conf)
1379 struct sla_station *station;
1380 struct sla_trunk_ref *trunk_ref = NULL;
1383 trunk_name = ast_strdupa(conf->confno);
1384 strsep(&trunk_name, "_");
1385 if (ast_strlen_zero(trunk_name)) {
1386 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1390 AST_RWLIST_RDLOCK(&sla_stations);
1391 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1392 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1393 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1399 AST_RWLIST_UNLOCK(&sla_stations);
1402 ast_debug(1, "Trunk not found for event!\n");
1406 sla_queue_event_full(type, trunk_ref, station, 1);
1409 /* Decrement reference counts, as incremented by find_conf() */
1410 static int dispose_conf(struct ast_conference *conf)
1415 AST_LIST_LOCK(&confs);
1416 if (ast_atomic_dec_and_test(&conf->refcount)) {
1417 /* Take the conference room number out of an inuse state */
1418 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1419 conf_map[confno_int] = 0;
1423 AST_LIST_UNLOCK(&confs);
1429 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1431 struct ast_conf_user *user = NULL;
1432 struct ast_conf_user *usr = NULL;
1434 struct zt_confinfo ztc, ztc_empty;
1435 struct ast_frame *f;
1436 struct ast_channel *c;
1437 struct ast_frame fr;
1445 int musiconhold = 0;
1448 int currentmarked = 0;
1451 int menu_active = 0;
1452 int talkreq_manager = 0;
1453 int using_pseudo = 0;
1458 int announcement_played = 0;
1460 struct ast_dsp *dsp=NULL;
1461 struct ast_app *app;
1462 const char *agifile;
1463 const char *agifiledefault = "conf-background.agi";
1464 char meetmesecs[30] = "";
1465 char exitcontext[AST_MAX_CONTEXT] = "";
1466 char recordingtmp[AST_MAX_EXTENSION] = "";
1467 char members[10] = "";
1468 int dtmf, opt_waitmarked_timeout = 0;
1471 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1472 char *buf = __buf + AST_FRIENDLY_OFFSET;
1473 char *exitkeys = NULL;
1475 if (!(user = ast_calloc(1, sizeof(*user))))
1478 /* Possible timeout waiting for marked user */
1479 if ((confflags & CONFFLAG_WAITMARKED) &&
1480 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1481 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1482 (opt_waitmarked_timeout > 0)) {
1483 timeout = time(NULL) + opt_waitmarked_timeout;
1487 if ((confflags & CONFFLAG_KEYEXIT)) {
1488 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1489 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1491 exitkeys = ast_strdupa("#"); /* Default */
1494 if (confflags & CONFFLAG_RECORDCONF) {
1495 if (!conf->recordingfilename) {
1496 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1497 if (!conf->recordingfilename) {
1498 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1499 conf->recordingfilename = ast_strdupa(recordingtmp);
1501 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1502 if (!conf->recordingformat) {
1503 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1504 conf->recordingformat = ast_strdupa(recordingtmp);
1506 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1507 conf->confno, conf->recordingfilename, conf->recordingformat);
1511 ast_mutex_lock(&conf->recordthreadlock);
1512 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1513 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1514 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1516 ztc.confno = conf->zapconf;
1517 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1518 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1519 ast_log(LOG_WARNING, "Error starting listen channel\n");
1520 ast_hangup(conf->lchan);
1523 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1526 ast_mutex_unlock(&conf->recordthreadlock);
1528 time(&user->jointime);
1530 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1531 /* Sorry, but this conference is locked! */
1532 if (!ast_streamfile(chan, "conf-locked", chan->language))
1533 ast_waitstream(chan, "");
1537 if (confflags & CONFFLAG_MARKEDUSER)
1538 conf->markedusers++;
1540 ast_mutex_lock(&conf->playlock);
1542 if (AST_LIST_EMPTY(&conf->userlist))
1545 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1547 if (rt_schedule && conf->maxusers)
1548 if (user->user_no > conf->maxusers){
1549 /* Sorry, but this confernce has reached the participant limit! */
1550 if (!ast_streamfile(chan, "conf-full", chan->language))
1551 ast_waitstream(chan, "");
1555 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1558 user->userflags = confflags;
1559 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1562 if (rt_log_members) {
1564 snprintf(members, sizeof(members), "%d", conf->users);
1565 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1567 /* This device changed state now - if this is the first user */
1568 if (conf->users == 1)
1569 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1571 ast_mutex_unlock(&conf->playlock);
1573 /* return the unique ID of the conference */
1574 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1576 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1577 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1578 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1579 else if (!ast_strlen_zero(chan->macrocontext))
1580 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1582 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1585 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1586 snprintf(user->namerecloc, sizeof(user->namerecloc),
1587 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1588 conf->confno, user->user_no);
1589 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1590 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1592 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1597 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1598 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1599 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1600 ast_waitstream(chan, "");
1601 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1602 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1603 ast_waitstream(chan, "");
1606 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1607 int keepplaying = 1;
1609 if (conf->users == 2) {
1610 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1611 res = ast_waitstream(chan, AST_DIGIT_ANY);
1612 ast_stopstream(chan);
1619 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1620 res = ast_waitstream(chan, AST_DIGIT_ANY);
1621 ast_stopstream(chan);
1628 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1634 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1635 res = ast_waitstream(chan, AST_DIGIT_ANY);
1636 ast_stopstream(chan);
1645 ast_indicate(chan, -1);
1647 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1648 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1652 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1653 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1657 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
1658 user->zapchannel = !retryzap;
1661 origfd = chan->fds[0];
1663 fd = open("/dev/zap/pseudo", O_RDWR);
1665 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1669 /* Make non-blocking */
1670 flags = fcntl(fd, F_GETFL);
1672 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1676 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1677 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1681 /* Setup buffering information */
1682 memset(&bi, 0, sizeof(bi));
1683 bi.bufsize = CONF_SIZE/2;
1684 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1685 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1686 bi.numbufs = audio_buffers;
1687 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1688 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1693 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1694 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1700 /* XXX Make sure we're not running on a pseudo channel XXX */
1704 memset(&ztc, 0, sizeof(ztc));
1705 memset(&ztc_empty, 0, sizeof(ztc_empty));
1706 /* Check to see if we're in a conference... */
1708 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1709 ast_log(LOG_WARNING, "Error getting conference\n");
1714 /* Whoa, already in a conference... Retry... */
1716 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1721 memset(&ztc, 0, sizeof(ztc));
1722 /* Add us to the conference */
1724 ztc.confno = conf->zapconf;
1726 ast_mutex_lock(&conf->playlock);
1728 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1729 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1730 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1731 ast_waitstream(conf->chan, "");
1732 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1733 ast_waitstream(conf->chan, "");
1737 if (confflags & CONFFLAG_MONITOR)
1738 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1739 else if (confflags & CONFFLAG_TALKER)
1740 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1742 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1744 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1745 ast_log(LOG_WARNING, "Error setting conference\n");
1747 ast_mutex_unlock(&conf->playlock);
1750 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1753 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1758 chan->name, chan->uniqueid, conf->confno, user->user_no);
1762 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1764 if (!(confflags & CONFFLAG_QUIET))
1765 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1766 conf_play(chan, conf, ENTER);
1769 ast_mutex_unlock(&conf->playlock);
1771 conf_flush(fd, chan);
1773 if (confflags & CONFFLAG_AGI) {
1774 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1775 or use default filename of conf-background.agi */
1777 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1779 agifile = agifiledefault;
1781 if (user->zapchannel) {
1782 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1784 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1786 /* Find a pointer to the agi app and execute the script */
1787 app = pbx_findapp("agi");
1789 char *s = ast_strdupa(agifile);
1790 ret = pbx_exec(chan, app, s);
1792 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1795 if (user->zapchannel) {
1796 /* Remove CONFMUTE mode on Zap channel */
1798 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1801 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1802 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1804 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1806 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1807 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1811 int menu_was_active = 0;
1814 char currenttime[32];
1818 if (now.tv_sec % 60 == 0) {
1820 ast_localtime(&now, &tm, NULL);
1821 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1822 if (strcmp(currenttime, conf->endtime) > 0){
1823 ast_verbose("Quitting time...\n");
1827 if (!announcement_played && conf->endalert) {
1828 now.tv_sec += conf->endalert;
1829 ast_localtime(&now, &tm, NULL);
1830 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1831 if (strcmp(currenttime, conf->endtime) > 0){
1832 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1833 ast_waitstream(chan, "");
1834 ast_say_digits(chan, conf->endalert / 60, "", chan->language);
1835 if (!ast_streamfile(chan, "minutes", chan->language))
1836 ast_waitstream(chan, "");
1837 announcement_played = 1;
1852 if (timeout && time(NULL) >= timeout)
1855 /* if we have just exited from the menu, and the user had a channel-driver
1856 volume adjustment, restore it
1858 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1859 set_talk_volume(user, user->listen.desired);
1861 menu_was_active = menu_active;
1863 currentmarked = conf->markedusers;
1864 if (!(confflags & CONFFLAG_QUIET) &&
1865 (confflags & CONFFLAG_MARKEDUSER) &&
1866 (confflags & CONFFLAG_WAITMARKED) &&
1868 if (currentmarked == 1 && conf->users > 1) {
1869 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1870 if (conf->users - 1 == 1) {
1871 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1872 ast_waitstream(chan, "");
1874 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1875 ast_waitstream(chan, "");
1878 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1879 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1880 ast_waitstream(chan, "");
1883 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1886 /* Update the struct with the actual confflags */
1887 user->userflags = confflags;
1889 if (confflags & CONFFLAG_WAITMARKED) {
1890 if(currentmarked == 0) {
1891 if (lastmarked != 0) {
1892 if (!(confflags & CONFFLAG_QUIET))
1893 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1894 ast_waitstream(chan, "");
1895 if (confflags & CONFFLAG_MARKEDEXIT) {
1896 if (confflags & CONFFLAG_KICK_CONTINUE)
1900 ztc.confmode = ZT_CONF_CONF;
1901 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1902 ast_log(LOG_WARNING, "Error setting conference\n");
1908 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1909 ast_moh_start(chan, NULL, NULL);
1912 } else if(currentmarked >= 1 && lastmarked == 0) {
1913 /* Marked user entered, so cancel timeout */
1915 if (confflags & CONFFLAG_MONITOR)
1916 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1917 else if (confflags & CONFFLAG_TALKER)
1918 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1920 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1921 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1922 ast_log(LOG_WARNING, "Error setting conference\n");
1926 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1930 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1931 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1932 ast_waitstream(chan, "");
1933 conf_play(chan, conf, ENTER);
1938 /* trying to add moh for single person conf */
1939 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1940 if (conf->users == 1) {
1941 if (musiconhold == 0) {
1942 ast_moh_start(chan, NULL, NULL);
1953 /* Leave if the last marked user left */
1954 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1955 if (confflags & CONFFLAG_KICK_CONTINUE)
1962 /* Check if my modes have changed */
1964 /* If I should be muted but am still talker, mute me */
1965 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1966 ztc.confmode ^= ZT_CONF_TALKER;
1967 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1968 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1973 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1979 chan->name, chan->uniqueid, conf->confno, user->user_no);
1982 /* If I should be un-muted but am not talker, un-mute me */
1983 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1984 ztc.confmode |= ZT_CONF_TALKER;
1985 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1986 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1991 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1997 chan->name, chan->uniqueid, conf->confno, user->user_no);
2000 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2001 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2002 talkreq_manager = 1;
2004 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2010 chan->name, chan->uniqueid, conf->confno, user->user_no);
2014 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2015 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2016 talkreq_manager = 0;
2017 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2023 chan->name, chan->uniqueid, conf->confno, user->user_no);
2026 /* If I have been kicked, exit the conference */
2027 if (user->adminflags & ADMINFLAG_KICKME) {
2028 //You have been kicked.
2029 if (!(confflags & CONFFLAG_QUIET) &&
2030 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2031 ast_waitstream(chan, "");
2037 /* Perform an extra hangup check just in case */
2038 if (ast_check_hangup(chan))
2042 if (c->fds[0] != origfd || (user->zapchannel && chan->audiohooks)) {
2044 /* Kill old pseudo */
2048 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2049 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
2050 user->zapchannel = !retryzap;
2053 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2054 f = ast_read_noaudio(c);
2059 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2060 if (user->talk.actual)
2061 ast_frame_adjust_volume(f, user->talk.actual);
2063 if (!(confflags & CONFFLAG_MONITOR)) {
2066 if (user->talking == -1)
2069 res = ast_dsp_silence(dsp, f, &totalsilence);
2070 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2072 if (confflags & CONFFLAG_MONITORTALKER)
2073 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2079 chan->name, chan->uniqueid, conf->confno, user->user_no);
2081 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2083 if (confflags & CONFFLAG_MONITORTALKER)
2084 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2090 chan->name, chan->uniqueid, conf->confno, user->user_no);
2094 /* Absolutely do _not_ use careful_write here...
2095 it is important that we read data from the channel
2096 as fast as it arrives, and feed it into the conference.
2097 The buffering in the pseudo channel will take care of any
2098 timing differences, unless they are so drastic as to lose
2099 audio frames (in which case carefully writing would only
2100 have delayed the audio even further).
2102 /* As it turns out, we do want to use careful write. We just
2103 don't want to block, but we do want to at least *try*
2104 to write out all the samples.
2107 careful_write(fd, f->data, f->datalen, 0);
2109 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2112 if (confflags & CONFFLAG_PASS_DTMF)
2113 conf_queue_dtmf(conf, user, f);
2115 tmp[0] = f->subclass;
2117 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2118 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2123 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2125 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2128 exitkey[0] = f->subclass;
2131 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2133 if (confflags & CONFFLAG_PASS_DTMF)
2134 conf_queue_dtmf(conf, user, f);
2138 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2139 if (confflags & CONFFLAG_PASS_DTMF)
2140 conf_queue_dtmf(conf, user, f);
2141 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2142 ast_log(LOG_WARNING, "Error setting conference\n");
2148 /* if we are entering the menu, and the user has a channel-driver
2149 volume adjustment, clear it
2151 if (!menu_active && user->talk.desired && !user->talk.actual)
2152 set_talk_volume(user, 0);
2157 if ((confflags & CONFFLAG_ADMIN)) {
2161 /* Record this sound! */
2162 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2163 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2164 ast_stopstream(chan);
2171 case '1': /* Un/Mute */
2174 /* for admin, change both admin and use flags */
2175 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2176 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2178 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2180 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2181 if (!ast_streamfile(chan, "conf-muted", chan->language))
2182 ast_waitstream(chan, "");
2184 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2185 ast_waitstream(chan, "");
2188 case '2': /* Un/Lock the Conference */
2192 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2193 ast_waitstream(chan, "");
2196 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2197 ast_waitstream(chan, "");
2200 case '3': /* Eject last user */
2202 usr = AST_LIST_LAST(&conf->userlist);
2203 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2204 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2205 ast_waitstream(chan, "");
2207 usr->adminflags |= ADMINFLAG_KICKME;
2208 ast_stopstream(chan);
2211 tweak_listen_volume(user, VOL_DOWN);
2214 tweak_listen_volume(user, VOL_UP);
2217 tweak_talk_volume(user, VOL_DOWN);
2223 tweak_talk_volume(user, VOL_UP);
2227 /* Play an error message! */
2228 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2229 ast_waitstream(chan, "");
2237 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2238 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2239 ast_stopstream(chan);
2246 case '1': /* Un/Mute */
2249 /* user can only toggle the self-muted state */
2250 user->adminflags ^= ADMINFLAG_SELFMUTED;
2252 /* they can't override the admin mute state */
2253 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2254 if (!ast_streamfile(chan, "conf-muted", chan->language))
2255 ast_waitstream(chan, "");
2257 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2258 ast_waitstream(chan, "");
2263 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2264 user->adminflags |= ADMINFLAG_T_REQUEST;
2266 if (user->adminflags & ADMINFLAG_T_REQUEST)
2267 if (!ast_streamfile(chan, "beep", chan->language))
2268 ast_waitstream(chan, "");
2271 tweak_listen_volume(user, VOL_DOWN);
2274 tweak_listen_volume(user, VOL_UP);
2277 tweak_talk_volume(user, VOL_DOWN);
2283 tweak_talk_volume(user, VOL_UP);
2287 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2288 ast_waitstream(chan, "");
2294 ast_moh_start(chan, NULL, NULL);
2296 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2297 ast_log(LOG_WARNING, "Error setting conference\n");
2303 conf_flush(fd, chan);
2304 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2305 && confflags & CONFFLAG_PASS_DTMF) {
2306 conf_queue_dtmf(conf, user, f);
2307 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2308 switch (f->subclass) {
2309 case AST_CONTROL_HOLD:
2310 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2315 } else if (f->frametype == AST_FRAME_NULL) {
2316 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2319 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2320 chan->name, f->frametype, f->subclass);
2323 } else if (outfd > -1) {
2324 res = read(outfd, buf, CONF_SIZE);
2326 memset(&fr, 0, sizeof(fr));
2327 fr.frametype = AST_FRAME_VOICE;
2328 fr.subclass = AST_FORMAT_SLINEAR;
2332 fr.offset = AST_FRIENDLY_OFFSET;
2333 if ( !user->listen.actual &&
2334 ((confflags & CONFFLAG_MONITOR) ||
2335 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2336 (!user->talking)) ) {
2338 for (index=0;index<AST_FRAME_BITS;index++)
2339 if (chan->rawwriteformat & (1 << index))
2341 if (index >= AST_FRAME_BITS)
2342 goto bailoutandtrynormal;
2343 ast_mutex_lock(&conf->listenlock);
2344 if (!conf->transframe[index]) {
2345 if (conf->origframe) {
2346 if (!conf->transpath[index])
2347 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2348 if (conf->transpath[index]) {
2349 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2350 if (!conf->transframe[index])
2351 conf->transframe[index] = &ast_null_frame;
2355 if (conf->transframe[index]) {
2356 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2357 if (ast_write(chan, conf->transframe[index]))
2358 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2361 ast_mutex_unlock(&conf->listenlock);
2362 goto bailoutandtrynormal;
2364 ast_mutex_unlock(&conf->listenlock);
2366 bailoutandtrynormal:
2367 if (user->listen.actual)
2368 ast_frame_adjust_volume(&fr, user->listen.actual);
2369 if (ast_write(chan, &fr) < 0) {
2370 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2374 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2376 lastmarked = currentmarked;
2386 /* Take out of conference */
2390 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2391 ast_log(LOG_WARNING, "Error setting conference\n");
2395 reset_volumes(user);
2397 AST_LIST_LOCK(&confs);
2398 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2399 conf_play(chan, conf, LEAVE);
2401 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2402 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2403 if ((conf->chan) && (conf->users > 1)) {
2404 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2405 ast_waitstream(conf->chan, "");
2406 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2407 ast_waitstream(conf->chan, "");
2409 ast_filedelete(user->namerecloc, NULL);
2412 AST_LIST_UNLOCK(&confs);
2415 AST_LIST_LOCK(&confs);
2420 if (user->user_no) { /* Only cleanup users who really joined! */
2422 hr = (now.tv_sec - user->jointime) / 3600;
2423 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2424 sec = (now.tv_sec - user->jointime) % 60;
2427 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2432 "CallerIDNum: %s\r\n"
2433 "CallerIDName: %s\r\n"
2434 "Duration: %ld\r\n",
2435 chan->name, chan->uniqueid, conf->confno,
2437 S_OR(user->chan->cid.cid_num, "<unknown>"),
2438 S_OR(user->chan->cid.cid_name, "<unknown>"),
2439 (long)(now.tv_sec - user->jointime));
2443 if (rt_log_members){
2445 snprintf(members, sizeof(members), "%d", conf->users);
2446 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2448 if (confflags & CONFFLAG_MARKEDUSER)
2449 conf->markedusers--;
2450 /* Remove ourselves from the list */
2451 AST_LIST_REMOVE(&conf->userlist, user, list);
2453 /* Change any states */
2455 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2457 /* Return the number of seconds the user was in the conf */
2458 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2459 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2462 AST_LIST_UNLOCK(&confs);
2467 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2468 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2469 char *optargs[], int *too_early)
2471 struct ast_variable *var;
2472 struct ast_conference *cnf;
2476 /* Check first in the conference list */
2477 AST_LIST_LOCK(&confs);
2478 AST_LIST_TRAVERSE(&confs, cnf, list) {
2479 if (!strcmp(confno, cnf->confno))
2483 cnf->refcount += refcount;
2485 AST_LIST_UNLOCK(&confs);
2488 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2491 char currenttime[19] = "";
2492 char startTime[19] = "";
2493 char endtime[19] = "";
2494 char eatime[19] = "";
2495 char userOpts[32] = "";
2496 char adminOpts[32] = "";
2497 struct ast_tm tm, etm;
2503 now.tv_sec += fuzzystart;
2505 ast_localtime(&now, &tm, NULL);
2506 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2509 now.tv_sec += earlyalert;
2510 ast_localtime(&now, &etm, NULL);
2511 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2513 ast_copy_string(eatime, currenttime, sizeof(eatime));
2516 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2518 var = ast_load_realtime("meetme", "confno",
2519 confno, "startTime<= ", eatime, "endtime>= ",
2522 var = ast_load_realtime("meetme", "confno", confno, NULL);
2528 if (!strcasecmp(var->name, "pin")) {
2529 pin = ast_strdupa(var->value);
2530 } else if (!strcasecmp(var->name, "adminpin")) {
2531 pinadmin = ast_strdupa(var->value);
2532 } else if (!strcasecmp(var->name, "opts")) {
2533 ast_copy_string(userOpts, var->value, sizeof(userOpts));
2534 } else if (!strcasecmp(var->name, "maxusers")) {
2535 maxusers = atoi(var->value);
2536 } else if (!strcasecmp(var->name, "adminopts")) {
2537 ast_copy_string(adminOpts, var->value, sizeof(adminOpts));
2538 } else if (!strcasecmp(var->name, "endtime")) {
2539 ast_copy_string(endtime, var->value, sizeof(endtime));
2540 } else if (!strcasecmp(var->name, "starttime")) {
2541 ast_copy_string(startTime, var->value, sizeof(startTime));
2546 ast_variables_destroy(var);
2549 if (strcmp(startTime, currenttime) > 0) {
2550 /* Announce that the caller is early and exit */
2551 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2552 ast_waitstream(chan, "");
2558 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2561 cnf->maxusers = maxusers;
2562 cnf->endalert = endalert;
2563 ast_copy_string(cnf->endtime, endtime, sizeof(cnf->endtime));
2568 if (confflags && !cnf->chan &&
2569 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2570 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2571 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2572 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2575 if (confflags && !cnf->chan &&
2576 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2577 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2578 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2586 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2587 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2589 struct ast_config *cfg;
2590 struct ast_variable *var;
2591 struct ast_flags config_flags = { 0 };
2592 struct ast_conference *cnf;
2594 AST_DECLARE_APP_ARGS(args,
2595 AST_APP_ARG(confno);
2597 AST_APP_ARG(pinadmin);
2600 /* Check first in the conference list */
2601 ast_debug(1,"The requested confno is '%s'?\n", confno);
2602 AST_LIST_LOCK(&confs);
2603 AST_LIST_TRAVERSE(&confs, cnf, list) {
2604 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2605 if (!strcmp(confno, cnf->confno))
2609 cnf->refcount += refcount;
2611 AST_LIST_UNLOCK(&confs);
2615 /* No need to parse meetme.conf */
2616 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2618 if (dynamic_pin[0] == 'q') {
2619 /* Query the user to enter a PIN */
2620 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2623 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2625 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2628 /* Check the config */
2629 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2631 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2634 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2635 if (strcasecmp(var->name, "conf"))
2638 if (!(parse = ast_strdupa(var->value)))
2641 AST_STANDARD_APP_ARGS(args, parse);
2642 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2643 if (!strcasecmp(args.confno, confno)) {
2644 /* Bingo it's a valid conference */
2645 cnf = build_conf(args.confno,
2647 S_OR(args.pinadmin, ""),
2648 make, dynamic, refcount, chan);
2653 ast_debug(1, "%s isn't a valid conference\n", confno);
2655 ast_config_destroy(cfg);
2657 } else if (dynamic_pin) {
2658 /* Correct for the user selecting 'D' instead of 'd' to have
2659 someone join into a conference that has already been created
2661 if (dynamic_pin[0] == 'q')
2662 dynamic_pin[0] = '\0';
2666 if (confflags && !cnf->chan &&
2667 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2668 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2669 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2670 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2673 if (confflags && !cnf->chan &&
2674 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2675 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2676 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2683 /*! \brief The MeetmeCount application */
2684 static int count_exec(struct ast_channel *chan, void *data)
2687 struct ast_conference *conf;
2691 AST_DECLARE_APP_ARGS(args,
2692 AST_APP_ARG(confno);
2693 AST_APP_ARG(varname);
2696 if (ast_strlen_zero(data)) {
2697 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2701 if (!(localdata = ast_strdupa(data)))
2704 AST_STANDARD_APP_ARGS(args, localdata);
2706 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2709 count = conf->users;
2715 if (!ast_strlen_zero(args.varname)){
2716 /* have var so load it and exit */
2717 snprintf(val, sizeof(val), "%d",count);
2718 pbx_builtin_setvar_helper(chan, args.varname, val);
2720 if (chan->_state != AST_STATE_UP)
2722 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2728 /*! \brief The meetme() application */
2729 static int conf_exec(struct ast_channel *chan, void *data)
2732 char confno[MAX_CONFNUM] = "";
2735 struct ast_conference *cnf = NULL;
2736 struct ast_flags confflags = {0}, config_flags = { 0 };
2738 int empty = 0, empty_no_pin = 0;
2739 int always_prompt = 0;
2740 char *notdata, *info, the_pin[MAX_PIN] = "";
2741 AST_DECLARE_APP_ARGS(args,
2742 AST_APP_ARG(confno);
2743 AST_APP_ARG(options);
2746 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2748 if (ast_strlen_zero(data)) {
2755 if (chan->_state != AST_STATE_UP)
2758 info = ast_strdupa(notdata);
2760 AST_STANDARD_APP_ARGS(args, info);
2763 ast_copy_string(confno, args.confno, sizeof(confno));
2764 if (ast_strlen_zero(confno)) {
2770 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2773 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2774 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2775 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2776 strcpy(the_pin, "q");
2778 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2779 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2780 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2788 struct ast_config *cfg;
2789 struct ast_variable *var;
2792 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2793 if ((empty_no_pin) || (!dynamic)) {
2794 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2796 var = ast_variable_browse(cfg, "rooms");
2798 if (!strcasecmp(var->name, "conf")) {
2799 char *stringp = ast_strdupa(var->value);
2801 char *confno_tmp = strsep(&stringp, "|,");
2804 /* For static: run through the list and see if this conference is empty */
2805 AST_LIST_LOCK(&confs);
2806 AST_LIST_TRAVERSE(&confs, cnf, list) {
2807 if (!strcmp(confno_tmp, cnf->confno)) {
2808 /* The conference exists, therefore it's not empty */
2813 AST_LIST_UNLOCK(&confs);
2815 /* At this point, we have a confno_tmp (static conference) that is empty */
2816 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2817 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2818 * Case 2: empty_no_pin and pin is blank (but not NULL)
2819 * Case 3: not empty_no_pin
2821 ast_copy_string(confno, confno_tmp, sizeof(confno));
2823 /* XXX the map is not complete (but we do have a confno) */
2831 ast_config_destroy(cfg);
2835 /* Select first conference number not in use */
2836 if (ast_strlen_zero(confno) && dynamic) {
2837 AST_LIST_LOCK(&confs);
2838 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2840 snprintf(confno, sizeof(confno), "%d", i);
2845 AST_LIST_UNLOCK(&confs);
2849 if (ast_strlen_zero(confno)) {
2850 res = ast_streamfile(chan, "conf-noempty", chan->language);
2852 ast_waitstream(chan, "");
2854 if (sscanf(confno, "%d", &confno_int) == 1) {
2855 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2856 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2858 ast_waitstream(chan, "");
2859 res = ast_say_digits(chan, confno_int, "", chan->language);
2863 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2868 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2869 /* Prompt user for conference number */
2870 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2872 /* Don't try to validate when we catch an error */
2878 if (!ast_strlen_zero(confno)) {
2879 /* Check the validity of the conference */
2880 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2881 sizeof(the_pin), 1, &confflags);
2884 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2885 the_pin, sizeof(the_pin), 1, &confflags, optargs, &too_early);
2886 if (rt_schedule && too_early)
2893 res = ast_streamfile(chan, "conf-invalid", chan->language);
2895 ast_waitstream(chan, "");
2899 if ((!ast_strlen_zero(cnf->pin) &&
2900 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2901 (!ast_strlen_zero(cnf->pinadmin) &&
2902 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2903 char pin[MAX_PIN] = "";
2906 /* Allow the pin to be retried up to 3 times */
2907 for (j = 0; j < 3; j++) {
2908 if (*the_pin && (always_prompt == 0)) {
2909 ast_copy_string(pin, the_pin, sizeof(pin));
2912 /* Prompt user for pin if pin is required */
2913 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2916 if (!strcasecmp(pin, cnf->pin) ||
2917 (!ast_strlen_zero(cnf->pinadmin) &&
2918 !strcasecmp(pin, cnf->pinadmin))) {
2921 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2922 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2923 /* Run the conference */
2924 res = conf_run(chan, cnf, confflags.flags, optargs);
2928 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2929 res = ast_waitstream(chan, AST_DIGIT_ANY);
2930 ast_stopstream(chan);
2933 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2945 /* failed when getting the pin */
2948 /* see if we need to get rid of the conference */
2952 /* Don't retry pin with a static pin */
2953 if (*the_pin && (always_prompt==0)) {
2958 /* No pin required */
2961 /* Run the conference */
2962 res = conf_run(chan, cnf, confflags.flags, optargs);
2968 } while (allowretry);
2976 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2978 struct ast_conf_user *user = NULL;
2981 sscanf(callerident, "%i", &cid);
2982 if (conf && callerident) {
2983 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2984 if (cid == user->user_no)
2991 /*! \brief The MeetMeadmin application */
2992 /* MeetMeAdmin(confno, command, caller) */
2993 static int admin_exec(struct ast_channel *chan, void *data) {
2995 struct ast_conference *cnf;
2996 struct ast_conf_user *user = NULL;
2997 AST_DECLARE_APP_ARGS(args,
2998 AST_APP_ARG(confno);
2999 AST_APP_ARG(command);
3003 if (ast_strlen_zero(data)) {
3004 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
3008 params = ast_strdupa(data);
3009 AST_STANDARD_APP_ARGS(args, params);
3011 if (!args.command) {
3012 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
3016 AST_LIST_LOCK(&confs);
3017 AST_LIST_TRAVERSE(&confs, cnf, list) {
3018 if (!strcmp(cnf->confno, args.confno))
3023 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
3024 AST_LIST_UNLOCK(&confs);
3028 ast_atomic_fetchadd_int(&cnf->refcount, 1);
3031 user = find_user(cnf, args.user);
3033 switch (*args.command) {
3034 case 76: /* L: Lock */
3037 case 108: /* l: Unlock */
3040 case 75: /* K: kick all users */
3041 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
3042 user->adminflags |= ADMINFLAG_KICKME;
3044 case 101: /* e: Eject last user*/
3045 user = AST_LIST_LAST(&cnf->userlist);