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$")
42 #include "asterisk/zapata.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/module.h"
49 #include "asterisk/config.h"
50 #include "asterisk/app.h"
51 #include "asterisk/dsp.h"
52 #include "asterisk/musiconhold.h"
53 #include "asterisk/manager.h"
54 #include "asterisk/options.h"
55 #include "asterisk/cli.h"
56 #include "asterisk/say.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/translate.h"
59 #include "asterisk/ulaw.h"
60 #include "asterisk/astobj.h"
61 #include "asterisk/devicestate.h"
62 #include "asterisk/dial.h"
63 #include "asterisk/causes.h"
68 #define CONFIG_FILE_NAME "meetme.conf"
69 #define SLA_CONFIG_FILE "sla.conf"
71 /*! each buffer is 20ms, so this is 640ms total */
72 #define DEFAULT_AUDIO_BUFFERS 32
74 /*! String format for scheduled conferences */
75 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
78 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
79 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
80 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
81 /*! User has requested to speak */
82 ADMINFLAG_T_REQUEST = (1 << 4),
85 #define MEETME_DELAYDETECTTALK 300
86 #define MEETME_DELAYDETECTENDTALK 1000
88 #define AST_FRAME_BITS 32
100 enum recording_state {
102 MEETME_RECORD_STARTED,
103 MEETME_RECORD_ACTIVE,
104 MEETME_RECORD_TERMINATE
107 #define CONF_SIZE 320
110 /*! user has admin access on the conference */
111 CONFFLAG_ADMIN = (1 << 0),
112 /*! If set the user can only receive audio from the conference */
113 CONFFLAG_MONITOR = (1 << 1),
114 /*! If set asterisk will exit conference when key defined in p() option is pressed */
115 CONFFLAG_KEYEXIT = (1 << 2),
116 /*! If set asterisk will provide a menu to the user when '*' is pressed */
117 CONFFLAG_STARMENU = (1 << 3),
118 /*! If set the use can only send audio to the conference */
119 CONFFLAG_TALKER = (1 << 4),
120 /*! If set there will be no enter or leave sounds */
121 CONFFLAG_QUIET = (1 << 5),
122 /*! If set, when user joins the conference, they will be told the number
123 * of users that are already in */
124 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
125 /*! Set to run AGI Script in Background */
126 CONFFLAG_AGI = (1 << 7),
127 /*! Set to have music on hold when user is alone in conference */
128 CONFFLAG_MOH = (1 << 8),
129 /*! If set the MeetMe will return if all marked with this flag left */
130 CONFFLAG_MARKEDEXIT = (1 << 9),
131 /*! If set, the MeetMe will wait until a marked user enters */
132 CONFFLAG_WAITMARKED = (1 << 10),
133 /*! If set, the MeetMe will exit to the specified context */
134 CONFFLAG_EXIT_CONTEXT = (1 << 11),
135 /*! If set, the user will be marked */
136 CONFFLAG_MARKEDUSER = (1 << 12),
137 /*! If set, user will be ask record name on entry of conference */
138 CONFFLAG_INTROUSER = (1 << 13),
139 /*! If set, the MeetMe will be recorded */
140 CONFFLAG_RECORDCONF = (1<< 14),
141 /*! If set, the user will be monitored if the user is talking or not */
142 CONFFLAG_MONITORTALKER = (1 << 15),
143 CONFFLAG_DYNAMIC = (1 << 16),
144 CONFFLAG_DYNAMICPIN = (1 << 17),
145 CONFFLAG_EMPTY = (1 << 18),
146 CONFFLAG_EMPTYNOPIN = (1 << 19),
147 CONFFLAG_ALWAYSPROMPT = (1 << 20),
148 /*! If set, won't speak the extra prompt when the first person
149 * enters the conference */
150 CONFFLAG_NOONLYPERSON = (1 << 22),
151 /*! If set, user will be asked to record name on entry of conference
153 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
154 /*! If set, the user will be initially self-muted */
155 CONFFLAG_STARTMUTED = (1 << 24),
156 /*! Pass DTMF through the conference */
157 CONFFLAG_PASS_DTMF = (1 << 25),
158 CONFFLAG_SLA_STATION = (1 << 26),
159 CONFFLAG_SLA_TRUNK = (1 << 27),
160 /*! If set, the user should continue in the dialplan if kicked out */
161 CONFFLAG_KICK_CONTINUE = (1 << 28),
162 CONFFLAG_DURATION_STOP = (1 << 29),
163 CONFFLAG_DURATION_LIMIT = (1 << 30),
167 OPT_ARG_WAITMARKED = 0,
168 OPT_ARG_EXITKEYS = 1,
169 OPT_ARG_DURATION_STOP = 2,
170 OPT_ARG_DURATION_LIMIT = 3,
171 OPT_ARG_ARRAY_SIZE = 4,
174 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
175 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
176 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
177 AST_APP_OPTION('b', CONFFLAG_AGI ),
178 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
179 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
180 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
181 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
182 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
183 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
184 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
185 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
186 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
187 AST_APP_OPTION('M', CONFFLAG_MOH ),
188 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
189 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
190 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
191 AST_APP_OPTION('q', CONFFLAG_QUIET ),
192 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
193 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
194 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
195 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
196 AST_APP_OPTION('t', CONFFLAG_TALKER ),
197 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
198 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
199 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
200 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
201 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
202 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
205 static const char *app = "MeetMe";
206 static const char *app2 = "MeetMeCount";
207 static const char *app3 = "MeetMeAdmin";
208 static const char *app4 = "MeetMeChannelAdmin";
209 static const char *slastation_app = "SLAStation";
210 static const char *slatrunk_app = "SLATrunk";
212 static const char *synopsis = "MeetMe conference bridge";
213 static const char *synopsis2 = "MeetMe participant count";
214 static const char *synopsis3 = "MeetMe conference Administration";
215 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
216 static const char *slastation_synopsis = "Shared Line Appearance Station";
217 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
219 /* Lookup RealTime conferences based on confno and current time */
220 static int rt_schedule;
221 static int fuzzystart;
222 static int earlyalert;
225 /* Log participant count to the RealTime backend */
226 static int rt_log_members;
228 static const char *descrip =
229 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
230 "conference. If the conference number is omitted, the user will be prompted\n"
231 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
232 "is specified, by pressing '#'.\n"
233 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
234 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
235 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
236 "The option string may contain zero or more of the following characters:\n"
237 " 'a' -- set admin mode\n"
238 " 'A' -- set marked mode\n"
239 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
240 " Default: conf-background.agi (Note: This does not work with\n"
241 " non-Zap channels in the same conference)\n"
242 " 'c' -- announce user(s) count on joining a conference\n"
243 " 'C' -- continue in dialplan when kicked out of conference\n"
244 " 'd' -- dynamically add conference\n"
245 " 'D' -- dynamically add conference, prompting for a PIN\n"
246 " 'e' -- select an empty conference\n"
247 " 'E' -- select an empty pinless conference\n"
248 " 'F' -- Pass DTMF through the conference.\n"
249 " 'i' -- announce user join/leave with review\n"
250 " 'I' -- announce user join/leave without review\n"
251 " 'l' -- set listen only mode (Listen only, no talking)\n"
252 " 'm' -- set initially muted\n"
253 " 'M' -- enable music on hold when the conference has a single caller\n"
254 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
255 " being muted, meaning (a) No encode is done on transmission and\n"
256 " (b) Received audio that is not registered as talking is omitted\n"
257 " causing no buildup in background noise\n"
259 " -- allow user to exit the conference by pressing '#' (default)\n"
260 " or any of the defined keys. If keys contain '*' this will override\n"
261 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
262 " 'P' -- always prompt for the pin even if it is specified\n"
263 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
264 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
265 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
266 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
268 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
269 " 't' -- set talk only mode. (Talk only, no listening)\n"
270 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
272 " -- wait until the marked user enters the conference\n"
273 " 'x' -- close the conference when last marked user exits\n"
274 " 'X' -- allow user to exit the conference by entering a valid single\n"
275 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
276 " if that variable is not defined.\n"
277 " '1' -- do not play message when first person enters\n"
278 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
279 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
280 " left. Repeat the warning every 'z' ms. The following special\n"
281 " variables can be used with this option:\n"
282 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
283 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
284 " The default is to say the time remaining.\n"
287 static const char *descrip2 =
288 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
289 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
290 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
291 "the channel, unless priority n+1 exists, in which case priority progress will\n"
293 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
295 static const char *descrip3 =
296 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
297 " 'e' -- Eject last user that joined\n"
298 " 'k' -- Kick one user out of conference\n"
299 " 'K' -- Kick all users out of conference\n"
300 " 'l' -- Unlock conference\n"
301 " 'L' -- Lock conference\n"
302 " 'm' -- Unmute one user\n"
303 " 'M' -- Mute one user\n"
304 " 'n' -- Unmute all users in the conference\n"
305 " 'N' -- Mute all non-admin users in the conference\n"
306 " 'r' -- Reset one user's volume settings\n"
307 " 'R' -- Reset all users volume settings\n"
308 " 's' -- Lower entire conference speaking volume\n"
309 " 'S' -- Raise entire conference speaking volume\n"
310 " 't' -- Lower one user's talk volume\n"
311 " 'T' -- Raise one user's talk volume\n"
312 " 'u' -- Lower one user's listen volume\n"
313 " 'U' -- Raise one user's listen volume\n"
314 " 'v' -- Lower entire conference listening volume\n"
315 " 'V' -- Raise entire conference listening volume\n"
318 static const char *descrip4 =
319 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
320 "channel in any coference.\n"
321 " 'k' -- Kick the specified user out of the conference he is in\n"
322 " 'm' -- Unmute the specified user\n"
323 " 'M' -- Mute the specified user\n"
326 static const char *slastation_desc =
327 " SLAStation(station):\n"
328 "This application should be executed by an SLA station. The argument depends\n"
329 "on how the call was initiated. If the phone was just taken off hook, then\n"
330 "the argument \"station\" should be just the station name. If the call was\n"
331 "initiated by pressing a line key, then the station name should be preceded\n"
332 "by an underscore and the trunk name associated with that line button.\n"
333 "For example: \"station1_line1\"."
334 " On exit, this application will set the variable SLASTATION_STATUS to\n"
335 "one of the following values:\n"
336 " FAILURE | CONGESTION | SUCCESS\n"
339 static const char *slatrunk_desc =
340 " SLATrunk(trunk):\n"
341 "This application should be executed by an SLA trunk on an inbound call.\n"
342 "The channel calling this application should correspond to the SLA trunk\n"
343 "with the name \"trunk\" that is being passed as an argument.\n"
344 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
345 "one of the following values:\n"
346 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
349 #define MAX_CONFNUM 80
352 /*! \brief The MeetMe Conference object */
353 struct ast_conference {
354 ast_mutex_t playlock; /*!< Conference specific lock (players) */
355 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
356 char confno[MAX_CONFNUM]; /*!< Conference */
357 struct ast_channel *chan; /*!< Announcements channel */
358 struct ast_channel *lchan; /*!< Listen/Record channel */
359 int fd; /*!< Announcements fd */
360 int zapconf; /*!< Zaptel Conf # */
361 int users; /*!< Number of active users */
362 int markedusers; /*!< Number of marked users */
363 int maxusers; /*!< Participant limit if scheduled */
364 int endalert; /*!< When to play conf ending message */
365 time_t start; /*!< Start time (s) */
366 int refcount; /*!< reference count of usage */
367 enum recording_state recording:2; /*!< recording status */
368 unsigned int isdynamic:1; /*!< Created on the fly? */
369 unsigned int locked:1; /*!< Is the conference locked? */
370 pthread_t recordthread; /*!< thread for recording */
371 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
372 pthread_attr_t attr; /*!< thread attribute */
373 const char *recordingfilename; /*!< Filename to record the Conference into */
374 const char *recordingformat; /*!< Format to record the Conference in */
375 char pin[MAX_PIN]; /*!< If protected by a PIN */
376 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
378 char endtime[19]; /*!< When to end the conf if scheduled */
379 struct ast_frame *transframe[32];
380 struct ast_frame *origframe;
381 struct ast_trans_pvt *transpath[32];
382 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
383 AST_LIST_ENTRY(ast_conference) list;
386 static AST_LIST_HEAD_STATIC(confs, ast_conference);
388 static unsigned int conf_map[1024] = {0, };
391 int desired; /*!< Desired volume adjustment */
392 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
395 /*! \brief The MeetMe User object */
396 struct ast_conf_user {
397 int user_no; /*!< User Number */
398 int userflags; /*!< Flags as set in the conference */
399 int adminflags; /*!< Flags set by the Admin */
400 struct ast_channel *chan; /*!< Connected channel */
401 int talking; /*!< Is user talking */
402 int zapchannel; /*!< Is a Zaptel channel */
403 char usrvalue[50]; /*!< Custom User Value */
404 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
405 time_t jointime; /*!< Time the user joined the conference */
406 time_t kicktime; /*!< Time the user will be kicked from the conference */
407 struct timeval start_time; /*!< Time the user entered into the conference */
408 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
409 long play_warning; /*!< Play a warning when 'y' ms are left */
410 long warning_freq; /*!< Repeat the warning every 'z' ms */
411 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
412 const char *end_sound; /*!< File to play when time is up. */
414 struct volume listen;
415 AST_LIST_ENTRY(ast_conf_user) list;
418 enum sla_which_trunk_refs {
423 enum sla_trunk_state {
424 SLA_TRUNK_STATE_IDLE,
425 SLA_TRUNK_STATE_RINGING,
427 SLA_TRUNK_STATE_ONHOLD,
428 SLA_TRUNK_STATE_ONHOLD_BYME,
431 enum sla_hold_access {
432 /*! This means that any station can put it on hold, and any station
433 * can retrieve the call from hold. */
435 /*! This means that only the station that put the call on hold may
436 * retrieve it from hold. */
440 struct sla_trunk_ref;
443 AST_RWLIST_ENTRY(sla_station) entry;
444 AST_DECLARE_STRING_FIELDS(
445 AST_STRING_FIELD(name);
446 AST_STRING_FIELD(device);
447 AST_STRING_FIELD(autocontext);
449 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
450 struct ast_dial *dial;
451 /*! Ring timeout for this station, for any trunk. If a ring timeout
452 * is set for a specific trunk on this station, that will take
453 * priority over this value. */
454 unsigned int ring_timeout;
455 /*! Ring delay for this station, for any trunk. If a ring delay
456 * is set for a specific trunk on this station, that will take
457 * priority over this value. */
458 unsigned int ring_delay;
459 /*! This option uses the values in the sla_hold_access enum and sets the
460 * access control type for hold on this station. */
461 unsigned int hold_access:1;
462 /*! Use count for inside sla_station_exec */
463 unsigned int ref_count;
466 struct sla_station_ref {
467 AST_LIST_ENTRY(sla_station_ref) entry;
468 struct sla_station *station;
472 AST_RWLIST_ENTRY(sla_trunk) entry;
473 AST_DECLARE_STRING_FIELDS(
474 AST_STRING_FIELD(name);
475 AST_STRING_FIELD(device);
476 AST_STRING_FIELD(autocontext);
478 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
479 /*! Number of stations that use this trunk */
480 unsigned int num_stations;
481 /*! Number of stations currently on a call with this trunk */
482 unsigned int active_stations;
483 /*! Number of stations that have this trunk on hold. */
484 unsigned int hold_stations;
485 struct ast_channel *chan;
486 unsigned int ring_timeout;
487 /*! If set to 1, no station will be able to join an active call with
489 unsigned int barge_disabled:1;
490 /*! This option uses the values in the sla_hold_access enum and sets the
491 * access control type for hold on this trunk. */
492 unsigned int hold_access:1;
493 /*! Whether this trunk is currently on hold, meaning that once a station
494 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
495 unsigned int on_hold:1;
496 /*! Use count for inside sla_trunk_exec */
497 unsigned int ref_count;
500 struct sla_trunk_ref {
501 AST_LIST_ENTRY(sla_trunk_ref) entry;
502 struct sla_trunk *trunk;
503 enum sla_trunk_state state;
504 struct ast_channel *chan;
505 /*! Ring timeout to use when this trunk is ringing on this specific
506 * station. This takes higher priority than a ring timeout set at
507 * the station level. */
508 unsigned int ring_timeout;
509 /*! Ring delay to use when this trunk is ringing on this specific
510 * station. This takes higher priority than a ring delay set at
511 * the station level. */
512 unsigned int ring_delay;
515 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
516 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
518 static const char sla_registrar[] = "SLA";
520 /*! \brief Event types that can be queued up for the SLA thread */
521 enum sla_event_type {
522 /*! A station has put the call on hold */
524 /*! The state of a dial has changed */
525 SLA_EVENT_DIAL_STATE,
526 /*! The state of a ringing trunk has changed */
527 SLA_EVENT_RINGING_TRUNK,
528 /*! A reload of configuration has been requested */
530 /*! Poke the SLA thread so it can check if it can perform a reload */
531 SLA_EVENT_CHECK_RELOAD,
535 enum sla_event_type type;
536 struct sla_station *station;
537 struct sla_trunk_ref *trunk_ref;
538 AST_LIST_ENTRY(sla_event) entry;
541 /*! \brief A station that failed to be dialed
542 * \note Only used by the SLA thread. */
543 struct sla_failed_station {
544 struct sla_station *station;
545 struct timeval last_try;
546 AST_LIST_ENTRY(sla_failed_station) entry;
549 /*! \brief A trunk that is ringing */
550 struct sla_ringing_trunk {
551 struct sla_trunk *trunk;
552 /*! The time that this trunk started ringing */
553 struct timeval ring_begin;
554 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
555 AST_LIST_ENTRY(sla_ringing_trunk) entry;
558 enum sla_station_hangup {
559 SLA_STATION_HANGUP_NORMAL,
560 SLA_STATION_HANGUP_TIMEOUT,
563 /*! \brief A station that is ringing */
564 struct sla_ringing_station {
565 struct sla_station *station;
566 /*! The time that this station started ringing */
567 struct timeval ring_begin;
568 AST_LIST_ENTRY(sla_ringing_station) entry;
572 * \brief A structure for data used by the sla thread
575 /*! The SLA thread ID */
579 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
580 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
581 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
582 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
584 /*! Attempt to handle CallerID, even though it is known not to work
585 * properly in some situations. */
586 unsigned int attempt_callerid:1;
587 /*! A reload has been requested */
588 unsigned int reload:1;
590 .thread = AST_PTHREADT_NULL,
593 /*! The number of audio buffers to be allocated on pseudo channels
594 * when in a conference */
595 static int audio_buffers;
597 /*! Map 'volume' levels from -5 through +5 into
598 * decibel (dB) settings for channel drivers
599 * Note: these are not a straight linear-to-dB
600 * conversion... the numbers have been modified
601 * to give the user a better level of adjustability
603 static char const gain_map[] = {
618 static int admin_exec(struct ast_channel *chan, void *data);
619 static void *recordthread(void *args);
621 static char *istalking(int x)
626 return "(unmonitored)";
628 return "(not talking)";
631 static int careful_write(int fd, unsigned char *data, int len, int block)
638 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
639 res = ioctl(fd, ZT_IOMUX, &x);
643 res = write(fd, data, len);
645 if (errno != EAGAIN) {
646 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
658 static int set_talk_volume(struct ast_conf_user *user, int volume)
662 /* attempt to make the adjustment in the channel driver;
663 if successful, don't adjust in the frame reading routine
665 gain_adjust = gain_map[volume + 5];
667 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
670 static int set_listen_volume(struct ast_conf_user *user, int volume)
674 /* attempt to make the adjustment in the channel driver;
675 if successful, don't adjust in the frame reading routine
677 gain_adjust = gain_map[volume + 5];
679 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
682 static void tweak_volume(struct volume *vol, enum volume_action action)
686 switch (vol->desired) {
701 switch (vol->desired) {
717 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
719 tweak_volume(&user->talk, action);
720 /* attempt to make the adjustment in the channel driver;
721 if successful, don't adjust in the frame reading routine
723 if (!set_talk_volume(user, user->talk.desired))
724 user->talk.actual = 0;
726 user->talk.actual = user->talk.desired;
729 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
731 tweak_volume(&user->listen, action);
732 /* attempt to make the adjustment in the channel driver;
733 if successful, don't adjust in the frame reading routine
735 if (!set_listen_volume(user, user->listen.desired))
736 user->listen.actual = 0;
738 user->listen.actual = user->listen.desired;
741 static void reset_volumes(struct ast_conf_user *user)
743 signed char zero_volume = 0;
745 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
746 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
749 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
755 if (!ast_check_hangup(chan))
756 res = ast_autoservice_start(chan);
758 AST_LIST_LOCK(&confs);
774 careful_write(conf->fd, data, len, 1);
777 AST_LIST_UNLOCK(&confs);
780 ast_autoservice_stop(chan);
784 * \brief Find or create a conference
786 * \param confno The conference name/number
787 * \param pin The regular user pin
788 * \param pinadmin The admin pin
789 * \param make Make the conf if it doesn't exist
790 * \param dynamic Mark the newly created conference as dynamic
791 * \param refcount How many references to mark on the conference
792 * \param chan The asterisk channel
794 * \return A pointer to the conference struct, or NULL if it wasn't found and
795 * make or dynamic were not set.
797 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
799 struct ast_conference *cnf;
800 struct zt_confinfo ztc = { 0, };
803 AST_LIST_LOCK(&confs);
805 AST_LIST_TRAVERSE(&confs, cnf, list) {
806 if (!strcmp(confno, cnf->confno))
810 if (cnf || (!make && !dynamic))
814 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
817 ast_mutex_init(&cnf->playlock);
818 ast_mutex_init(&cnf->listenlock);
819 cnf->recordthread = AST_PTHREADT_NULL;
820 ast_mutex_init(&cnf->recordthreadlock);
821 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
822 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
823 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
824 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
826 /* Setup a new zap conference */
828 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
829 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
830 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
831 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
839 cnf->zapconf = ztc.confno;
841 /* Setup a new channel for playback of audio files */
842 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
844 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
845 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
847 ztc.confno = cnf->zapconf;
848 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
849 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
850 ast_log(LOG_WARNING, "Error setting conference\n");
852 ast_hangup(cnf->chan);
862 /* Fill the conference struct */
863 cnf->start = time(NULL);
864 cnf->maxusers = 0x7fffffff;
865 cnf->isdynamic = dynamic ? 1 : 0;
866 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
867 AST_LIST_INSERT_HEAD(&confs, cnf, list);
869 /* Reserve conference number in map */
870 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
871 conf_map[confno_int] = 1;
875 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
877 AST_LIST_UNLOCK(&confs);
883 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
885 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
887 int len = strlen(word);
889 struct ast_conference *cnf = NULL;
890 struct ast_conf_user *usr = NULL;
893 char *myline, *ret = NULL;
895 if (pos == 1) { /* Command */
896 return ast_cli_complete(word, cmds, state);
897 } else if (pos == 2) { /* Conference Number */
898 AST_LIST_LOCK(&confs);
899 AST_LIST_TRAVERSE(&confs, cnf, list) {
900 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
905 ret = ast_strdup(ret); /* dup before releasing the lock */
906 AST_LIST_UNLOCK(&confs);
908 } else if (pos == 3) {
909 /* User Number || Conf Command option*/
910 if (strstr(line, "mute") || strstr(line, "kick")) {
911 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
912 return ast_strdup("all");
914 AST_LIST_LOCK(&confs);
916 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
917 myline = ast_strdupa(line);
918 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
919 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
923 AST_LIST_TRAVERSE(&confs, cnf, list) {
924 if (!strcmp(confno, cnf->confno))
929 /* Search for the user */
930 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
931 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
932 if (!strncasecmp(word, usrno, len) && ++which > state)
936 AST_LIST_UNLOCK(&confs);
937 return usr ? ast_strdup(usrno) : NULL;
938 } else if ( strstr(line, "list") && ( 0 == state ) )
939 return ast_strdup("concise");
945 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
947 /* Process the command */
948 struct ast_conference *cnf;
949 struct ast_conf_user *user;
951 int i = 0, total = 0;
953 char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
954 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
955 char cmdline[1024] = "";
959 e->command = "meetme";
961 "Usage: meetme concise|(un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
962 " Executes a command for the conference or on a conferee\n";
965 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
969 ast_cli(a->fd, "Invalid Arguments.\n");
970 /* Check for length so no buffer will overflow... */
971 for (i = 0; i < a->argc; i++) {
972 if (strlen(a->argv[i]) > 100)
973 ast_cli(a->fd, "Invalid Arguments.\n");
975 if (a->argc == 1 || ( a->argc == 2 && !strcasecmp(a->argv[1], "concise") )) {
976 /* 'MeetMe': List all the conferences */
977 int concise = ( a->argc == 2 && !strcasecmp(a->argv[1], "concise") );
979 AST_LIST_LOCK(&confs);
980 if (AST_LIST_EMPTY(&confs)) {
982 ast_cli(a->fd, "No active MeetMe conferences.\n");
983 AST_LIST_UNLOCK(&confs);
987 ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
988 AST_LIST_TRAVERSE(&confs, cnf, list) {
989 if (cnf->markedusers == 0)
990 strcpy(cmdline, "N/A ");
992 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
993 hr = (now - cnf->start) / 3600;
994 min = ((now - cnf->start) % 3600) / 60;
995 sec = (now - cnf->start) % 60;
997 ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
999 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1008 total += cnf->users;
1010 AST_LIST_UNLOCK(&confs);
1012 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1016 return CLI_SHOWUSAGE;
1017 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
1018 if (strstr(a->argv[1], "lock")) {
1019 if (strcmp(a->argv[1], "lock") == 0) {
1021 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
1024 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
1026 } else if (strstr(a->argv[1], "mute")) {
1028 return CLI_SHOWUSAGE;
1029 if (strcmp(a->argv[1], "mute") == 0) {
1031 if (strcmp(a->argv[3], "all") == 0) {
1032 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
1034 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
1035 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1039 if (strcmp(a->argv[3], "all") == 0) {
1040 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
1042 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
1043 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1046 } else if (strcmp(a->argv[1], "kick") == 0) {
1048 return CLI_SHOWUSAGE;
1049 if (strcmp(a->argv[3], "all") == 0) {
1051 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1053 /* Kick a single user */
1054 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1055 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1057 } else if(strcmp(a->argv[1], "list") == 0) {
1058 int concise = ( 4 == a->argc && ( !strcasecmp(a->argv[3], "concise") ) );
1059 /* List all the users in a conference */
1060 if (AST_LIST_EMPTY(&confs)) {
1062 ast_cli(a->fd, "No active conferences.\n");
1065 /* Find the right conference */
1066 AST_LIST_LOCK(&confs);
1067 AST_LIST_TRAVERSE(&confs, cnf, list) {
1068 if (strcmp(cnf->confno, a->argv[2]) == 0)
1073 ast_cli(a->fd, "No such conference: %s.\n",a->argv[2]);
1074 AST_LIST_UNLOCK(&confs);
1077 /* Show all the users */
1079 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1080 hr = (now - user->jointime) / 3600;
1081 min = ((now - user->jointime) % 3600) / 60;
1082 sec = (now - user->jointime) % 60;
1084 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1086 S_OR(user->chan->cid.cid_num, "<unknown>"),
1087 S_OR(user->chan->cid.cid_name, "<no name>"),
1089 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1090 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1091 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1092 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1093 istalking(user->talking), hr, min, sec);
1095 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1097 S_OR(user->chan->cid.cid_num, ""),
1098 S_OR(user->chan->cid.cid_name, ""),
1100 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1101 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1102 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1103 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1104 user->talking, hr, min, sec);
1108 ast_cli(a->fd,"%d users in that conference.\n",cnf->users);
1109 AST_LIST_UNLOCK(&confs);
1112 return CLI_SHOWUSAGE;
1114 ast_debug(1, "Cmdline: %s\n", cmdline);
1116 admin_exec(NULL, cmdline);
1121 static const char *sla_hold_str(unsigned int hold_access)
1123 const char *hold = "Unknown";
1125 switch (hold_access) {
1129 case SLA_HOLD_PRIVATE:
1138 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1140 const struct sla_trunk *trunk;
1144 e->command = "sla show trunks";
1146 "Usage: sla show trunks\n"
1147 " This will list all trunks defined in sla.conf\n";
1154 "=============================================================\n"
1155 "=== Configured SLA Trunks ===================================\n"
1156 "=============================================================\n"
1158 AST_RWLIST_RDLOCK(&sla_trunks);
1159 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1160 struct sla_station_ref *station_ref;
1161 char ring_timeout[16] = "(none)";
1162 if (trunk->ring_timeout)
1163 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1164 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1165 "=== Trunk Name: %s\n"
1166 "=== ==> Device: %s\n"
1167 "=== ==> AutoContext: %s\n"
1168 "=== ==> RingTimeout: %s\n"
1169 "=== ==> BargeAllowed: %s\n"
1170 "=== ==> HoldAccess: %s\n"
1171 "=== ==> Stations ...\n",
1172 trunk->name, trunk->device,
1173 S_OR(trunk->autocontext, "(none)"),
1175 trunk->barge_disabled ? "No" : "Yes",
1176 sla_hold_str(trunk->hold_access));
1177 AST_RWLIST_RDLOCK(&sla_stations);
1178 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1179 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1180 AST_RWLIST_UNLOCK(&sla_stations);
1181 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1183 AST_RWLIST_UNLOCK(&sla_trunks);
1184 ast_cli(a->fd, "=============================================================\n\n");
1189 static const char *trunkstate2str(enum sla_trunk_state state)
1191 #define S(e) case e: return # e;
1193 S(SLA_TRUNK_STATE_IDLE)
1194 S(SLA_TRUNK_STATE_RINGING)
1195 S(SLA_TRUNK_STATE_UP)
1196 S(SLA_TRUNK_STATE_ONHOLD)
1197 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1199 return "Uknown State";
1203 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1205 const struct sla_station *station;
1209 e->command = "sla show stations";
1211 "Usage: sla show stations\n"
1212 " This will list all stations defined in sla.conf\n";
1219 "=============================================================\n"
1220 "=== Configured SLA Stations =================================\n"
1221 "=============================================================\n"
1223 AST_RWLIST_RDLOCK(&sla_stations);
1224 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1225 struct sla_trunk_ref *trunk_ref;
1226 char ring_timeout[16] = "(none)";
1227 char ring_delay[16] = "(none)";
1228 if (station->ring_timeout) {
1229 snprintf(ring_timeout, sizeof(ring_timeout),
1230 "%u", station->ring_timeout);
1232 if (station->ring_delay) {
1233 snprintf(ring_delay, sizeof(ring_delay),
1234 "%u", station->ring_delay);
1236 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1237 "=== Station Name: %s\n"
1238 "=== ==> Device: %s\n"
1239 "=== ==> AutoContext: %s\n"
1240 "=== ==> RingTimeout: %s\n"
1241 "=== ==> RingDelay: %s\n"
1242 "=== ==> HoldAccess: %s\n"
1243 "=== ==> Trunks ...\n",
1244 station->name, station->device,
1245 S_OR(station->autocontext, "(none)"),
1246 ring_timeout, ring_delay,
1247 sla_hold_str(station->hold_access));
1248 AST_RWLIST_RDLOCK(&sla_trunks);
1249 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1250 if (trunk_ref->ring_timeout) {
1251 snprintf(ring_timeout, sizeof(ring_timeout),
1252 "%u", trunk_ref->ring_timeout);
1254 strcpy(ring_timeout, "(none)");
1255 if (trunk_ref->ring_delay) {
1256 snprintf(ring_delay, sizeof(ring_delay),
1257 "%u", trunk_ref->ring_delay);
1259 strcpy(ring_delay, "(none)");
1260 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1261 "=== ==> State: %s\n"
1262 "=== ==> RingTimeout: %s\n"
1263 "=== ==> RingDelay: %s\n",
1264 trunk_ref->trunk->name,
1265 trunkstate2str(trunk_ref->state),
1266 ring_timeout, ring_delay);
1268 AST_RWLIST_UNLOCK(&sla_trunks);
1269 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1272 AST_RWLIST_UNLOCK(&sla_stations);
1273 ast_cli(a->fd, "============================================================\n"
1279 static struct ast_cli_entry cli_meetme[] = {
1280 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1281 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1282 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1285 static void conf_flush(int fd, struct ast_channel *chan)
1289 /* read any frames that may be waiting on the channel
1293 struct ast_frame *f;
1295 /* when no frames are available, this will wait
1296 for 1 millisecond maximum
1298 while (ast_waitfor(chan, 1)) {
1302 else /* channel was hung up or something else happened */
1307 /* flush any data sitting in the pseudo channel */
1309 if (ioctl(fd, ZT_FLUSH, &x))
1310 ast_log(LOG_WARNING, "Error flushing channel\n");
1314 /* Remove the conference from the list and free it.
1315 We assume that this was called while holding conflock. */
1316 static int conf_free(struct ast_conference *conf)
1320 AST_LIST_REMOVE(&confs, conf, list);
1321 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1323 if (conf->recording == MEETME_RECORD_ACTIVE) {
1324 conf->recording = MEETME_RECORD_TERMINATE;
1325 AST_LIST_UNLOCK(&confs);
1328 AST_LIST_LOCK(&confs);
1329 if (conf->recording == MEETME_RECORD_OFF)
1331 AST_LIST_UNLOCK(&confs);
1335 for (x=0;x<AST_FRAME_BITS;x++) {
1336 if (conf->transframe[x])
1337 ast_frfree(conf->transframe[x]);
1338 if (conf->transpath[x])
1339 ast_translator_free_path(conf->transpath[x]);
1341 if (conf->origframe)
1342 ast_frfree(conf->origframe);
1344 ast_hangup(conf->lchan);
1346 ast_hangup(conf->chan);
1350 ast_mutex_destroy(&conf->playlock);
1351 ast_mutex_destroy(&conf->listenlock);
1352 ast_mutex_destroy(&conf->recordthreadlock);
1358 static void conf_queue_dtmf(const struct ast_conference *conf,
1359 const struct ast_conf_user *sender, struct ast_frame *f)
1361 struct ast_conf_user *user;
1363 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1366 if (ast_write(user->chan, f) < 0)
1367 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1371 static void sla_queue_event_full(enum sla_event_type type,
1372 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1374 struct sla_event *event;
1376 if (!(event = ast_calloc(1, sizeof(*event))))
1380 event->trunk_ref = trunk_ref;
1381 event->station = station;
1384 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1388 ast_mutex_lock(&sla.lock);
1389 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1390 ast_cond_signal(&sla.cond);
1391 ast_mutex_unlock(&sla.lock);
1394 static void sla_queue_event_nolock(enum sla_event_type type)
1396 sla_queue_event_full(type, NULL, NULL, 0);
1399 static void sla_queue_event(enum sla_event_type type)
1401 sla_queue_event_full(type, NULL, NULL, 1);
1404 /*! \brief Queue a SLA event from the conference */
1405 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1406 struct ast_conference *conf)
1408 struct sla_station *station;
1409 struct sla_trunk_ref *trunk_ref = NULL;
1412 trunk_name = ast_strdupa(conf->confno);
1413 strsep(&trunk_name, "_");
1414 if (ast_strlen_zero(trunk_name)) {
1415 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1419 AST_RWLIST_RDLOCK(&sla_stations);
1420 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1421 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1422 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1428 AST_RWLIST_UNLOCK(&sla_stations);
1431 ast_debug(1, "Trunk not found for event!\n");
1435 sla_queue_event_full(type, trunk_ref, station, 1);
1438 /* Decrement reference counts, as incremented by find_conf() */
1439 static int dispose_conf(struct ast_conference *conf)
1444 AST_LIST_LOCK(&confs);
1445 if (ast_atomic_dec_and_test(&conf->refcount)) {
1446 /* Take the conference room number out of an inuse state */
1447 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1448 conf_map[confno_int] = 0;
1452 AST_LIST_UNLOCK(&confs);
1458 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1460 struct ast_conf_user *user = NULL;
1461 struct ast_conf_user *usr = NULL;
1463 struct zt_confinfo ztc, ztc_empty;
1464 struct ast_frame *f;
1465 struct ast_channel *c;
1466 struct ast_frame fr;
1474 int musiconhold = 0;
1477 int currentmarked = 0;
1480 int menu_active = 0;
1481 int talkreq_manager = 0;
1482 int using_pseudo = 0;
1487 int announcement_played = 0;
1489 struct ast_dsp *dsp=NULL;
1490 struct ast_app *app;
1491 const char *agifile;
1492 const char *agifiledefault = "conf-background.agi";
1493 char meetmesecs[30] = "";
1494 char exitcontext[AST_MAX_CONTEXT] = "";
1495 char recordingtmp[AST_MAX_EXTENSION] = "";
1496 char members[10] = "";
1497 int dtmf, opt_waitmarked_timeout = 0;
1500 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1501 char *buf = __buf + AST_FRIENDLY_OFFSET;
1502 char *exitkeys = NULL;
1504 unsigned int calldurationlimit = 0;
1506 long play_warning = 0;
1507 long warning_freq = 0;
1508 const char *warning_sound = NULL;
1509 const char *end_sound = NULL;
1511 long time_left_ms = 0;
1512 struct timeval nexteventts = { 0, };
1515 if (!(user = ast_calloc(1, sizeof(*user))))
1518 /* Possible timeout waiting for marked user */
1519 if ((confflags & CONFFLAG_WAITMARKED) &&
1520 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1521 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1522 (opt_waitmarked_timeout > 0)) {
1523 timeout = time(NULL) + opt_waitmarked_timeout;
1526 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1527 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1528 if (option_verbose > 2)
1529 ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit);
1532 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1533 char *limit_str, *warning_str, *warnfreq_str;
1536 parse = optargs[OPT_ARG_DURATION_LIMIT];
1537 limit_str = strsep(&parse, ":");
1538 warning_str = strsep(&parse, ":");
1539 warnfreq_str = parse;
1541 timelimit = atol(limit_str);
1543 play_warning = atol(warning_str);
1545 warning_freq = atol(warnfreq_str);
1548 timelimit = play_warning = warning_freq = 0;
1549 warning_sound = NULL;
1550 } else if (play_warning > timelimit) {
1551 if (!warning_freq) {
1554 while (play_warning > timelimit)
1555 play_warning -= warning_freq;
1556 if (play_warning < 1)
1557 play_warning = warning_freq = 0;
1561 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_WARNING_FILE");
1562 warning_sound = var ? var : "timeleft";
1564 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_TIMEOUT_FILE");
1565 end_sound = var ? var : NULL;
1567 /* undo effect of S(x) in case they are both used */
1568 calldurationlimit = 0;
1569 /* more efficient do it like S(x) does since no advanced opts */
1570 if (!play_warning && !end_sound && timelimit) {
1571 calldurationlimit = timelimit / 1000;
1572 timelimit = play_warning = warning_freq = 0;
1574 ast_debug(2, "Limit Data for this call:\n");
1575 ast_debug(2, "- timelimit = %ld\n", timelimit);
1576 ast_debug(2, "- play_warning = %ld\n", play_warning);
1577 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1578 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1579 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1584 if ((confflags & CONFFLAG_KEYEXIT)) {
1585 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1586 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1588 exitkeys = ast_strdupa("#"); /* Default */
1591 if (confflags & CONFFLAG_RECORDCONF) {
1592 if (!conf->recordingfilename) {
1593 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1594 if (!conf->recordingfilename) {
1595 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1596 conf->recordingfilename = ast_strdupa(recordingtmp);
1598 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1599 if (!conf->recordingformat) {
1600 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1601 conf->recordingformat = ast_strdupa(recordingtmp);
1603 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1604 conf->confno, conf->recordingfilename, conf->recordingformat);
1608 ast_mutex_lock(&conf->recordthreadlock);
1609 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1610 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1611 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1613 ztc.confno = conf->zapconf;
1614 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1615 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1616 ast_log(LOG_WARNING, "Error starting listen channel\n");
1617 ast_hangup(conf->lchan);
1620 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1623 ast_mutex_unlock(&conf->recordthreadlock);
1625 time(&user->jointime);
1627 user->timelimit = timelimit;
1628 user->play_warning = play_warning;
1629 user->warning_freq = warning_freq;
1630 user->warning_sound = warning_sound;
1631 user->end_sound = end_sound;
1633 if (calldurationlimit > 0) {
1634 time(&user->kicktime);
1635 user->kicktime = user->kicktime + calldurationlimit;
1638 if (ast_tvzero(user->start_time))
1639 user->start_time = ast_tvnow();
1640 time_left_ms = user->timelimit;
1642 if (user->timelimit) {
1643 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1644 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1647 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1648 /* Sorry, but this conference is locked! */
1649 if (!ast_streamfile(chan, "conf-locked", chan->language))
1650 ast_waitstream(chan, "");
1654 if (confflags & CONFFLAG_MARKEDUSER)
1655 conf->markedusers++;
1657 ast_mutex_lock(&conf->playlock);
1659 if (AST_LIST_EMPTY(&conf->userlist))
1662 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1664 if (rt_schedule && conf->maxusers)
1665 if (user->user_no > conf->maxusers){
1666 /* Sorry, but this confernce has reached the participant limit! */
1667 if (!ast_streamfile(chan, "conf-full", chan->language))
1668 ast_waitstream(chan, "");
1672 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1675 user->userflags = confflags;
1676 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1679 if (rt_log_members) {
1681 snprintf(members, sizeof(members), "%d", conf->users);
1682 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1684 /* This device changed state now - if this is the first user */
1685 if (conf->users == 1)
1686 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1688 ast_mutex_unlock(&conf->playlock);
1690 /* return the unique ID of the conference */
1691 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1693 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1694 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1695 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1696 else if (!ast_strlen_zero(chan->macrocontext))
1697 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1699 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1702 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1703 snprintf(user->namerecloc, sizeof(user->namerecloc),
1704 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1705 conf->confno, user->user_no);
1706 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1707 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1709 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1714 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1715 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1716 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1717 ast_waitstream(chan, "");
1718 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1719 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1720 ast_waitstream(chan, "");
1723 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1724 int keepplaying = 1;
1726 if (conf->users == 2) {
1727 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1728 res = ast_waitstream(chan, AST_DIGIT_ANY);
1729 ast_stopstream(chan);
1736 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1737 res = ast_waitstream(chan, AST_DIGIT_ANY);
1738 ast_stopstream(chan);
1745 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1751 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1752 res = ast_waitstream(chan, AST_DIGIT_ANY);
1753 ast_stopstream(chan);
1762 ast_indicate(chan, -1);
1764 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1765 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1769 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1770 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1774 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
1775 user->zapchannel = !retryzap;
1778 origfd = chan->fds[0];
1780 fd = open("/dev/zap/pseudo", O_RDWR);
1782 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1786 /* Make non-blocking */
1787 flags = fcntl(fd, F_GETFL);
1789 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1793 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1794 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1798 /* Setup buffering information */
1799 memset(&bi, 0, sizeof(bi));
1800 bi.bufsize = CONF_SIZE/2;
1801 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1802 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1803 bi.numbufs = audio_buffers;
1804 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1805 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1810 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1811 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1817 /* XXX Make sure we're not running on a pseudo channel XXX */
1821 memset(&ztc, 0, sizeof(ztc));
1822 memset(&ztc_empty, 0, sizeof(ztc_empty));
1823 /* Check to see if we're in a conference... */
1825 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1826 ast_log(LOG_WARNING, "Error getting conference\n");
1831 /* Whoa, already in a conference... Retry... */
1833 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1838 memset(&ztc, 0, sizeof(ztc));
1839 /* Add us to the conference */
1841 ztc.confno = conf->zapconf;
1843 ast_mutex_lock(&conf->playlock);
1845 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1846 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1847 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1848 ast_waitstream(conf->chan, "");
1849 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1850 ast_waitstream(conf->chan, "");
1854 if (confflags & CONFFLAG_MONITOR)
1855 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1856 else if (confflags & CONFFLAG_TALKER)
1857 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1859 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1861 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1862 ast_log(LOG_WARNING, "Error setting conference\n");
1864 ast_mutex_unlock(&conf->playlock);
1867 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1870 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1875 chan->name, chan->uniqueid, conf->confno, user->user_no);
1879 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1881 if (!(confflags & CONFFLAG_QUIET))
1882 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1883 conf_play(chan, conf, ENTER);
1886 ast_mutex_unlock(&conf->playlock);
1888 conf_flush(fd, chan);
1890 if (confflags & CONFFLAG_AGI) {
1891 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1892 or use default filename of conf-background.agi */
1894 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1896 agifile = agifiledefault;
1898 if (user->zapchannel) {
1899 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1901 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1903 /* Find a pointer to the agi app and execute the script */
1904 app = pbx_findapp("agi");
1906 char *s = ast_strdupa(agifile);
1907 ret = pbx_exec(chan, app, s);
1909 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1912 if (user->zapchannel) {
1913 /* Remove CONFMUTE mode on Zap channel */
1915 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1918 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1919 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1921 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1923 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1924 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1928 int menu_was_active = 0;
1934 char currenttime[32];
1938 if (now.tv_sec % 60 == 0) {
1940 ast_localtime(&now, &tm, NULL);
1941 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1942 if (strcmp(currenttime, conf->endtime) > 0){
1943 ast_verbose("Quitting time...\n");
1947 if (!announcement_played && conf->endalert) {
1948 now.tv_sec += conf->endalert;
1949 ast_localtime(&now, &tm, NULL);
1950 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1951 if (strcmp(currenttime, conf->endtime) > 0){
1952 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1953 ast_waitstream(chan, "");
1954 ast_say_digits(chan, conf->endalert / 60, "", chan->language);
1955 if (!ast_streamfile(chan, "minutes", chan->language))
1956 ast_waitstream(chan, "");
1957 announcement_played = 1;
1969 time(&myt); /* get current time */
1970 if (user->kicktime && (user->kicktime < myt))
1974 if (user->timelimit) {
1976 int minutes = 0, seconds = 0, remain = 0;
1979 to = ast_tvdiff_ms(nexteventts, now);
1982 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
1983 if (time_left_ms < to)
1986 if (time_left_ms <= 0) {
1987 if (user->end_sound){
1988 res = ast_streamfile(chan, user->end_sound, chan->language);
1989 res = ast_waitstream(chan, "");
1995 if (time_left_ms >= 5000) {
1997 remain = (time_left_ms + 500) / 1000;
1998 if (remain / 60 >= 1) {
1999 minutes = remain / 60;
2000 seconds = remain % 60;
2005 /* force the time left to round up if appropriate */
2006 if (user->warning_sound && user->play_warning){
2007 if (!strcmp(user->warning_sound,"timeleft")) {
2009 res = ast_streamfile(chan, "vm-youhave", chan->language);
2010 res = ast_waitstream(chan, "");
2012 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2013 res = ast_streamfile(chan, "queue-minutes", chan->language);
2014 res = ast_waitstream(chan, "");
2017 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2018 res = ast_streamfile(chan, "queue-seconds", chan->language);
2019 res = ast_waitstream(chan, "");
2022 res = ast_streamfile(chan, user->warning_sound, chan->language);
2023 res = ast_waitstream(chan, "");
2027 if (user->warning_freq)
2028 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2030 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2034 if (timeout && time(NULL) >= timeout)
2037 /* if we have just exited from the menu, and the user had a channel-driver
2038 volume adjustment, restore it
2040 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2041 set_talk_volume(user, user->listen.desired);
2043 menu_was_active = menu_active;
2045 currentmarked = conf->markedusers;
2046 if (!(confflags & CONFFLAG_QUIET) &&
2047 (confflags & CONFFLAG_MARKEDUSER) &&
2048 (confflags & CONFFLAG_WAITMARKED) &&
2050 if (currentmarked == 1 && conf->users > 1) {
2051 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2052 if (conf->users - 1 == 1) {
2053 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2054 ast_waitstream(chan, "");
2056 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2057 ast_waitstream(chan, "");
2060 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2061 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2062 ast_waitstream(chan, "");
2065 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2068 /* Update the struct with the actual confflags */
2069 user->userflags = confflags;
2071 if (confflags & CONFFLAG_WAITMARKED) {
2072 if(currentmarked == 0) {
2073 if (lastmarked != 0) {
2074 if (!(confflags & CONFFLAG_QUIET))
2075 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2076 ast_waitstream(chan, "");
2077 if (confflags & CONFFLAG_MARKEDEXIT) {
2078 if (confflags & CONFFLAG_KICK_CONTINUE)
2082 ztc.confmode = ZT_CONF_CONF;
2083 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2084 ast_log(LOG_WARNING, "Error setting conference\n");
2090 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
2091 ast_moh_start(chan, NULL, NULL);
2094 } else if(currentmarked >= 1 && lastmarked == 0) {
2095 /* Marked user entered, so cancel timeout */
2097 if (confflags & CONFFLAG_MONITOR)
2098 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
2099 else if (confflags & CONFFLAG_TALKER)
2100 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
2102 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
2103 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2104 ast_log(LOG_WARNING, "Error setting conference\n");
2108 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2112 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2113 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2114 ast_waitstream(chan, "");
2115 conf_play(chan, conf, ENTER);
2120 /* trying to add moh for single person conf */
2121 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2122 if (conf->users == 1) {
2123 if (musiconhold == 0) {
2124 ast_moh_start(chan, NULL, NULL);
2135 /* Leave if the last marked user left */
2136 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2137 if (confflags & CONFFLAG_KICK_CONTINUE)
2144 /* Check if my modes have changed */
2146 /* If I should be muted but am still talker, mute me */
2147 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
2148 ztc.confmode ^= ZT_CONF_TALKER;
2149 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2150 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2155 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2161 chan->name, chan->uniqueid, conf->confno, user->user_no);
2164 /* If I should be un-muted but am not talker, un-mute me */
2165 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
2166 ztc.confmode |= ZT_CONF_TALKER;
2167 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2168 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2173 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2179 chan->name, chan->uniqueid, conf->confno, user->user_no);
2182 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2183 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2184 talkreq_manager = 1;
2186 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2192 chan->name, chan->uniqueid, conf->confno, user->user_no);
2196 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2197 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2198 talkreq_manager = 0;
2199 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2205 chan->name, chan->uniqueid, conf->confno, user->user_no);
2208 /* If I have been kicked, exit the conference */
2209 if (user->adminflags & ADMINFLAG_KICKME) {
2210 //You have been kicked.
2211 if (!(confflags & CONFFLAG_QUIET) &&
2212 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2213 ast_waitstream(chan, "");
2219 /* Perform an extra hangup check just in case */
2220 if (ast_check_hangup(chan))
2224 if (c->fds[0] != origfd || (user->zapchannel && c->audiohooks)) {
2226 /* Kill old pseudo */
2230 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2231 retryzap = (strcasecmp(c->tech->type, "Zap") || c->audiohooks ? 1 : 0);
2232 user->zapchannel = !retryzap;
2235 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2236 f = ast_read_noaudio(c);
2241 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2242 if (user->talk.actual)
2243 ast_frame_adjust_volume(f, user->talk.actual);
2245 if (!(confflags & CONFFLAG_MONITOR)) {
2248 if (user->talking == -1)
2251 res = ast_dsp_silence(dsp, f, &totalsilence);
2252 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2254 if (confflags & CONFFLAG_MONITORTALKER)
2255 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2261 chan->name, chan->uniqueid, conf->confno, user->user_no);
2263 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2265 if (confflags & CONFFLAG_MONITORTALKER)
2266 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2272 chan->name, chan->uniqueid, conf->confno, user->user_no);
2276 /* Absolutely do _not_ use careful_write here...
2277 it is important that we read data from the channel
2278 as fast as it arrives, and feed it into the conference.
2279 The buffering in the pseudo channel will take care of any
2280 timing differences, unless they are so drastic as to lose
2281 audio frames (in which case carefully writing would only
2282 have delayed the audio even further).
2284 /* As it turns out, we do want to use careful write. We just
2285 don't want to block, but we do want to at least *try*
2286 to write out all the samples.
2289 careful_write(fd, f->data, f->datalen, 0);
2291 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2294 if (confflags & CONFFLAG_PASS_DTMF)
2295 conf_queue_dtmf(conf, user, f);
2297 tmp[0] = f->subclass;
2299 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2300 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2305 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2307 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2310 exitkey[0] = f->subclass;
2313 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2315 if (confflags & CONFFLAG_PASS_DTMF)
2316 conf_queue_dtmf(conf, user, f);
2320 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2321 if (confflags & CONFFLAG_PASS_DTMF)
2322 conf_queue_dtmf(conf, user, f);
2323 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2324 ast_log(LOG_WARNING, "Error setting conference\n");
2330 /* if we are entering the menu, and the user has a channel-driver
2331 volume adjustment, clear it
2333 if (!menu_active && user->talk.desired && !user->talk.actual)
2334 set_talk_volume(user, 0);
2339 if ((confflags & CONFFLAG_ADMIN)) {
2343 /* Record this sound! */
2344 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2345 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2346 ast_stopstream(chan);
2353 case '1': /* Un/Mute */
2356 /* for admin, change both admin and use flags */
2357 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2358 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2360 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2362 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2363 if (!ast_streamfile(chan, "conf-muted", chan->language))
2364 ast_waitstream(chan, "");
2366 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2367 ast_waitstream(chan, "");
2370 case '2': /* Un/Lock the Conference */
2374 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2375 ast_waitstream(chan, "");
2378 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2379 ast_waitstream(chan, "");
2382 case '3': /* Eject last user */
2384 usr = AST_LIST_LAST(&conf->userlist);
2385 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2386 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2387 ast_waitstream(chan, "");
2389 usr->adminflags |= ADMINFLAG_KICKME;
2390 ast_stopstream(chan);
2393 tweak_listen_volume(user, VOL_DOWN);
2396 tweak_listen_volume(user, VOL_UP);
2399 tweak_talk_volume(user, VOL_DOWN);
2405 tweak_talk_volume(user, VOL_UP);
2409 /* Play an error message! */
2410 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2411 ast_waitstream(chan, "");
2419 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2420 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2421 ast_stopstream(chan);
2428 case '1': /* Un/Mute */
2431 /* user can only toggle the self-muted state */
2432 user->adminflags ^= ADMINFLAG_SELFMUTED;
2434 /* they can't override the admin mute state */
2435 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2436 if (!ast_streamfile(chan, "conf-muted", chan->language))
2437 ast_waitstream(chan, "");
2439 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2440 ast_waitstream(chan, "");
2445 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2446 user->adminflags |= ADMINFLAG_T_REQUEST;
2448 if (user->adminflags & ADMINFLAG_T_REQUEST)
2449 if (!ast_streamfile(chan, "beep", chan->language))
2450 ast_waitstream(chan, "");
2453 tweak_listen_volume(user, VOL_DOWN);
2456 tweak_listen_volume(user, VOL_UP);
2459 tweak_talk_volume(user, VOL_DOWN);
2465 tweak_talk_volume(user, VOL_UP);
2469 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2470 ast_waitstream(chan, "");
2476 ast_moh_start(chan, NULL, NULL);
2478 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2479 ast_log(LOG_WARNING, "Error setting conference\n");
2485 conf_flush(fd, chan);
2486 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2487 && confflags & CONFFLAG_PASS_DTMF) {
2488 conf_queue_dtmf(conf, user, f);
2489 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2490 switch (f->subclass) {
2491 case AST_CONTROL_HOLD:
2492 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2497 } else if (f->frametype == AST_FRAME_NULL) {
2498 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2501 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2502 chan->name, f->frametype, f->subclass);
2505 } else if (outfd > -1) {
2506 res = read(outfd, buf, CONF_SIZE);
2508 memset(&fr, 0, sizeof(fr));
2509 fr.frametype = AST_FRAME_VOICE;
2510 fr.subclass = AST_FORMAT_SLINEAR;
2514 fr.offset = AST_FRIENDLY_OFFSET;
2515 if ( !user->listen.actual &&
2516 ((confflags & CONFFLAG_MONITOR) ||
2517 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2518 (!user->talking)) ) {
2520 for (index=0;index<AST_FRAME_BITS;index++)
2521 if (chan->rawwriteformat & (1 << index))
2523 if (index >= AST_FRAME_BITS)
2524 goto bailoutandtrynormal;
2525 ast_mutex_lock(&conf->listenlock);
2526 if (!conf->transframe[index]) {
2527 if (conf->origframe) {
2528 if (!conf->transpath[index])
2529 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2530 if (conf->transpath[index]) {
2531 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2532 if (!conf->transframe[index])
2533 conf->transframe[index] = &ast_null_frame;
2537 if (conf->transframe[index]) {
2538 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2539 if (ast_write(chan, conf->transframe[index]))
2540 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2543 ast_mutex_unlock(&conf->listenlock);
2544 goto bailoutandtrynormal;
2546 ast_mutex_unlock(&conf->listenlock);
2548 bailoutandtrynormal:
2549 if (user->listen.actual)
2550 ast_frame_adjust_volume(&fr, user->listen.actual);
2551 if (ast_write(chan, &fr) < 0) {
2552 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2556 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2558 lastmarked = currentmarked;
2568 /* Take out of conference */
2572 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2573 ast_log(LOG_WARNING, "Error setting conference\n");
2577 reset_volumes(user);
2579 AST_LIST_LOCK(&confs);
2580 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2581 conf_play(chan, conf, LEAVE);
2583 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2584 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2585 if ((conf->chan) && (conf->users > 1)) {
2586 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2587 ast_waitstream(conf->chan, "");
2588 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2589 ast_waitstream(conf->chan, "");
2591 ast_filedelete(user->namerecloc, NULL);
2594 AST_LIST_UNLOCK(&confs);
2597 AST_LIST_LOCK(&confs);
2602 if (user->user_no) { /* Only cleanup users who really joined! */
2604 hr = (now.tv_sec - user->jointime) / 3600;
2605 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2606 sec = (now.tv_sec - user->jointime) % 60;
2609 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2614 "CallerIDNum: %s\r\n"
2615 "CallerIDName: %s\r\n"
2616 "Duration: %ld\r\n",
2617 chan->name, chan->uniqueid, conf->confno,
2619 S_OR(user->chan->cid.cid_num, "<unknown>"),
2620 S_OR(user->chan->cid.cid_name, "<unknown>"),
2621 (long)(now.tv_sec - user->jointime));
2625 if (rt_log_members){
2627 snprintf(members, sizeof(members), "%d", conf->users);
2628 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2630 if (confflags & CONFFLAG_MARKEDUSER)
2631 conf->markedusers--;
2632 /* Remove ourselves from the list */
2633 AST_LIST_REMOVE(&conf->userlist, user, list);
2635 /* Change any states */
2637 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2639 /* Return the number of seconds the user was in the conf */
2640 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2641 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2644 AST_LIST_UNLOCK(&confs);
2649 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2650 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2651 char *optargs[], int *too_early)
2653 struct ast_variable *var;
2654 struct ast_conference *cnf;
2658 /* Check first in the conference list */
2659 AST_LIST_LOCK(&confs);
2660 AST_LIST_TRAVERSE(&confs, cnf, list) {
2661 if (!strcmp(confno, cnf->confno))
2665 cnf->refcount += refcount;
2667 AST_LIST_UNLOCK(&confs);
2670 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2673 char currenttime[19] = "";
2674 char startTime[19] = "";
2675 char endtime[19] = "";
2676 char eatime[19] = "";
2677 char userOpts[32] = "";
2678 char adminOpts[32] = "";
2679 struct ast_tm tm, etm;
2685 now.tv_sec += fuzzystart;
2687 ast_localtime(&now, &tm, NULL);
2688 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2691 now.tv_sec += earlyalert;
2692 ast_localtime(&now, &etm, NULL);
2693 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2695 ast_copy_string(eatime, currenttime, sizeof(eatime));
2698 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2700 var = ast_load_realtime("meetme", "confno",
2701 confno, "startTime<= ", eatime, "endtime>= ",
2704 var = ast_load_realtime("meetme", "confno", confno, NULL);
2710 if (!strcasecmp(var->name, "pin")) {
2711 pin = ast_strdupa(var->value);
2712 } else if (!strcasecmp(var->name, "adminpin")) {
2713 pinadmin = ast_strdupa(var->value);
2714 } else if (!strcasecmp(var->name, "opts")) {
2715 ast_copy_string(userOpts, var->value, sizeof(userOpts));
2716 } else if (!strcasecmp(var->name, "maxusers")) {
2717 maxusers = atoi(var->value);
2718 } else if (!strcasecmp(var->name, "adminopts")) {
2719 ast_copy_string(adminOpts, var->value, sizeof(adminOpts));
2720 } else if (!strcasecmp(var->name, "endtime")) {
2721 ast_copy_string(endtime, var->value, sizeof(endtime));
2722 } else if (!strcasecmp(var->name, "starttime")) {
2723 ast_copy_string(startTime, var->value, sizeof(startTime));
2728 ast_variables_destroy(var);
2731 if (strcmp(startTime, currenttime) > 0) {
2732 /* Announce that the caller is early and exit */
2733 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2734 ast_waitstream(chan, "");
2740 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2743 cnf->maxusers = maxusers;
2744 cnf->endalert = endalert;
2745 ast_copy_string(cnf->endtime, endtime, sizeof(cnf->endtime));
2750 if (confflags && !cnf->chan &&
2751 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2752 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2753 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2754 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2757 if (confflags && !cnf->chan &&
2758 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2759 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2760 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2768 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2769 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2771 struct ast_config *cfg;
2772 struct ast_variable *var;
2773 struct ast_flags config_flags = { 0 };
2774 struct ast_conference *cnf;
2776 AST_DECLARE_APP_ARGS(args,
2777 AST_APP_ARG(confno);
2779 AST_APP_ARG(pinadmin);
2782 /* Check first in the conference list */
2783 ast_debug(1,"The requested confno is '%s'?\n", confno);
2784 AST_LIST_LOCK(&confs);
2785 AST_LIST_TRAVERSE(&confs, cnf, list) {
2786 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2787 if (!strcmp(confno, cnf->confno))
2791 cnf->refcount += refcount;
2793 AST_LIST_UNLOCK(&confs);
2797 /* No need to parse meetme.conf */
2798 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2800 if (dynamic_pin[0] == 'q') {
2801 /* Query the user to enter a PIN */
2802 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2805 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2807 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2810 /* Check the config */
2811 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2813 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2816 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2817 if (strcasecmp(var->name, "conf"))
2820 if (!(parse = ast_strdupa(var->value)))
2823 AST_STANDARD_APP_ARGS(args, parse);
2824 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2825 if (!strcasecmp(args.confno, confno)) {
2826 /* Bingo it's a valid conference */
2827 cnf = build_conf(args.confno,
2829 S_OR(args.pinadmin, ""),
2830 make, dynamic, refcount, chan);
2835 ast_debug(1, "%s isn't a valid conference\n", confno);
2837 ast_config_destroy(cfg);
2839 } else if (dynamic_pin) {
2840 /* Correct for the user selecting 'D' instead of 'd' to have
2841 someone join into a conference that has already been created
2843 if (dynamic_pin[0] == 'q')
2844 dynamic_pin[0] = '\0';
2848 if (confflags && !cnf->chan &&
2849 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2850 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2851 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2852 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2855 if (confflags && !cnf->chan &&
2856 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2857 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2858 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2865 /*! \brief The MeetmeCount application */
2866 static int count_exec(struct ast_channel *chan, void *data)
2869 struct ast_conference *conf;
2873 AST_DECLARE_APP_ARGS(args,
2874 AST_APP_ARG(confno);
2875 AST_APP_ARG(varname);
2878 if (ast_strlen_zero(data)) {
2879 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2883 if (!(localdata = ast_strdupa(data)))
2886 AST_STANDARD_APP_ARGS(args, localdata);
2888 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2891 count = conf->users;
2897 if (!ast_strlen_zero(args.varname)){
2898 /* have var so load it and exit */
2899 snprintf(val, sizeof(val), "%d",count);
2900 pbx_builtin_setvar_helper(chan, args.varname, val);
2902 if (chan->_state != AST_STATE_UP)
2904 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2910 /*! \brief The meetme() application */
2911 static int conf_exec(struct ast_channel *chan, void *data)
2914 char confno[MAX_CONFNUM] = "";
2917 struct ast_conference *cnf = NULL;
2918 struct ast_flags confflags = {0}, config_flags = { 0 };
2920 int empty = 0, empty_no_pin = 0;
2921 int always_prompt = 0;
2922 char *notdata, *info, the_pin[MAX_PIN] = "";
2923 AST_DECLARE_APP_ARGS(args,
2924 AST_APP_ARG(confno);
2925 AST_APP_ARG(options);
2928 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2930 if (ast_strlen_zero(data)) {
2937 if (chan->_state != AST_STATE_UP)
2940 info = ast_strdupa(notdata);
2942 AST_STANDARD_APP_ARGS(args, info);
2945 ast_copy_string(confno, args.confno, sizeof(confno));
2946 if (ast_strlen_zero(confno)) {
2952 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2955 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2956 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2957 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2958 strcpy(the_pin, "q");
2960 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2961 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2962 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2970 struct ast_config *cfg;
2971 struct ast_variable *var;
2974 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2975 if ((empty_no_pin) || (!dynamic)) {
2976 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2978 var = ast_variable_browse(cfg, "rooms");
2980 if (!strcasecmp(var->name, "conf")) {
2981 char *stringp = ast_strdupa(var->value);
2983 char *confno_tmp = strsep(&stringp, "|,");
2986 /* For static: run through the list and see if this conference is empty */
2987 AST_LIST_LOCK(&confs);
2988 AST_LIST_TRAVERSE(&confs, cnf, list) {
2989 if (!strcmp(confno_tmp, cnf->confno)) {
2990 /* The conference exists, therefore it's not empty */
2995 AST_LIST_UNLOCK(&confs);
2997 /* At this point, we have a confno_tmp (static conference) that is empty */
2998 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2999 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
3000 * Case 2: empty_no_pin and pin is blank (but not NULL)
3001 * Case 3: not empty_no_pin
3003 ast_copy_string(confno, confno_tmp, sizeof(confno));
3005 /* XXX the map is not complete (but we do have a confno) */
3013 ast_config_destroy(cfg);
3017 /* Select first conference number not in use */
3018 if (ast_strlen_zero(confno) && dynamic) {
3019 AST_LIST_LOCK(&confs);
3020 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
3022 snprintf(confno, sizeof(confno), "%d", i);
3027 AST_LIST_UNLOCK(&confs);
3031 if (ast_strlen_zero(confno)) {
3032 res = ast_streamfile(chan, "conf-noempty", chan->language);
3034 ast_waitstream(chan, "");
3036 if (sscanf(confno, "%d", &confno_int) == 1) {
3037 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
3038 res = ast_streamfile(chan, "conf-enteringno", chan->language);