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$")
40 #include "asterisk/zapata.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/options.h"
53 #include "asterisk/cli.h"
54 #include "asterisk/say.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/translate.h"
57 #include "asterisk/ulaw.h"
58 #include "asterisk/astobj.h"
59 #include "asterisk/devicestate.h"
60 #include "asterisk/dial.h"
61 #include "asterisk/causes.h"
66 #define CONFIG_FILE_NAME "meetme.conf"
67 #define SLA_CONFIG_FILE "sla.conf"
69 /*! each buffer is 20ms, so this is 640ms total */
70 #define DEFAULT_AUDIO_BUFFERS 32
72 /*! String format for scheduled conferences */
73 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
76 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
77 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
78 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
79 /*! User has requested to speak */
80 ADMINFLAG_T_REQUEST = (1 << 4),
83 #define MEETME_DELAYDETECTTALK 300
84 #define MEETME_DELAYDETECTENDTALK 1000
86 #define AST_FRAME_BITS 32
98 enum recording_state {
100 MEETME_RECORD_STARTED,
101 MEETME_RECORD_ACTIVE,
102 MEETME_RECORD_TERMINATE
105 #define CONF_SIZE 320
108 /*! user has admin access on the conference */
109 CONFFLAG_ADMIN = (1 << 0),
110 /*! If set the user can only receive audio from the conference */
111 CONFFLAG_MONITOR = (1 << 1),
112 /*! If set asterisk will exit conference when key defined in p() option is pressed */
113 CONFFLAG_KEYEXIT = (1 << 2),
114 /*! If set asterisk will provide a menu to the user when '*' is pressed */
115 CONFFLAG_STARMENU = (1 << 3),
116 /*! If set the use can only send audio to the conference */
117 CONFFLAG_TALKER = (1 << 4),
118 /*! If set there will be no enter or leave sounds */
119 CONFFLAG_QUIET = (1 << 5),
120 /*! If set, when user joins the conference, they will be told the number
121 * of users that are already in */
122 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
123 /*! Set to run AGI Script in Background */
124 CONFFLAG_AGI = (1 << 7),
125 /*! Set to have music on hold when user is alone in conference */
126 CONFFLAG_MOH = (1 << 8),
127 /*! If set the MeetMe will return if all marked with this flag left */
128 CONFFLAG_MARKEDEXIT = (1 << 9),
129 /*! If set, the MeetMe will wait until a marked user enters */
130 CONFFLAG_WAITMARKED = (1 << 10),
131 /*! If set, the MeetMe will exit to the specified context */
132 CONFFLAG_EXIT_CONTEXT = (1 << 11),
133 /*! If set, the user will be marked */
134 CONFFLAG_MARKEDUSER = (1 << 12),
135 /*! If set, user will be ask record name on entry of conference */
136 CONFFLAG_INTROUSER = (1 << 13),
137 /*! If set, the MeetMe will be recorded */
138 CONFFLAG_RECORDCONF = (1<< 14),
139 /*! If set, the user will be monitored if the user is talking or not */
140 CONFFLAG_MONITORTALKER = (1 << 15),
141 CONFFLAG_DYNAMIC = (1 << 16),
142 CONFFLAG_DYNAMICPIN = (1 << 17),
143 CONFFLAG_EMPTY = (1 << 18),
144 CONFFLAG_EMPTYNOPIN = (1 << 19),
145 CONFFLAG_ALWAYSPROMPT = (1 << 20),
146 /*! If set, won't speak the extra prompt when the first person
147 * enters the conference */
148 CONFFLAG_NOONLYPERSON = (1 << 22),
149 /*! If set, user will be asked to record name on entry of conference
151 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
152 /*! If set, the user will be initially self-muted */
153 CONFFLAG_STARTMUTED = (1 << 24),
154 /*! Pass DTMF through the conference */
155 CONFFLAG_PASS_DTMF = (1 << 25),
156 CONFFLAG_SLA_STATION = (1 << 26),
157 CONFFLAG_SLA_TRUNK = (1 << 27),
158 /*! If set, the user should continue in the dialplan if kicked out */
159 CONFFLAG_KICK_CONTINUE = (1 << 28),
160 CONFFLAG_DURATION_STOP = (1 << 29),
161 CONFFLAG_DURATION_LIMIT = (1 << 30),
165 OPT_ARG_WAITMARKED = 0,
166 OPT_ARG_EXITKEYS = 1,
167 OPT_ARG_DURATION_STOP = 2,
168 OPT_ARG_DURATION_LIMIT = 3,
169 OPT_ARG_ARRAY_SIZE = 4,
172 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
173 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
174 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
175 AST_APP_OPTION('b', CONFFLAG_AGI ),
176 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
177 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
178 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
179 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
180 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
181 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
182 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
183 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
184 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
185 AST_APP_OPTION('M', CONFFLAG_MOH ),
186 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
187 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
188 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
189 AST_APP_OPTION('q', CONFFLAG_QUIET ),
190 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
191 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
192 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
193 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
194 AST_APP_OPTION('t', CONFFLAG_TALKER ),
195 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
196 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
197 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
198 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
199 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
200 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
203 static const char *app = "MeetMe";
204 static const char *app2 = "MeetMeCount";
205 static const char *app3 = "MeetMeAdmin";
206 static const char *app4 = "MeetMeChannelAdmin";
207 static const char *slastation_app = "SLAStation";
208 static const char *slatrunk_app = "SLATrunk";
210 static const char *synopsis = "MeetMe conference bridge";
211 static const char *synopsis2 = "MeetMe participant count";
212 static const char *synopsis3 = "MeetMe conference Administration";
213 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
214 static const char *slastation_synopsis = "Shared Line Appearance Station";
215 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
217 /* Lookup RealTime conferences based on confno and current time */
218 static int rt_schedule;
219 static int fuzzystart;
220 static int earlyalert;
223 /* Log participant count to the RealTime backend */
224 static int rt_log_members;
226 static const char *descrip =
227 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
228 "conference. If the conference number is omitted, the user will be prompted\n"
229 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
230 "is specified, by pressing '#'.\n"
231 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
232 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
233 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
234 "The option string may contain zero or more of the following characters:\n"
235 " 'a' -- set admin mode\n"
236 " 'A' -- set marked mode\n"
237 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
238 " Default: conf-background.agi (Note: This does not work with\n"
239 " non-Zap channels in the same conference)\n"
240 " 'c' -- announce user(s) count on joining a conference\n"
241 " 'C' -- continue in dialplan when kicked out of conference\n"
242 " 'd' -- dynamically add conference\n"
243 " 'D' -- dynamically add conference, prompting for a PIN\n"
244 " 'e' -- select an empty conference\n"
245 " 'E' -- select an empty pinless conference\n"
246 " 'F' -- Pass DTMF through the conference.\n"
247 " 'i' -- announce user join/leave with review\n"
248 " 'I' -- announce user join/leave without review\n"
249 " 'l' -- set listen only mode (Listen only, no talking)\n"
250 " 'm' -- set initially muted\n"
251 " 'M' -- enable music on hold when the conference has a single caller\n"
252 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
253 " being muted, meaning (a) No encode is done on transmission and\n"
254 " (b) Received audio that is not registered as talking is omitted\n"
255 " causing no buildup in background noise\n"
257 " -- allow user to exit the conference by pressing '#' (default)\n"
258 " or any of the defined keys. If keys contain '*' this will override\n"
259 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
260 " 'P' -- always prompt for the pin even if it is specified\n"
261 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
262 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
263 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
264 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
266 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
267 " 't' -- set talk only mode. (Talk only, no listening)\n"
268 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
270 " -- wait until the marked user enters the conference\n"
271 " 'x' -- close the conference when last marked user exits\n"
272 " 'X' -- allow user to exit the conference by entering a valid single\n"
273 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
274 " if that variable is not defined.\n"
275 " '1' -- do not play message when first person enters\n"
276 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
277 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
278 " left. Repeat the warning every 'z' ms. The following special\n"
279 " variables can be used with this option:\n"
280 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
281 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
282 " The default is to say the time remaining.\n"
285 static const char *descrip2 =
286 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
287 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
288 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
289 "the channel, unless priority n+1 exists, in which case priority progress will\n"
291 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
293 static const char *descrip3 =
294 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
295 " 'e' -- Eject last user that joined\n"
296 " 'k' -- Kick one user out of conference\n"
297 " 'K' -- Kick all users out of conference\n"
298 " 'l' -- Unlock conference\n"
299 " 'L' -- Lock conference\n"
300 " 'm' -- Unmute one user\n"
301 " 'M' -- Mute one user\n"
302 " 'n' -- Unmute all users in the conference\n"
303 " 'N' -- Mute all non-admin users in the conference\n"
304 " 'r' -- Reset one user's volume settings\n"
305 " 'R' -- Reset all users volume settings\n"
306 " 's' -- Lower entire conference speaking volume\n"
307 " 'S' -- Raise entire conference speaking volume\n"
308 " 't' -- Lower one user's talk volume\n"
309 " 'T' -- Raise one user's talk volume\n"
310 " 'u' -- Lower one user's listen volume\n"
311 " 'U' -- Raise one user's listen volume\n"
312 " 'v' -- Lower entire conference listening volume\n"
313 " 'V' -- Raise entire conference listening volume\n"
316 static const char *descrip4 =
317 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
318 "channel in any coference.\n"
319 " 'k' -- Kick the specified user out of the conference he is in\n"
320 " 'm' -- Unmute the specified user\n"
321 " 'M' -- Mute the specified user\n"
324 static const char *slastation_desc =
325 " SLAStation(station):\n"
326 "This application should be executed by an SLA station. The argument depends\n"
327 "on how the call was initiated. If the phone was just taken off hook, then\n"
328 "the argument \"station\" should be just the station name. If the call was\n"
329 "initiated by pressing a line key, then the station name should be preceded\n"
330 "by an underscore and the trunk name associated with that line button.\n"
331 "For example: \"station1_line1\"."
332 " On exit, this application will set the variable SLASTATION_STATUS to\n"
333 "one of the following values:\n"
334 " FAILURE | CONGESTION | SUCCESS\n"
337 static const char *slatrunk_desc =
338 " SLATrunk(trunk):\n"
339 "This application should be executed by an SLA trunk on an inbound call.\n"
340 "The channel calling this application should correspond to the SLA trunk\n"
341 "with the name \"trunk\" that is being passed as an argument.\n"
342 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
343 "one of the following values:\n"
344 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
347 #define MAX_CONFNUM 80
350 /*! \brief The MeetMe Conference object */
351 struct ast_conference {
352 ast_mutex_t playlock; /*!< Conference specific lock (players) */
353 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
354 char confno[MAX_CONFNUM]; /*!< Conference */
355 struct ast_channel *chan; /*!< Announcements channel */
356 struct ast_channel *lchan; /*!< Listen/Record channel */
357 int fd; /*!< Announcements fd */
358 int zapconf; /*!< Zaptel Conf # */
359 int users; /*!< Number of active users */
360 int markedusers; /*!< Number of marked users */
361 int maxusers; /*!< Participant limit if scheduled */
362 int endalert; /*!< When to play conf ending message */
363 time_t start; /*!< Start time (s) */
364 int refcount; /*!< reference count of usage */
365 enum recording_state recording:2; /*!< recording status */
366 unsigned int isdynamic:1; /*!< Created on the fly? */
367 unsigned int locked:1; /*!< Is the conference locked? */
368 pthread_t recordthread; /*!< thread for recording */
369 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
370 pthread_attr_t attr; /*!< thread attribute */
371 const char *recordingfilename; /*!< Filename to record the Conference into */
372 const char *recordingformat; /*!< Format to record the Conference in */
373 char pin[MAX_PIN]; /*!< If protected by a PIN */
374 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
376 char endtime[19]; /*!< When to end the conf if scheduled */
377 struct ast_frame *transframe[32];
378 struct ast_frame *origframe;
379 struct ast_trans_pvt *transpath[32];
380 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
381 AST_LIST_ENTRY(ast_conference) list;
384 static AST_LIST_HEAD_STATIC(confs, ast_conference);
386 static unsigned int conf_map[1024] = {0, };
389 int desired; /*!< Desired volume adjustment */
390 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
393 /*! \brief The MeetMe User object */
394 struct ast_conf_user {
395 int user_no; /*!< User Number */
396 int userflags; /*!< Flags as set in the conference */
397 int adminflags; /*!< Flags set by the Admin */
398 struct ast_channel *chan; /*!< Connected channel */
399 int talking; /*!< Is user talking */
400 int zapchannel; /*!< Is a Zaptel channel */
401 char usrvalue[50]; /*!< Custom User Value */
402 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
403 time_t jointime; /*!< Time the user joined the conference */
404 time_t kicktime; /*!< Time the user will be kicked from the conference */
405 struct timeval start_time; /*!< Time the user entered into the conference */
406 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
407 long play_warning; /*!< Play a warning when 'y' ms are left */
408 long warning_freq; /*!< Repeat the warning every 'z' ms */
409 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
410 const char *end_sound; /*!< File to play when time is up. */
412 struct volume listen;
413 AST_LIST_ENTRY(ast_conf_user) list;
416 enum sla_which_trunk_refs {
421 enum sla_trunk_state {
422 SLA_TRUNK_STATE_IDLE,
423 SLA_TRUNK_STATE_RINGING,
425 SLA_TRUNK_STATE_ONHOLD,
426 SLA_TRUNK_STATE_ONHOLD_BYME,
429 enum sla_hold_access {
430 /*! This means that any station can put it on hold, and any station
431 * can retrieve the call from hold. */
433 /*! This means that only the station that put the call on hold may
434 * retrieve it from hold. */
438 struct sla_trunk_ref;
441 AST_RWLIST_ENTRY(sla_station) entry;
442 AST_DECLARE_STRING_FIELDS(
443 AST_STRING_FIELD(name);
444 AST_STRING_FIELD(device);
445 AST_STRING_FIELD(autocontext);
447 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
448 struct ast_dial *dial;
449 /*! Ring timeout for this station, for any trunk. If a ring timeout
450 * is set for a specific trunk on this station, that will take
451 * priority over this value. */
452 unsigned int ring_timeout;
453 /*! Ring delay for this station, for any trunk. If a ring delay
454 * is set for a specific trunk on this station, that will take
455 * priority over this value. */
456 unsigned int ring_delay;
457 /*! This option uses the values in the sla_hold_access enum and sets the
458 * access control type for hold on this station. */
459 unsigned int hold_access:1;
460 /*! Use count for inside sla_station_exec */
461 unsigned int ref_count;
464 struct sla_station_ref {
465 AST_LIST_ENTRY(sla_station_ref) entry;
466 struct sla_station *station;
470 AST_RWLIST_ENTRY(sla_trunk) entry;
471 AST_DECLARE_STRING_FIELDS(
472 AST_STRING_FIELD(name);
473 AST_STRING_FIELD(device);
474 AST_STRING_FIELD(autocontext);
476 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
477 /*! Number of stations that use this trunk */
478 unsigned int num_stations;
479 /*! Number of stations currently on a call with this trunk */
480 unsigned int active_stations;
481 /*! Number of stations that have this trunk on hold. */
482 unsigned int hold_stations;
483 struct ast_channel *chan;
484 unsigned int ring_timeout;
485 /*! If set to 1, no station will be able to join an active call with
487 unsigned int barge_disabled:1;
488 /*! This option uses the values in the sla_hold_access enum and sets the
489 * access control type for hold on this trunk. */
490 unsigned int hold_access:1;
491 /*! Whether this trunk is currently on hold, meaning that once a station
492 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
493 unsigned int on_hold:1;
494 /*! Use count for inside sla_trunk_exec */
495 unsigned int ref_count;
498 struct sla_trunk_ref {
499 AST_LIST_ENTRY(sla_trunk_ref) entry;
500 struct sla_trunk *trunk;
501 enum sla_trunk_state state;
502 struct ast_channel *chan;
503 /*! Ring timeout to use when this trunk is ringing on this specific
504 * station. This takes higher priority than a ring timeout set at
505 * the station level. */
506 unsigned int ring_timeout;
507 /*! Ring delay to use when this trunk is ringing on this specific
508 * station. This takes higher priority than a ring delay set at
509 * the station level. */
510 unsigned int ring_delay;
513 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
514 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
516 static const char sla_registrar[] = "SLA";
518 /*! \brief Event types that can be queued up for the SLA thread */
519 enum sla_event_type {
520 /*! A station has put the call on hold */
522 /*! The state of a dial has changed */
523 SLA_EVENT_DIAL_STATE,
524 /*! The state of a ringing trunk has changed */
525 SLA_EVENT_RINGING_TRUNK,
526 /*! A reload of configuration has been requested */
528 /*! Poke the SLA thread so it can check if it can perform a reload */
529 SLA_EVENT_CHECK_RELOAD,
533 enum sla_event_type type;
534 struct sla_station *station;
535 struct sla_trunk_ref *trunk_ref;
536 AST_LIST_ENTRY(sla_event) entry;
539 /*! \brief A station that failed to be dialed
540 * \note Only used by the SLA thread. */
541 struct sla_failed_station {
542 struct sla_station *station;
543 struct timeval last_try;
544 AST_LIST_ENTRY(sla_failed_station) entry;
547 /*! \brief A trunk that is ringing */
548 struct sla_ringing_trunk {
549 struct sla_trunk *trunk;
550 /*! The time that this trunk started ringing */
551 struct timeval ring_begin;
552 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
553 AST_LIST_ENTRY(sla_ringing_trunk) entry;
556 enum sla_station_hangup {
557 SLA_STATION_HANGUP_NORMAL,
558 SLA_STATION_HANGUP_TIMEOUT,
561 /*! \brief A station that is ringing */
562 struct sla_ringing_station {
563 struct sla_station *station;
564 /*! The time that this station started ringing */
565 struct timeval ring_begin;
566 AST_LIST_ENTRY(sla_ringing_station) entry;
570 * \brief A structure for data used by the sla thread
573 /*! The SLA thread ID */
577 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
578 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
579 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
580 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
582 /*! Attempt to handle CallerID, even though it is known not to work
583 * properly in some situations. */
584 unsigned int attempt_callerid:1;
585 /*! A reload has been requested */
586 unsigned int reload:1;
588 .thread = AST_PTHREADT_NULL,
591 /*! The number of audio buffers to be allocated on pseudo channels
592 * when in a conference */
593 static int audio_buffers;
595 /*! Map 'volume' levels from -5 through +5 into
596 * decibel (dB) settings for channel drivers
597 * Note: these are not a straight linear-to-dB
598 * conversion... the numbers have been modified
599 * to give the user a better level of adjustability
601 static char const gain_map[] = {
616 static int admin_exec(struct ast_channel *chan, void *data);
617 static void *recordthread(void *args);
619 static char *istalking(int x)
624 return "(unmonitored)";
626 return "(not talking)";
629 static int careful_write(int fd, unsigned char *data, int len, int block)
636 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
637 res = ioctl(fd, ZT_IOMUX, &x);
641 res = write(fd, data, len);
643 if (errno != EAGAIN) {
644 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
656 static int set_talk_volume(struct ast_conf_user *user, int volume)
660 /* attempt to make the adjustment in the channel driver;
661 if successful, don't adjust in the frame reading routine
663 gain_adjust = gain_map[volume + 5];
665 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
668 static int set_listen_volume(struct ast_conf_user *user, int volume)
672 /* attempt to make the adjustment in the channel driver;
673 if successful, don't adjust in the frame reading routine
675 gain_adjust = gain_map[volume + 5];
677 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
680 static void tweak_volume(struct volume *vol, enum volume_action action)
684 switch (vol->desired) {
699 switch (vol->desired) {
715 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
717 tweak_volume(&user->talk, action);
718 /* attempt to make the adjustment in the channel driver;
719 if successful, don't adjust in the frame reading routine
721 if (!set_talk_volume(user, user->talk.desired))
722 user->talk.actual = 0;
724 user->talk.actual = user->talk.desired;
727 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
729 tweak_volume(&user->listen, action);
730 /* attempt to make the adjustment in the channel driver;
731 if successful, don't adjust in the frame reading routine
733 if (!set_listen_volume(user, user->listen.desired))
734 user->listen.actual = 0;
736 user->listen.actual = user->listen.desired;
739 static void reset_volumes(struct ast_conf_user *user)
741 signed char zero_volume = 0;
743 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
744 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
747 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
753 if (!ast_check_hangup(chan))
754 res = ast_autoservice_start(chan);
756 AST_LIST_LOCK(&confs);
772 careful_write(conf->fd, data, len, 1);
775 AST_LIST_UNLOCK(&confs);
778 ast_autoservice_stop(chan);
782 * \brief Find or create a conference
784 * \param confno The conference name/number
785 * \param pin The regular user pin
786 * \param pinadmin The admin pin
787 * \param make Make the conf if it doesn't exist
788 * \param dynamic Mark the newly created conference as dynamic
789 * \param refcount How many references to mark on the conference
790 * \param chan The asterisk channel
792 * \return A pointer to the conference struct, or NULL if it wasn't found and
793 * make or dynamic were not set.
795 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
797 struct ast_conference *cnf;
798 struct zt_confinfo ztc = { 0, };
801 AST_LIST_LOCK(&confs);
803 AST_LIST_TRAVERSE(&confs, cnf, list) {
804 if (!strcmp(confno, cnf->confno))
808 if (cnf || (!make && !dynamic))
812 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
815 ast_mutex_init(&cnf->playlock);
816 ast_mutex_init(&cnf->listenlock);
817 cnf->recordthread = AST_PTHREADT_NULL;
818 ast_mutex_init(&cnf->recordthreadlock);
819 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
820 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
821 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
822 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
824 /* Setup a new zap conference */
826 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
827 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
828 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
829 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
837 cnf->zapconf = ztc.confno;
839 /* Setup a new channel for playback of audio files */
840 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
842 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
843 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
845 ztc.confno = cnf->zapconf;
846 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
847 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
848 ast_log(LOG_WARNING, "Error setting conference\n");
850 ast_hangup(cnf->chan);
860 /* Fill the conference struct */
861 cnf->start = time(NULL);
862 cnf->maxusers = 0x7fffffff;
863 cnf->isdynamic = dynamic ? 1 : 0;
864 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
865 AST_LIST_INSERT_HEAD(&confs, cnf, list);
867 /* Reserve conference number in map */
868 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
869 conf_map[confno_int] = 1;
873 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
875 AST_LIST_UNLOCK(&confs);
881 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
883 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
885 int len = strlen(word);
887 struct ast_conference *cnf = NULL;
888 struct ast_conf_user *usr = NULL;
891 char *myline, *ret = NULL;
893 if (pos == 1) { /* Command */
894 return ast_cli_complete(word, cmds, state);
895 } else if (pos == 2) { /* Conference Number */
896 AST_LIST_LOCK(&confs);
897 AST_LIST_TRAVERSE(&confs, cnf, list) {
898 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
903 ret = ast_strdup(ret); /* dup before releasing the lock */
904 AST_LIST_UNLOCK(&confs);
906 } else if (pos == 3) {
907 /* User Number || Conf Command option*/
908 if (strstr(line, "mute") || strstr(line, "kick")) {
909 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
910 return ast_strdup("all");
912 AST_LIST_LOCK(&confs);
914 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
915 myline = ast_strdupa(line);
916 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
917 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
921 AST_LIST_TRAVERSE(&confs, cnf, list) {
922 if (!strcmp(confno, cnf->confno))
927 /* Search for the user */
928 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
929 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
930 if (!strncasecmp(word, usrno, len) && ++which > state)
934 AST_LIST_UNLOCK(&confs);
935 return usr ? ast_strdup(usrno) : NULL;
936 } else if ( strstr(line, "list") && ( 0 == state ) )
937 return ast_strdup("concise");
943 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
945 /* Process the command */
946 struct ast_conference *cnf;
947 struct ast_conf_user *user;
949 int i = 0, total = 0;
951 char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
952 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
953 char cmdline[1024] = "";
957 e->command = "meetme";
959 "Usage: meetme concise|(un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
960 " Executes a command for the conference or on a conferee\n";
963 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
967 ast_cli(a->fd, "Invalid Arguments.\n");
968 /* Check for length so no buffer will overflow... */
969 for (i = 0; i < a->argc; i++) {
970 if (strlen(a->argv[i]) > 100)
971 ast_cli(a->fd, "Invalid Arguments.\n");
973 if (a->argc == 1 || ( a->argc == 2 && !strcasecmp(a->argv[1], "concise") )) {
974 /* 'MeetMe': List all the conferences */
975 int concise = ( a->argc == 2 && !strcasecmp(a->argv[1], "concise") );
977 AST_LIST_LOCK(&confs);
978 if (AST_LIST_EMPTY(&confs)) {
980 ast_cli(a->fd, "No active MeetMe conferences.\n");
981 AST_LIST_UNLOCK(&confs);
985 ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
986 AST_LIST_TRAVERSE(&confs, cnf, list) {
987 if (cnf->markedusers == 0)
988 strcpy(cmdline, "N/A ");
990 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
991 hr = (now - cnf->start) / 3600;
992 min = ((now - cnf->start) % 3600) / 60;
993 sec = (now - cnf->start) % 60;
995 ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
997 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1006 total += cnf->users;
1008 AST_LIST_UNLOCK(&confs);
1010 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1014 return CLI_SHOWUSAGE;
1015 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
1016 if (strstr(a->argv[1], "lock")) {
1017 if (strcmp(a->argv[1], "lock") == 0) {
1019 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
1022 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
1024 } else if (strstr(a->argv[1], "mute")) {
1026 return CLI_SHOWUSAGE;
1027 if (strcmp(a->argv[1], "mute") == 0) {
1029 if (strcmp(a->argv[3], "all") == 0) {
1030 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
1032 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
1033 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1037 if (strcmp(a->argv[3], "all") == 0) {
1038 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
1040 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
1041 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1044 } else if (strcmp(a->argv[1], "kick") == 0) {
1046 return CLI_SHOWUSAGE;
1047 if (strcmp(a->argv[3], "all") == 0) {
1049 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1051 /* Kick a single user */
1052 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1053 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1055 } else if(strcmp(a->argv[1], "list") == 0) {
1056 int concise = ( 4 == a->argc && ( !strcasecmp(a->argv[3], "concise") ) );
1057 /* List all the users in a conference */
1058 if (AST_LIST_EMPTY(&confs)) {
1060 ast_cli(a->fd, "No active conferences.\n");
1063 /* Find the right conference */
1064 AST_LIST_LOCK(&confs);
1065 AST_LIST_TRAVERSE(&confs, cnf, list) {
1066 if (strcmp(cnf->confno, a->argv[2]) == 0)
1071 ast_cli(a->fd, "No such conference: %s.\n",a->argv[2]);
1072 AST_LIST_UNLOCK(&confs);
1075 /* Show all the users */
1077 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1078 hr = (now - user->jointime) / 3600;
1079 min = ((now - user->jointime) % 3600) / 60;
1080 sec = (now - user->jointime) % 60;
1082 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1084 S_OR(user->chan->cid.cid_num, "<unknown>"),
1085 S_OR(user->chan->cid.cid_name, "<no name>"),
1087 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1088 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1089 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1090 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1091 istalking(user->talking), hr, min, sec);
1093 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1095 S_OR(user->chan->cid.cid_num, ""),
1096 S_OR(user->chan->cid.cid_name, ""),
1098 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1099 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1100 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1101 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1102 user->talking, hr, min, sec);
1106 ast_cli(a->fd,"%d users in that conference.\n",cnf->users);
1107 AST_LIST_UNLOCK(&confs);
1110 return CLI_SHOWUSAGE;
1112 ast_debug(1, "Cmdline: %s\n", cmdline);
1114 admin_exec(NULL, cmdline);
1119 static const char *sla_hold_str(unsigned int hold_access)
1121 const char *hold = "Unknown";
1123 switch (hold_access) {
1127 case SLA_HOLD_PRIVATE:
1136 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1138 const struct sla_trunk *trunk;
1142 e->command = "sla show trunks";
1144 "Usage: sla show trunks\n"
1145 " This will list all trunks defined in sla.conf\n";
1152 "=============================================================\n"
1153 "=== Configured SLA Trunks ===================================\n"
1154 "=============================================================\n"
1156 AST_RWLIST_RDLOCK(&sla_trunks);
1157 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1158 struct sla_station_ref *station_ref;
1159 char ring_timeout[16] = "(none)";
1160 if (trunk->ring_timeout)
1161 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1162 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1163 "=== Trunk Name: %s\n"
1164 "=== ==> Device: %s\n"
1165 "=== ==> AutoContext: %s\n"
1166 "=== ==> RingTimeout: %s\n"
1167 "=== ==> BargeAllowed: %s\n"
1168 "=== ==> HoldAccess: %s\n"
1169 "=== ==> Stations ...\n",
1170 trunk->name, trunk->device,
1171 S_OR(trunk->autocontext, "(none)"),
1173 trunk->barge_disabled ? "No" : "Yes",
1174 sla_hold_str(trunk->hold_access));
1175 AST_RWLIST_RDLOCK(&sla_stations);
1176 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1177 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1178 AST_RWLIST_UNLOCK(&sla_stations);
1179 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1181 AST_RWLIST_UNLOCK(&sla_trunks);
1182 ast_cli(a->fd, "=============================================================\n\n");
1187 static const char *trunkstate2str(enum sla_trunk_state state)
1189 #define S(e) case e: return # e;
1191 S(SLA_TRUNK_STATE_IDLE)
1192 S(SLA_TRUNK_STATE_RINGING)
1193 S(SLA_TRUNK_STATE_UP)
1194 S(SLA_TRUNK_STATE_ONHOLD)
1195 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1197 return "Uknown State";
1201 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1203 const struct sla_station *station;
1207 e->command = "sla show stations";
1209 "Usage: sla show stations\n"
1210 " This will list all stations defined in sla.conf\n";
1217 "=============================================================\n"
1218 "=== Configured SLA Stations =================================\n"
1219 "=============================================================\n"
1221 AST_RWLIST_RDLOCK(&sla_stations);
1222 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1223 struct sla_trunk_ref *trunk_ref;
1224 char ring_timeout[16] = "(none)";
1225 char ring_delay[16] = "(none)";
1226 if (station->ring_timeout) {
1227 snprintf(ring_timeout, sizeof(ring_timeout),
1228 "%u", station->ring_timeout);
1230 if (station->ring_delay) {
1231 snprintf(ring_delay, sizeof(ring_delay),
1232 "%u", station->ring_delay);
1234 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1235 "=== Station Name: %s\n"
1236 "=== ==> Device: %s\n"
1237 "=== ==> AutoContext: %s\n"
1238 "=== ==> RingTimeout: %s\n"
1239 "=== ==> RingDelay: %s\n"
1240 "=== ==> HoldAccess: %s\n"
1241 "=== ==> Trunks ...\n",
1242 station->name, station->device,
1243 S_OR(station->autocontext, "(none)"),
1244 ring_timeout, ring_delay,
1245 sla_hold_str(station->hold_access));
1246 AST_RWLIST_RDLOCK(&sla_trunks);
1247 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1248 if (trunk_ref->ring_timeout) {
1249 snprintf(ring_timeout, sizeof(ring_timeout),
1250 "%u", trunk_ref->ring_timeout);
1252 strcpy(ring_timeout, "(none)");
1253 if (trunk_ref->ring_delay) {
1254 snprintf(ring_delay, sizeof(ring_delay),
1255 "%u", trunk_ref->ring_delay);
1257 strcpy(ring_delay, "(none)");
1258 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1259 "=== ==> State: %s\n"
1260 "=== ==> RingTimeout: %s\n"
1261 "=== ==> RingDelay: %s\n",
1262 trunk_ref->trunk->name,
1263 trunkstate2str(trunk_ref->state),
1264 ring_timeout, ring_delay);
1266 AST_RWLIST_UNLOCK(&sla_trunks);
1267 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1270 AST_RWLIST_UNLOCK(&sla_stations);
1271 ast_cli(a->fd, "============================================================\n"
1277 static struct ast_cli_entry cli_meetme[] = {
1278 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1279 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1280 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1283 static void conf_flush(int fd, struct ast_channel *chan)
1287 /* read any frames that may be waiting on the channel
1291 struct ast_frame *f;
1293 /* when no frames are available, this will wait
1294 for 1 millisecond maximum
1296 while (ast_waitfor(chan, 1)) {
1300 else /* channel was hung up or something else happened */
1305 /* flush any data sitting in the pseudo channel */
1307 if (ioctl(fd, ZT_FLUSH, &x))
1308 ast_log(LOG_WARNING, "Error flushing channel\n");
1312 /* Remove the conference from the list and free it.
1313 We assume that this was called while holding conflock. */
1314 static int conf_free(struct ast_conference *conf)
1318 AST_LIST_REMOVE(&confs, conf, list);
1319 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1321 if (conf->recording == MEETME_RECORD_ACTIVE) {
1322 conf->recording = MEETME_RECORD_TERMINATE;
1323 AST_LIST_UNLOCK(&confs);
1326 AST_LIST_LOCK(&confs);
1327 if (conf->recording == MEETME_RECORD_OFF)
1329 AST_LIST_UNLOCK(&confs);
1333 for (x=0;x<AST_FRAME_BITS;x++) {
1334 if (conf->transframe[x])
1335 ast_frfree(conf->transframe[x]);
1336 if (conf->transpath[x])
1337 ast_translator_free_path(conf->transpath[x]);
1339 if (conf->origframe)
1340 ast_frfree(conf->origframe);
1342 ast_hangup(conf->lchan);
1344 ast_hangup(conf->chan);
1348 ast_mutex_destroy(&conf->playlock);
1349 ast_mutex_destroy(&conf->listenlock);
1350 ast_mutex_destroy(&conf->recordthreadlock);
1356 static void conf_queue_dtmf(const struct ast_conference *conf,
1357 const struct ast_conf_user *sender, struct ast_frame *f)
1359 struct ast_conf_user *user;
1361 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1364 if (ast_write(user->chan, f) < 0)
1365 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1369 static void sla_queue_event_full(enum sla_event_type type,
1370 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1372 struct sla_event *event;
1374 if (!(event = ast_calloc(1, sizeof(*event))))
1378 event->trunk_ref = trunk_ref;
1379 event->station = station;
1382 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1386 ast_mutex_lock(&sla.lock);
1387 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1388 ast_cond_signal(&sla.cond);
1389 ast_mutex_unlock(&sla.lock);
1392 static void sla_queue_event_nolock(enum sla_event_type type)
1394 sla_queue_event_full(type, NULL, NULL, 0);
1397 static void sla_queue_event(enum sla_event_type type)
1399 sla_queue_event_full(type, NULL, NULL, 1);
1402 /*! \brief Queue a SLA event from the conference */
1403 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1404 struct ast_conference *conf)
1406 struct sla_station *station;
1407 struct sla_trunk_ref *trunk_ref = NULL;
1410 trunk_name = ast_strdupa(conf->confno);
1411 strsep(&trunk_name, "_");
1412 if (ast_strlen_zero(trunk_name)) {
1413 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1417 AST_RWLIST_RDLOCK(&sla_stations);
1418 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1419 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1420 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1426 AST_RWLIST_UNLOCK(&sla_stations);
1429 ast_debug(1, "Trunk not found for event!\n");
1433 sla_queue_event_full(type, trunk_ref, station, 1);
1436 /* Decrement reference counts, as incremented by find_conf() */
1437 static int dispose_conf(struct ast_conference *conf)
1442 AST_LIST_LOCK(&confs);
1443 if (ast_atomic_dec_and_test(&conf->refcount)) {
1444 /* Take the conference room number out of an inuse state */
1445 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1446 conf_map[confno_int] = 0;
1450 AST_LIST_UNLOCK(&confs);
1456 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1458 struct ast_conf_user *user = NULL;
1459 struct ast_conf_user *usr = NULL;
1461 struct zt_confinfo ztc, ztc_empty;
1462 struct ast_frame *f;
1463 struct ast_channel *c;
1464 struct ast_frame fr;
1472 int musiconhold = 0;
1475 int currentmarked = 0;
1478 int menu_active = 0;
1479 int talkreq_manager = 0;
1480 int using_pseudo = 0;
1485 int announcement_played = 0;
1487 struct ast_dsp *dsp=NULL;
1488 struct ast_app *app;
1489 const char *agifile;
1490 const char *agifiledefault = "conf-background.agi";
1491 char meetmesecs[30] = "";
1492 char exitcontext[AST_MAX_CONTEXT] = "";
1493 char recordingtmp[AST_MAX_EXTENSION] = "";
1494 char members[10] = "";
1495 int dtmf, opt_waitmarked_timeout = 0;
1498 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1499 char *buf = __buf + AST_FRIENDLY_OFFSET;
1500 char *exitkeys = NULL;
1502 unsigned int calldurationlimit = 0;
1504 long play_warning = 0;
1505 long warning_freq = 0;
1506 const char *warning_sound = NULL;
1507 const char *end_sound = NULL;
1509 long time_left_ms = 0;
1510 struct timeval nexteventts = { 0, };
1513 if (!(user = ast_calloc(1, sizeof(*user))))
1516 /* Possible timeout waiting for marked user */
1517 if ((confflags & CONFFLAG_WAITMARKED) &&
1518 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1519 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1520 (opt_waitmarked_timeout > 0)) {
1521 timeout = time(NULL) + opt_waitmarked_timeout;
1524 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1525 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1526 if (option_verbose > 2)
1527 ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit);
1530 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1531 char *limit_str, *warning_str, *warnfreq_str;
1534 parse = optargs[OPT_ARG_DURATION_LIMIT];
1535 limit_str = strsep(&parse, ":");
1536 warning_str = strsep(&parse, ":");
1537 warnfreq_str = parse;
1539 timelimit = atol(limit_str);
1541 play_warning = atol(warning_str);
1543 warning_freq = atol(warnfreq_str);
1546 timelimit = play_warning = warning_freq = 0;
1547 warning_sound = NULL;
1548 } else if (play_warning > timelimit) {
1549 if (!warning_freq) {
1552 while (play_warning > timelimit)
1553 play_warning -= warning_freq;
1554 if (play_warning < 1)
1555 play_warning = warning_freq = 0;
1559 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_WARNING_FILE");
1560 warning_sound = var ? var : "timeleft";
1562 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_TIMEOUT_FILE");
1563 end_sound = var ? var : NULL;
1565 /* undo effect of S(x) in case they are both used */
1566 calldurationlimit = 0;
1567 /* more efficient do it like S(x) does since no advanced opts */
1568 if (!play_warning && !end_sound && timelimit) {
1569 calldurationlimit = timelimit / 1000;
1570 timelimit = play_warning = warning_freq = 0;
1572 ast_debug(2, "Limit Data for this call:\n");
1573 ast_debug(2, "- timelimit = %ld\n", timelimit);
1574 ast_debug(2, "- play_warning = %ld\n", play_warning);
1575 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1576 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1577 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1582 if ((confflags & CONFFLAG_KEYEXIT)) {
1583 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1584 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1586 exitkeys = ast_strdupa("#"); /* Default */
1589 if (confflags & CONFFLAG_RECORDCONF) {
1590 if (!conf->recordingfilename) {
1591 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1592 if (!conf->recordingfilename) {
1593 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1594 conf->recordingfilename = ast_strdupa(recordingtmp);
1596 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1597 if (!conf->recordingformat) {
1598 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1599 conf->recordingformat = ast_strdupa(recordingtmp);
1601 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1602 conf->confno, conf->recordingfilename, conf->recordingformat);
1606 ast_mutex_lock(&conf->recordthreadlock);
1607 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1608 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1609 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1611 ztc.confno = conf->zapconf;
1612 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1613 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1614 ast_log(LOG_WARNING, "Error starting listen channel\n");
1615 ast_hangup(conf->lchan);
1618 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1621 ast_mutex_unlock(&conf->recordthreadlock);
1623 time(&user->jointime);
1625 user->timelimit = timelimit;
1626 user->play_warning = play_warning;
1627 user->warning_freq = warning_freq;
1628 user->warning_sound = warning_sound;
1629 user->end_sound = end_sound;
1631 if (calldurationlimit > 0) {
1632 time(&user->kicktime);
1633 user->kicktime = user->kicktime + calldurationlimit;
1636 if (ast_tvzero(user->start_time))
1637 user->start_time = ast_tvnow();
1638 time_left_ms = user->timelimit;
1640 if (user->timelimit) {
1641 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1642 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1645 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1646 /* Sorry, but this conference is locked! */
1647 if (!ast_streamfile(chan, "conf-locked", chan->language))
1648 ast_waitstream(chan, "");
1652 if (confflags & CONFFLAG_MARKEDUSER)
1653 conf->markedusers++;
1655 ast_mutex_lock(&conf->playlock);
1657 if (AST_LIST_EMPTY(&conf->userlist))
1660 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1662 if (rt_schedule && conf->maxusers)
1663 if (user->user_no > conf->maxusers){
1664 /* Sorry, but this confernce has reached the participant limit! */
1665 if (!ast_streamfile(chan, "conf-full", chan->language))
1666 ast_waitstream(chan, "");
1670 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1673 user->userflags = confflags;
1674 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1677 if (rt_log_members) {
1679 snprintf(members, sizeof(members), "%d", conf->users);
1680 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1682 /* This device changed state now - if this is the first user */
1683 if (conf->users == 1)
1684 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1686 ast_mutex_unlock(&conf->playlock);
1688 /* return the unique ID of the conference */
1689 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1691 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1692 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1693 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1694 else if (!ast_strlen_zero(chan->macrocontext))
1695 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1697 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1700 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1701 snprintf(user->namerecloc, sizeof(user->namerecloc),
1702 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1703 conf->confno, user->user_no);
1704 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1705 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1707 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1712 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1713 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1714 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1715 ast_waitstream(chan, "");
1716 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1717 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1718 ast_waitstream(chan, "");
1721 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1722 int keepplaying = 1;
1724 if (conf->users == 2) {
1725 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1726 res = ast_waitstream(chan, AST_DIGIT_ANY);
1727 ast_stopstream(chan);
1734 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1735 res = ast_waitstream(chan, AST_DIGIT_ANY);
1736 ast_stopstream(chan);
1743 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1749 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1750 res = ast_waitstream(chan, AST_DIGIT_ANY);
1751 ast_stopstream(chan);
1760 ast_indicate(chan, -1);
1762 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1763 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1767 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1768 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1772 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
1773 user->zapchannel = !retryzap;
1776 origfd = chan->fds[0];
1778 fd = open("/dev/zap/pseudo", O_RDWR);
1780 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1784 /* Make non-blocking */
1785 flags = fcntl(fd, F_GETFL);
1787 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1791 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1792 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1796 /* Setup buffering information */
1797 memset(&bi, 0, sizeof(bi));
1798 bi.bufsize = CONF_SIZE/2;
1799 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1800 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1801 bi.numbufs = audio_buffers;
1802 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1803 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1808 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1809 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1815 /* XXX Make sure we're not running on a pseudo channel XXX */
1819 memset(&ztc, 0, sizeof(ztc));
1820 memset(&ztc_empty, 0, sizeof(ztc_empty));
1821 /* Check to see if we're in a conference... */
1823 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1824 ast_log(LOG_WARNING, "Error getting conference\n");
1829 /* Whoa, already in a conference... Retry... */
1831 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1836 memset(&ztc, 0, sizeof(ztc));
1837 /* Add us to the conference */
1839 ztc.confno = conf->zapconf;
1841 ast_mutex_lock(&conf->playlock);
1843 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1844 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1845 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1846 ast_waitstream(conf->chan, "");
1847 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1848 ast_waitstream(conf->chan, "");
1852 if (confflags & CONFFLAG_MONITOR)
1853 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1854 else if (confflags & CONFFLAG_TALKER)
1855 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1857 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1859 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1860 ast_log(LOG_WARNING, "Error setting conference\n");
1862 ast_mutex_unlock(&conf->playlock);
1865 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1868 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1873 chan->name, chan->uniqueid, conf->confno, user->user_no);
1877 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1879 if (!(confflags & CONFFLAG_QUIET))
1880 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1881 conf_play(chan, conf, ENTER);
1884 ast_mutex_unlock(&conf->playlock);
1886 conf_flush(fd, chan);
1888 if (confflags & CONFFLAG_AGI) {
1889 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1890 or use default filename of conf-background.agi */
1892 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1894 agifile = agifiledefault;
1896 if (user->zapchannel) {
1897 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1899 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1901 /* Find a pointer to the agi app and execute the script */
1902 app = pbx_findapp("agi");
1904 char *s = ast_strdupa(agifile);
1905 ret = pbx_exec(chan, app, s);
1907 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1910 if (user->zapchannel) {
1911 /* Remove CONFMUTE mode on Zap channel */
1913 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1916 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1917 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1919 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1921 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1922 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1926 int menu_was_active = 0;
1932 char currenttime[32];
1936 if (now.tv_sec % 60 == 0) {
1938 ast_localtime(&now, &tm, NULL);
1939 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1940 if (strcmp(currenttime, conf->endtime) > 0){
1941 ast_verbose("Quitting time...\n");
1945 if (!announcement_played && conf->endalert) {
1946 now.tv_sec += conf->endalert;
1947 ast_localtime(&now, &tm, NULL);
1948 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1949 if (strcmp(currenttime, conf->endtime) > 0){
1950 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1951 ast_waitstream(chan, "");
1952 ast_say_digits(chan, conf->endalert / 60, "", chan->language);
1953 if (!ast_streamfile(chan, "minutes", chan->language))
1954 ast_waitstream(chan, "");
1955 announcement_played = 1;
1967 time(&myt); /* get current time */
1968 if (user->kicktime && (user->kicktime < myt))
1972 if (user->timelimit) {
1974 int minutes = 0, seconds = 0, remain = 0;
1977 to = ast_tvdiff_ms(nexteventts, now);
1980 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
1981 if (time_left_ms < to)
1984 if (time_left_ms <= 0) {
1985 if (user->end_sound){
1986 res = ast_streamfile(chan, user->end_sound, chan->language);
1987 res = ast_waitstream(chan, "");
1993 if (time_left_ms >= 5000) {
1995 remain = (time_left_ms + 500) / 1000;
1996 if (remain / 60 >= 1) {
1997 minutes = remain / 60;
1998 seconds = remain % 60;
2003 /* force the time left to round up if appropriate */
2004 if (user->warning_sound && user->play_warning){
2005 if (!strcmp(user->warning_sound,"timeleft")) {
2007 res = ast_streamfile(chan, "vm-youhave", chan->language);
2008 res = ast_waitstream(chan, "");
2010 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2011 res = ast_streamfile(chan, "queue-minutes", chan->language);
2012 res = ast_waitstream(chan, "");
2015 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2016 res = ast_streamfile(chan, "queue-seconds", chan->language);
2017 res = ast_waitstream(chan, "");
2020 res = ast_streamfile(chan, user->warning_sound, chan->language);
2021 res = ast_waitstream(chan, "");
2025 if (user->warning_freq)
2026 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2028 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2032 if (timeout && time(NULL) >= timeout)
2035 /* if we have just exited from the menu, and the user had a channel-driver
2036 volume adjustment, restore it
2038 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2039 set_talk_volume(user, user->listen.desired);
2041 menu_was_active = menu_active;
2043 currentmarked = conf->markedusers;
2044 if (!(confflags & CONFFLAG_QUIET) &&
2045 (confflags & CONFFLAG_MARKEDUSER) &&
2046 (confflags & CONFFLAG_WAITMARKED) &&
2048 if (currentmarked == 1 && conf->users > 1) {
2049 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2050 if (conf->users - 1 == 1) {
2051 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2052 ast_waitstream(chan, "");
2054 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2055 ast_waitstream(chan, "");
2058 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2059 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2060 ast_waitstream(chan, "");
2063 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2066 /* Update the struct with the actual confflags */
2067 user->userflags = confflags;
2069 if (confflags & CONFFLAG_WAITMARKED) {
2070 if(currentmarked == 0) {
2071 if (lastmarked != 0) {
2072 if (!(confflags & CONFFLAG_QUIET))
2073 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2074 ast_waitstream(chan, "");
2075 if (confflags & CONFFLAG_MARKEDEXIT) {
2076 if (confflags & CONFFLAG_KICK_CONTINUE)
2080 ztc.confmode = ZT_CONF_CONF;
2081 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2082 ast_log(LOG_WARNING, "Error setting conference\n");
2088 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
2089 ast_moh_start(chan, NULL, NULL);
2092 } else if(currentmarked >= 1 && lastmarked == 0) {
2093 /* Marked user entered, so cancel timeout */
2095 if (confflags & CONFFLAG_MONITOR)
2096 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
2097 else if (confflags & CONFFLAG_TALKER)
2098 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
2100 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
2101 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2102 ast_log(LOG_WARNING, "Error setting conference\n");
2106 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2110 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2111 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2112 ast_waitstream(chan, "");
2113 conf_play(chan, conf, ENTER);
2118 /* trying to add moh for single person conf */
2119 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2120 if (conf->users == 1) {
2121 if (musiconhold == 0) {
2122 ast_moh_start(chan, NULL, NULL);
2133 /* Leave if the last marked user left */
2134 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2135 if (confflags & CONFFLAG_KICK_CONTINUE)
2142 /* Check if my modes have changed */
2144 /* If I should be muted but am still talker, mute me */
2145 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
2146 ztc.confmode ^= ZT_CONF_TALKER;
2147 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2148 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2153 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2159 chan->name, chan->uniqueid, conf->confno, user->user_no);
2162 /* If I should be un-muted but am not talker, un-mute me */
2163 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
2164 ztc.confmode |= ZT_CONF_TALKER;
2165 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2166 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2171 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2177 chan->name, chan->uniqueid, conf->confno, user->user_no);
2180 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2181 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2182 talkreq_manager = 1;
2184 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2190 chan->name, chan->uniqueid, conf->confno, user->user_no);
2194 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2195 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2196 talkreq_manager = 0;
2197 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2203 chan->name, chan->uniqueid, conf->confno, user->user_no);
2206 /* If I have been kicked, exit the conference */
2207 if (user->adminflags & ADMINFLAG_KICKME) {
2208 //You have been kicked.
2209 if (!(confflags & CONFFLAG_QUIET) &&
2210 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2211 ast_waitstream(chan, "");
2217 /* Perform an extra hangup check just in case */
2218 if (ast_check_hangup(chan))
2222 if (c->fds[0] != origfd || (user->zapchannel && c->audiohooks)) {
2224 /* Kill old pseudo */
2228 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2229 retryzap = (strcasecmp(c->tech->type, "Zap") || c->audiohooks ? 1 : 0);
2230 user->zapchannel = !retryzap;
2233 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2234 f = ast_read_noaudio(c);
2239 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2240 if (user->talk.actual)
2241 ast_frame_adjust_volume(f, user->talk.actual);
2243 if (!(confflags & CONFFLAG_MONITOR)) {
2246 if (user->talking == -1)
2249 res = ast_dsp_silence(dsp, f, &totalsilence);
2250 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2252 if (confflags & CONFFLAG_MONITORTALKER)
2253 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2259 chan->name, chan->uniqueid, conf->confno, user->user_no);
2261 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2263 if (confflags & CONFFLAG_MONITORTALKER)
2264 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2270 chan->name, chan->uniqueid, conf->confno, user->user_no);
2274 /* Absolutely do _not_ use careful_write here...
2275 it is important that we read data from the channel
2276 as fast as it arrives, and feed it into the conference.
2277 The buffering in the pseudo channel will take care of any
2278 timing differences, unless they are so drastic as to lose
2279 audio frames (in which case carefully writing would only
2280 have delayed the audio even further).
2282 /* As it turns out, we do want to use careful write. We just
2283 don't want to block, but we do want to at least *try*
2284 to write out all the samples.
2287 careful_write(fd, f->data, f->datalen, 0);
2289 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2292 if (confflags & CONFFLAG_PASS_DTMF)
2293 conf_queue_dtmf(conf, user, f);
2295 tmp[0] = f->subclass;
2297 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2298 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2303 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2305 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2308 exitkey[0] = f->subclass;
2311 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2313 if (confflags & CONFFLAG_PASS_DTMF)
2314 conf_queue_dtmf(conf, user, f);
2318 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2319 if (confflags & CONFFLAG_PASS_DTMF)
2320 conf_queue_dtmf(conf, user, f);
2321 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2322 ast_log(LOG_WARNING, "Error setting conference\n");
2328 /* if we are entering the menu, and the user has a channel-driver
2329 volume adjustment, clear it
2331 if (!menu_active && user->talk.desired && !user->talk.actual)
2332 set_talk_volume(user, 0);
2337 if ((confflags & CONFFLAG_ADMIN)) {
2341 /* Record this sound! */
2342 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2343 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2344 ast_stopstream(chan);
2351 case '1': /* Un/Mute */
2354 /* for admin, change both admin and use flags */
2355 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2356 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2358 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2360 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2361 if (!ast_streamfile(chan, "conf-muted", chan->language))
2362 ast_waitstream(chan, "");
2364 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2365 ast_waitstream(chan, "");
2368 case '2': /* Un/Lock the Conference */
2372 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2373 ast_waitstream(chan, "");
2376 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2377 ast_waitstream(chan, "");
2380 case '3': /* Eject last user */
2382 usr = AST_LIST_LAST(&conf->userlist);
2383 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2384 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2385 ast_waitstream(chan, "");
2387 usr->adminflags |= ADMINFLAG_KICKME;
2388 ast_stopstream(chan);
2391 tweak_listen_volume(user, VOL_DOWN);
2394 tweak_listen_volume(user, VOL_UP);
2397 tweak_talk_volume(user, VOL_DOWN);
2403 tweak_talk_volume(user, VOL_UP);
2407 /* Play an error message! */
2408 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2409 ast_waitstream(chan, "");
2417 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2418 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2419 ast_stopstream(chan);
2426 case '1': /* Un/Mute */
2429 /* user can only toggle the self-muted state */
2430 user->adminflags ^= ADMINFLAG_SELFMUTED;
2432 /* they can't override the admin mute state */
2433 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2434 if (!ast_streamfile(chan, "conf-muted", chan->language))
2435 ast_waitstream(chan, "");
2437 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2438 ast_waitstream(chan, "");
2443 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2444 user->adminflags |= ADMINFLAG_T_REQUEST;
2446 if (user->adminflags & ADMINFLAG_T_REQUEST)
2447 if (!ast_streamfile(chan, "beep", chan->language))
2448 ast_waitstream(chan, "");
2451 tweak_listen_volume(user, VOL_DOWN);
2454 tweak_listen_volume(user, VOL_UP);
2457 tweak_talk_volume(user, VOL_DOWN);
2463 tweak_talk_volume(user, VOL_UP);
2467 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2468 ast_waitstream(chan, "");
2474 ast_moh_start(chan, NULL, NULL);
2476 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2477 ast_log(LOG_WARNING, "Error setting conference\n");
2483 conf_flush(fd, chan);
2484 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2485 && confflags & CONFFLAG_PASS_DTMF) {
2486 conf_queue_dtmf(conf, user, f);
2487 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2488 switch (f->subclass) {
2489 case AST_CONTROL_HOLD:
2490 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2495 } else if (f->frametype == AST_FRAME_NULL) {
2496 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2499 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2500 chan->name, f->frametype, f->subclass);
2503 } else if (outfd > -1) {
2504 res = read(outfd, buf, CONF_SIZE);
2506 memset(&fr, 0, sizeof(fr));
2507 fr.frametype = AST_FRAME_VOICE;
2508 fr.subclass = AST_FORMAT_SLINEAR;
2512 fr.offset = AST_FRIENDLY_OFFSET;
2513 if ( !user->listen.actual &&
2514 ((confflags & CONFFLAG_MONITOR) ||
2515 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2516 (!user->talking)) ) {
2518 for (index=0;index<AST_FRAME_BITS;index++)
2519 if (chan->rawwriteformat & (1 << index))
2521 if (index >= AST_FRAME_BITS)
2522 goto bailoutandtrynormal;
2523 ast_mutex_lock(&conf->listenlock);
2524 if (!conf->transframe[index]) {
2525 if (conf->origframe) {
2526 if (!conf->transpath[index])
2527 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2528 if (conf->transpath[index]) {
2529 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2530 if (!conf->transframe[index])
2531 conf->transframe[index] = &ast_null_frame;
2535 if (conf->transframe[index]) {
2536 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2537 if (ast_write(chan, conf->transframe[index]))
2538 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2541 ast_mutex_unlock(&conf->listenlock);
2542 goto bailoutandtrynormal;
2544 ast_mutex_unlock(&conf->listenlock);
2546 bailoutandtrynormal:
2547 if (user->listen.actual)
2548 ast_frame_adjust_volume(&fr, user->listen.actual);
2549 if (ast_write(chan, &fr) < 0) {
2550 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2554 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2556 lastmarked = currentmarked;
2566 /* Take out of conference */
2570 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2571 ast_log(LOG_WARNING, "Error setting conference\n");
2575 reset_volumes(user);
2577 AST_LIST_LOCK(&confs);
2578 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2579 conf_play(chan, conf, LEAVE);
2581 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2582 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2583 if ((conf->chan) && (conf->users > 1)) {
2584 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2585 ast_waitstream(conf->chan, "");
2586 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2587 ast_waitstream(conf->chan, "");
2589 ast_filedelete(user->namerecloc, NULL);
2592 AST_LIST_UNLOCK(&confs);
2595 AST_LIST_LOCK(&confs);
2600 if (user->user_no) { /* Only cleanup users who really joined! */
2602 hr = (now.tv_sec - user->jointime) / 3600;
2603 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2604 sec = (now.tv_sec - user->jointime) % 60;
2607 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2612 "CallerIDNum: %s\r\n"
2613 "CallerIDName: %s\r\n"
2614 "Duration: %ld\r\n",
2615 chan->name, chan->uniqueid, conf->confno,
2617 S_OR(user->chan->cid.cid_num, "<unknown>"),
2618 S_OR(user->chan->cid.cid_name, "<unknown>"),
2619 (long)(now.tv_sec - user->jointime));
2623 if (rt_log_members){
2625 snprintf(members, sizeof(members), "%d", conf->users);
2626 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2628 if (confflags & CONFFLAG_MARKEDUSER)
2629 conf->markedusers--;
2630 /* Remove ourselves from the list */
2631 AST_LIST_REMOVE(&conf->userlist, user, list);
2633 /* Change any states */
2635 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2637 /* Return the number of seconds the user was in the conf */
2638 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2639 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2642 AST_LIST_UNLOCK(&confs);
2647 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2648 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2649 char *optargs[], int *too_early)
2651 struct ast_variable *var;
2652 struct ast_conference *cnf;
2656 /* Check first in the conference list */
2657 AST_LIST_LOCK(&confs);
2658 AST_LIST_TRAVERSE(&confs, cnf, list) {
2659 if (!strcmp(confno, cnf->confno))
2663 cnf->refcount += refcount;
2665 AST_LIST_UNLOCK(&confs);
2668 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2671 char currenttime[19] = "";
2672 char startTime[19] = "";
2673 char endtime[19] = "";
2674 char eatime[19] = "";
2675 char userOpts[32] = "";
2676 char adminOpts[32] = "";
2677 struct ast_tm tm, etm;
2683 now.tv_sec += fuzzystart;
2685 ast_localtime(&now, &tm, NULL);
2686 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2689 now.tv_sec += earlyalert;
2690 ast_localtime(&now, &etm, NULL);
2691 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2693 ast_copy_string(eatime, currenttime, sizeof(eatime));
2696 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2698 var = ast_load_realtime("meetme", "confno",
2699 confno, "startTime<= ", eatime, "endtime>= ",
2702 var = ast_load_realtime("meetme", "confno", confno, NULL);
2708 if (!strcasecmp(var->name, "pin")) {
2709 pin = ast_strdupa(var->value);
2710 } else if (!strcasecmp(var->name, "adminpin")) {
2711 pinadmin = ast_strdupa(var->value);
2712 } else if (!strcasecmp(var->name, "opts")) {
2713 ast_copy_string(userOpts, var->value, sizeof(userOpts));
2714 } else if (!strcasecmp(var->name, "maxusers")) {
2715 maxusers = atoi(var->value);
2716 } else if (!strcasecmp(var->name, "adminopts")) {
2717 ast_copy_string(adminOpts, var->value, sizeof(adminOpts));
2718 } else if (!strcasecmp(var->name, "endtime")) {
2719 ast_copy_string(endtime, var->value, sizeof(endtime));
2720 } else if (!strcasecmp(var->name, "starttime")) {
2721 ast_copy_string(startTime, var->value, sizeof(startTime));
2726 ast_variables_destroy(var);
2729 if (strcmp(startTime, currenttime) > 0) {
2730 /* Announce that the caller is early and exit */
2731 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2732 ast_waitstream(chan, "");
2738 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2741 cnf->maxusers = maxusers;
2742 cnf->endalert = endalert;
2743 ast_copy_string(cnf->endtime, endtime, sizeof(cnf->endtime));
2748 if (confflags && !cnf->chan &&
2749 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2750 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2751 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2752 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2755 if (confflags && !cnf->chan &&
2756 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2757 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2758 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2766 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2767 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2769 struct ast_config *cfg;
2770 struct ast_variable *var;
2771 struct ast_flags config_flags = { 0 };
2772 struct ast_conference *cnf;
2774 AST_DECLARE_APP_ARGS(args,
2775 AST_APP_ARG(confno);
2777 AST_APP_ARG(pinadmin);
2780 /* Check first in the conference list */
2781 ast_debug(1,"The requested confno is '%s'?\n", confno);
2782 AST_LIST_LOCK(&confs);
2783 AST_LIST_TRAVERSE(&confs, cnf, list) {
2784 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2785 if (!strcmp(confno, cnf->confno))
2789 cnf->refcount += refcount;
2791 AST_LIST_UNLOCK(&confs);
2795 /* No need to parse meetme.conf */
2796 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2798 if (dynamic_pin[0] == 'q') {
2799 /* Query the user to enter a PIN */
2800 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2803 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2805 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2808 /* Check the config */
2809 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2811 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2814 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2815 if (strcasecmp(var->name, "conf"))
2818 if (!(parse = ast_strdupa(var->value)))
2821 AST_STANDARD_APP_ARGS(args, parse);
2822 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2823 if (!strcasecmp(args.confno, confno)) {
2824 /* Bingo it's a valid conference */
2825 cnf = build_conf(args.confno,
2827 S_OR(args.pinadmin, ""),
2828 make, dynamic, refcount, chan);
2833 ast_debug(1, "%s isn't a valid conference\n", confno);
2835 ast_config_destroy(cfg);
2837 } else if (dynamic_pin) {
2838 /* Correct for the user selecting 'D' instead of 'd' to have
2839 someone join into a conference that has already been created
2841 if (dynamic_pin[0] == 'q')
2842 dynamic_pin[0] = '\0';
2846 if (confflags && !cnf->chan &&
2847 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2848 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2849 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2850 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2853 if (confflags && !cnf->chan &&
2854 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2855 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2856 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2863 /*! \brief The MeetmeCount application */
2864 static int count_exec(struct ast_channel *chan, void *data)
2867 struct ast_conference *conf;
2871 AST_DECLARE_APP_ARGS(args,
2872 AST_APP_ARG(confno);
2873 AST_APP_ARG(varname);
2876 if (ast_strlen_zero(data)) {
2877 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2881 if (!(localdata = ast_strdupa(data)))
2884 AST_STANDARD_APP_ARGS(args, localdata);
2886 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2889 count = conf->users;
2895 if (!ast_strlen_zero(args.varname)){
2896 /* have var so load it and exit */
2897 snprintf(val, sizeof(val), "%d",count);
2898 pbx_builtin_setvar_helper(chan, args.varname, val);
2900 if (chan->_state != AST_STATE_UP)
2902 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2908 /*! \brief The meetme() application */
2909 static int conf_exec(struct ast_channel *chan, void *data)
2912 char confno[MAX_CONFNUM] = "";
2915 struct ast_conference *cnf = NULL;
2916 struct ast_flags confflags = {0}, config_flags = { 0 };
2918 int empty = 0, empty_no_pin = 0;
2919 int always_prompt = 0;
2920 char *notdata, *info, the_pin[MAX_PIN] = "";
2921 AST_DECLARE_APP_ARGS(args,
2922 AST_APP_ARG(confno);
2923 AST_APP_ARG(options);
2926 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2928 if (ast_strlen_zero(data)) {
2935 if (chan->_state != AST_STATE_UP)
2938 info = ast_strdupa(notdata);
2940 AST_STANDARD_APP_ARGS(args, info);
2943 ast_copy_string(confno, args.confno, sizeof(confno));
2944 if (ast_strlen_zero(confno)) {
2950 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2953 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2954 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2955 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2956 strcpy(the_pin, "q");
2958 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2959 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2960 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2968 struct ast_config *cfg;
2969 struct ast_variable *var;
2972 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2973 if ((empty_no_pin) || (!dynamic)) {
2974 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2976 var = ast_variable_browse(cfg, "rooms");
2978 if (!strcasecmp(var->name, "conf")) {
2979 char *stringp = ast_strdupa(var->value);
2981 char *confno_tmp = strsep(&stringp, "|,");
2984 /* For static: run through the list and see if this conference is empty */
2985 AST_LIST_LOCK(&confs);
2986 AST_LIST_TRAVERSE(&confs, cnf, list) {
2987 if (!strcmp(confno_tmp, cnf->confno)) {
2988 /* The conference exists, therefore it's not empty */
2993 AST_LIST_UNLOCK(&confs);
2995 /* At this point, we have a confno_tmp (static conference) that is empty */
2996 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2997 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2998 * Case 2: empty_no_pin and pin is blank (but not NULL)
2999 * Case 3: not empty_no_pin
3001 ast_copy_string(confno, confno_tmp, sizeof(confno));
3003 /* XXX the map is not complete (but we do have a confno) */
3011 ast_config_destroy(cfg);
3015 /* Select first conference number not in use */
3016 if (ast_strlen_zero(confno) && dynamic) {
3017 AST_LIST_LOCK(&confs);
3018 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
3020 snprintf(confno, sizeof(confno), "%d", i);
3025 AST_LIST_UNLOCK(&confs);
3029 if (ast_strlen_zero(confno)) {
3030 res = ast_streamfile(chan, "conf-noempty", chan->language);
3032 ast_waitstream(chan, "");
3034 if (sscanf(confno, "%d", &confno_int) == 1) {
3035 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
3036 res = ast_streamfile(chan, "conf-enteringno", chan->language);