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/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/astobj.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.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_MOH_CLASS = 4,
170 OPT_ARG_ARRAY_SIZE = 5,
173 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
174 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
175 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
176 AST_APP_OPTION('b', CONFFLAG_AGI ),
177 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
178 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
179 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
180 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
181 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
182 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
183 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
184 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
185 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
186 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
187 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
188 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
189 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
190 AST_APP_OPTION('q', CONFFLAG_QUIET ),
191 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
192 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
193 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
194 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
195 AST_APP_OPTION('t', CONFFLAG_TALKER ),
196 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
197 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
198 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
199 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
200 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
201 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
204 static const char *app = "MeetMe";
205 static const char *app2 = "MeetMeCount";
206 static const char *app3 = "MeetMeAdmin";
207 static const char *app4 = "MeetMeChannelAdmin";
208 static const char *slastation_app = "SLAStation";
209 static const char *slatrunk_app = "SLATrunk";
211 static const char *synopsis = "MeetMe conference bridge";
212 static const char *synopsis2 = "MeetMe participant count";
213 static const char *synopsis3 = "MeetMe conference Administration";
214 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
215 static const char *slastation_synopsis = "Shared Line Appearance Station";
216 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
218 /* Lookup RealTime conferences based on confno and current time */
219 static int rt_schedule;
220 static int fuzzystart;
221 static int earlyalert;
224 /* Log participant count to the RealTime backend */
225 static int rt_log_members;
227 static const char *descrip =
228 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
229 "conference. If the conference number is omitted, the user will be prompted\n"
230 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
231 "is specified, by pressing '#'.\n"
232 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
233 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
234 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
235 "The option string may contain zero or more of the following characters:\n"
236 " 'a' -- set admin mode\n"
237 " 'A' -- set marked mode\n"
238 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
239 " Default: conf-background.agi (Note: This does not work with\n"
240 " non-Zap channels in the same conference)\n"
241 " 'c' -- announce user(s) count on joining a conference\n"
242 " 'C' -- continue in dialplan when kicked out of conference\n"
243 " 'd' -- dynamically add conference\n"
244 " 'D' -- dynamically add conference, prompting for a PIN\n"
245 " 'e' -- select an empty conference\n"
246 " 'E' -- select an empty pinless conference\n"
247 " 'F' -- Pass DTMF through the conference.\n"
248 " 'i' -- announce user join/leave with review\n"
249 " 'I' -- announce user join/leave without review\n"
250 " 'l' -- set listen only mode (Listen only, no talking)\n"
251 " 'm' -- set initially muted\n"
253 " -- enable music on hold when the conference has a single caller.\n"
254 " Optionally, specify a musiconhold class to use. If one is not\n"
255 " provided, it will use the channel's currently set music class,\n"
257 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
258 " being muted, meaning (a) No encode is done on transmission and\n"
259 " (b) Received audio that is not registered as talking is omitted\n"
260 " causing no buildup in background noise\n"
262 " -- allow user to exit the conference by pressing '#' (default)\n"
263 " or any of the defined keys. If keys contain '*' this will override\n"
264 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
265 " 'P' -- always prompt for the pin even if it is specified\n"
266 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
267 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
268 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
269 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
271 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
272 " 't' -- set talk only mode. (Talk only, no listening)\n"
273 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
275 " -- wait until the marked user enters the conference\n"
276 " 'x' -- close the conference when last marked user exits\n"
277 " 'X' -- allow user to exit the conference by entering a valid single\n"
278 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
279 " if that variable is not defined.\n"
280 " '1' -- do not play message when first person enters\n"
281 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
282 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
283 " left. Repeat the warning every 'z' ms. The following special\n"
284 " variables can be used with this option:\n"
285 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
286 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
287 " The default is to say the time remaining.\n"
290 static const char *descrip2 =
291 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
292 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
293 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
294 "the channel, unless priority n+1 exists, in which case priority progress will\n"
296 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
298 static const char *descrip3 =
299 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
300 " 'e' -- Eject last user that joined\n"
301 " 'k' -- Kick one user out of conference\n"
302 " 'K' -- Kick all users out of conference\n"
303 " 'l' -- Unlock conference\n"
304 " 'L' -- Lock conference\n"
305 " 'm' -- Unmute one user\n"
306 " 'M' -- Mute one user\n"
307 " 'n' -- Unmute all users in the conference\n"
308 " 'N' -- Mute all non-admin users in the conference\n"
309 " 'r' -- Reset one user's volume settings\n"
310 " 'R' -- Reset all users volume settings\n"
311 " 's' -- Lower entire conference speaking volume\n"
312 " 'S' -- Raise entire conference speaking volume\n"
313 " 't' -- Lower one user's talk volume\n"
314 " 'T' -- Raise one user's talk volume\n"
315 " 'u' -- Lower one user's listen volume\n"
316 " 'U' -- Raise one user's listen volume\n"
317 " 'v' -- Lower entire conference listening volume\n"
318 " 'V' -- Raise entire conference listening volume\n"
321 static const char *descrip4 =
322 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
323 "channel in any coference.\n"
324 " 'k' -- Kick the specified user out of the conference he is in\n"
325 " 'm' -- Unmute the specified user\n"
326 " 'M' -- Mute the specified user\n"
329 static const char *slastation_desc =
330 " SLAStation(<station name>):\n"
331 "This application should be executed by an SLA station. The argument depends\n"
332 "on how the call was initiated. If the phone was just taken off hook, then\n"
333 "the argument \"station\" should be just the station name. If the call was\n"
334 "initiated by pressing a line key, then the station name should be preceded\n"
335 "by an underscore and the trunk name associated with that line button.\n"
336 "For example: \"station1_line1\"."
337 " On exit, this application will set the variable SLASTATION_STATUS to\n"
338 "one of the following values:\n"
339 " FAILURE | CONGESTION | SUCCESS\n"
342 static const char *slatrunk_desc =
343 " SLATrunk(<trunk name>[,options]):\n"
344 "This application should be executed by an SLA trunk on an inbound call.\n"
345 "The channel calling this application should correspond to the SLA trunk\n"
346 "with the name \"trunk\" that is being passed as an argument.\n"
347 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
348 "one of the following values:\n"
349 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
350 " The available options are:\n"
351 " M[(<class>)] - Play back the specified MOH class instead of ringing\n"
354 #define MAX_CONFNUM 80
357 /*! \brief The MeetMe Conference object */
358 struct ast_conference {
359 ast_mutex_t playlock; /*!< Conference specific lock (players) */
360 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
361 char confno[MAX_CONFNUM]; /*!< Conference */
362 struct ast_channel *chan; /*!< Announcements channel */
363 struct ast_channel *lchan; /*!< Listen/Record channel */
364 int fd; /*!< Announcements fd */
365 int zapconf; /*!< Zaptel Conf # */
366 int users; /*!< Number of active users */
367 int markedusers; /*!< Number of marked users */
368 int maxusers; /*!< Participant limit if scheduled */
369 int endalert; /*!< When to play conf ending message */
370 time_t start; /*!< Start time (s) */
371 int refcount; /*!< reference count of usage */
372 enum recording_state recording:2; /*!< recording status */
373 unsigned int isdynamic:1; /*!< Created on the fly? */
374 unsigned int locked:1; /*!< Is the conference locked? */
375 pthread_t recordthread; /*!< thread for recording */
376 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
377 pthread_attr_t attr; /*!< thread attribute */
378 const char *recordingfilename; /*!< Filename to record the Conference into */
379 const char *recordingformat; /*!< Format to record the Conference in */
380 char pin[MAX_PIN]; /*!< If protected by a PIN */
381 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
383 long endtime; /*!< When to end the conf if scheduled */
384 struct ast_frame *transframe[32];
385 struct ast_frame *origframe;
386 struct ast_trans_pvt *transpath[32];
387 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
388 AST_LIST_ENTRY(ast_conference) list;
391 static AST_LIST_HEAD_STATIC(confs, ast_conference);
393 static unsigned int conf_map[1024] = {0, };
396 int desired; /*!< Desired volume adjustment */
397 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
400 /*! \brief The MeetMe User object */
401 struct ast_conf_user {
402 int user_no; /*!< User Number */
403 int userflags; /*!< Flags as set in the conference */
404 int adminflags; /*!< Flags set by the Admin */
405 struct ast_channel *chan; /*!< Connected channel */
406 int talking; /*!< Is user talking */
407 int zapchannel; /*!< Is a Zaptel channel */
408 char usrvalue[50]; /*!< Custom User Value */
409 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
410 time_t jointime; /*!< Time the user joined the conference */
411 time_t kicktime; /*!< Time the user will be kicked from the conference */
412 struct timeval start_time; /*!< Time the user entered into the conference */
413 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
414 long play_warning; /*!< Play a warning when 'y' ms are left */
415 long warning_freq; /*!< Repeat the warning every 'z' ms */
416 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
417 const char *end_sound; /*!< File to play when time is up. */
419 struct volume listen;
420 AST_LIST_ENTRY(ast_conf_user) list;
423 enum sla_which_trunk_refs {
428 enum sla_trunk_state {
429 SLA_TRUNK_STATE_IDLE,
430 SLA_TRUNK_STATE_RINGING,
432 SLA_TRUNK_STATE_ONHOLD,
433 SLA_TRUNK_STATE_ONHOLD_BYME,
436 enum sla_hold_access {
437 /*! This means that any station can put it on hold, and any station
438 * can retrieve the call from hold. */
440 /*! This means that only the station that put the call on hold may
441 * retrieve it from hold. */
445 struct sla_trunk_ref;
448 AST_RWLIST_ENTRY(sla_station) entry;
449 AST_DECLARE_STRING_FIELDS(
450 AST_STRING_FIELD(name);
451 AST_STRING_FIELD(device);
452 AST_STRING_FIELD(autocontext);
454 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
455 struct ast_dial *dial;
456 /*! Ring timeout for this station, for any trunk. If a ring timeout
457 * is set for a specific trunk on this station, that will take
458 * priority over this value. */
459 unsigned int ring_timeout;
460 /*! Ring delay for this station, for any trunk. If a ring delay
461 * is set for a specific trunk on this station, that will take
462 * priority over this value. */
463 unsigned int ring_delay;
464 /*! This option uses the values in the sla_hold_access enum and sets the
465 * access control type for hold on this station. */
466 unsigned int hold_access:1;
467 /*! Use count for inside sla_station_exec */
468 unsigned int ref_count;
471 struct sla_station_ref {
472 AST_LIST_ENTRY(sla_station_ref) entry;
473 struct sla_station *station;
477 AST_RWLIST_ENTRY(sla_trunk) entry;
478 AST_DECLARE_STRING_FIELDS(
479 AST_STRING_FIELD(name);
480 AST_STRING_FIELD(device);
481 AST_STRING_FIELD(autocontext);
483 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
484 /*! Number of stations that use this trunk */
485 unsigned int num_stations;
486 /*! Number of stations currently on a call with this trunk */
487 unsigned int active_stations;
488 /*! Number of stations that have this trunk on hold. */
489 unsigned int hold_stations;
490 struct ast_channel *chan;
491 unsigned int ring_timeout;
492 /*! If set to 1, no station will be able to join an active call with
494 unsigned int barge_disabled:1;
495 /*! This option uses the values in the sla_hold_access enum and sets the
496 * access control type for hold on this trunk. */
497 unsigned int hold_access:1;
498 /*! Whether this trunk is currently on hold, meaning that once a station
499 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
500 unsigned int on_hold:1;
501 /*! Use count for inside sla_trunk_exec */
502 unsigned int ref_count;
505 struct sla_trunk_ref {
506 AST_LIST_ENTRY(sla_trunk_ref) entry;
507 struct sla_trunk *trunk;
508 enum sla_trunk_state state;
509 struct ast_channel *chan;
510 /*! Ring timeout to use when this trunk is ringing on this specific
511 * station. This takes higher priority than a ring timeout set at
512 * the station level. */
513 unsigned int ring_timeout;
514 /*! Ring delay to use when this trunk is ringing on this specific
515 * station. This takes higher priority than a ring delay set at
516 * the station level. */
517 unsigned int ring_delay;
520 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
521 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
523 static const char sla_registrar[] = "SLA";
525 /*! \brief Event types that can be queued up for the SLA thread */
526 enum sla_event_type {
527 /*! A station has put the call on hold */
529 /*! The state of a dial has changed */
530 SLA_EVENT_DIAL_STATE,
531 /*! The state of a ringing trunk has changed */
532 SLA_EVENT_RINGING_TRUNK,
533 /*! A reload of configuration has been requested */
535 /*! Poke the SLA thread so it can check if it can perform a reload */
536 SLA_EVENT_CHECK_RELOAD,
540 enum sla_event_type type;
541 struct sla_station *station;
542 struct sla_trunk_ref *trunk_ref;
543 AST_LIST_ENTRY(sla_event) entry;
546 /*! \brief A station that failed to be dialed
547 * \note Only used by the SLA thread. */
548 struct sla_failed_station {
549 struct sla_station *station;
550 struct timeval last_try;
551 AST_LIST_ENTRY(sla_failed_station) entry;
554 /*! \brief A trunk that is ringing */
555 struct sla_ringing_trunk {
556 struct sla_trunk *trunk;
557 /*! The time that this trunk started ringing */
558 struct timeval ring_begin;
559 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
560 AST_LIST_ENTRY(sla_ringing_trunk) entry;
563 enum sla_station_hangup {
564 SLA_STATION_HANGUP_NORMAL,
565 SLA_STATION_HANGUP_TIMEOUT,
568 /*! \brief A station that is ringing */
569 struct sla_ringing_station {
570 struct sla_station *station;
571 /*! The time that this station started ringing */
572 struct timeval ring_begin;
573 AST_LIST_ENTRY(sla_ringing_station) entry;
577 * \brief A structure for data used by the sla thread
580 /*! The SLA thread ID */
584 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
585 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
586 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
587 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
589 /*! Attempt to handle CallerID, even though it is known not to work
590 * properly in some situations. */
591 unsigned int attempt_callerid:1;
592 /*! A reload has been requested */
593 unsigned int reload:1;
595 .thread = AST_PTHREADT_NULL,
598 /*! The number of audio buffers to be allocated on pseudo channels
599 * when in a conference */
600 static int audio_buffers;
602 /*! Map 'volume' levels from -5 through +5 into
603 * decibel (dB) settings for channel drivers
604 * Note: these are not a straight linear-to-dB
605 * conversion... the numbers have been modified
606 * to give the user a better level of adjustability
608 static char const gain_map[] = {
623 static int admin_exec(struct ast_channel *chan, void *data);
624 static void *recordthread(void *args);
626 static char *istalking(int x)
631 return "(unmonitored)";
633 return "(not talking)";
636 static int careful_write(int fd, unsigned char *data, int len, int block)
643 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
644 res = ioctl(fd, ZT_IOMUX, &x);
648 res = write(fd, data, len);
650 if (errno != EAGAIN) {
651 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
663 static int set_talk_volume(struct ast_conf_user *user, int volume)
667 /* attempt to make the adjustment in the channel driver;
668 if successful, don't adjust in the frame reading routine
670 gain_adjust = gain_map[volume + 5];
672 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
675 static int set_listen_volume(struct ast_conf_user *user, int volume)
679 /* attempt to make the adjustment in the channel driver;
680 if successful, don't adjust in the frame reading routine
682 gain_adjust = gain_map[volume + 5];
684 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
687 static void tweak_volume(struct volume *vol, enum volume_action action)
691 switch (vol->desired) {
706 switch (vol->desired) {
722 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
724 tweak_volume(&user->talk, action);
725 /* attempt to make the adjustment in the channel driver;
726 if successful, don't adjust in the frame reading routine
728 if (!set_talk_volume(user, user->talk.desired))
729 user->talk.actual = 0;
731 user->talk.actual = user->talk.desired;
734 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
736 tweak_volume(&user->listen, action);
737 /* attempt to make the adjustment in the channel driver;
738 if successful, don't adjust in the frame reading routine
740 if (!set_listen_volume(user, user->listen.desired))
741 user->listen.actual = 0;
743 user->listen.actual = user->listen.desired;
746 static void reset_volumes(struct ast_conf_user *user)
748 signed char zero_volume = 0;
750 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
751 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
754 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
760 if (!ast_check_hangup(chan))
761 res = ast_autoservice_start(chan);
763 AST_LIST_LOCK(&confs);
779 careful_write(conf->fd, data, len, 1);
782 AST_LIST_UNLOCK(&confs);
785 ast_autoservice_stop(chan);
789 * \brief Find or create a conference
791 * \param confno The conference name/number
792 * \param pin The regular user pin
793 * \param pinadmin The admin pin
794 * \param make Make the conf if it doesn't exist
795 * \param dynamic Mark the newly created conference as dynamic
796 * \param refcount How many references to mark on the conference
797 * \param chan The asterisk channel
799 * \return A pointer to the conference struct, or NULL if it wasn't found and
800 * make or dynamic were not set.
802 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
804 struct ast_conference *cnf;
805 struct zt_confinfo ztc = { 0, };
808 AST_LIST_LOCK(&confs);
810 AST_LIST_TRAVERSE(&confs, cnf, list) {
811 if (!strcmp(confno, cnf->confno))
815 if (cnf || (!make && !dynamic))
819 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
822 ast_mutex_init(&cnf->playlock);
823 ast_mutex_init(&cnf->listenlock);
824 cnf->recordthread = AST_PTHREADT_NULL;
825 ast_mutex_init(&cnf->recordthreadlock);
826 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
827 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
828 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
829 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
831 /* Setup a new zap conference */
833 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
834 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
835 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
836 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
844 cnf->zapconf = ztc.confno;
846 /* Setup a new channel for playback of audio files */
847 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
849 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
850 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
852 ztc.confno = cnf->zapconf;
853 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
854 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
855 ast_log(LOG_WARNING, "Error setting conference\n");
857 ast_hangup(cnf->chan);
867 /* Fill the conference struct */
868 cnf->start = time(NULL);
869 cnf->maxusers = 0x7fffffff;
870 cnf->isdynamic = dynamic ? 1 : 0;
871 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
872 AST_LIST_INSERT_HEAD(&confs, cnf, list);
874 /* Reserve conference number in map */
875 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
876 conf_map[confno_int] = 1;
880 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
882 AST_LIST_UNLOCK(&confs);
888 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
890 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
892 int len = strlen(word);
894 struct ast_conference *cnf = NULL;
895 struct ast_conf_user *usr = NULL;
898 char *myline, *ret = NULL;
900 if (pos == 1) { /* Command */
901 return ast_cli_complete(word, cmds, state);
902 } else if (pos == 2) { /* Conference Number */
903 AST_LIST_LOCK(&confs);
904 AST_LIST_TRAVERSE(&confs, cnf, list) {
905 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
910 ret = ast_strdup(ret); /* dup before releasing the lock */
911 AST_LIST_UNLOCK(&confs);
913 } else if (pos == 3) {
914 /* User Number || Conf Command option*/
915 if (strstr(line, "mute") || strstr(line, "kick")) {
916 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
917 return ast_strdup("all");
919 AST_LIST_LOCK(&confs);
921 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
922 myline = ast_strdupa(line);
923 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
924 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
928 AST_LIST_TRAVERSE(&confs, cnf, list) {
929 if (!strcmp(confno, cnf->confno))
934 /* Search for the user */
935 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
936 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
937 if (!strncasecmp(word, usrno, len) && ++which > state)
941 AST_LIST_UNLOCK(&confs);
942 return usr ? ast_strdup(usrno) : NULL;
943 } else if (strstr(line, "list") && (state == 0))
944 return ast_strdup("concise");
950 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
952 /* Process the command */
953 struct ast_conference *cnf;
954 struct ast_conf_user *user;
956 int i = 0, total = 0;
958 char cmdline[1024] = "";
959 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
960 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
964 e->command = "meetme";
966 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
967 " Executes a command for the conference or on a conferee\n";
970 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
974 ast_cli(a->fd, "Invalid Arguments.\n");
975 /* Check for length so no buffer will overflow... */
976 for (i = 0; i < a->argc; i++) {
977 if (strlen(a->argv[i]) > 100)
978 ast_cli(a->fd, "Invalid Arguments.\n");
980 if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
981 /* 'MeetMe': List all the conferences */
982 int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
984 AST_LIST_LOCK(&confs);
985 if (AST_LIST_EMPTY(&confs)) {
987 ast_cli(a->fd, "No active MeetMe conferences.\n");
988 AST_LIST_UNLOCK(&confs);
992 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
993 AST_LIST_TRAVERSE(&confs, cnf, list) {
994 if (cnf->markedusers == 0)
995 strcpy(cmdline, "N/A ");
997 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
998 hr = (now - cnf->start) / 3600;
999 min = ((now - cnf->start) % 3600) / 60;
1000 sec = (now - cnf->start) % 60;
1002 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1004 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1013 total += cnf->users;
1015 AST_LIST_UNLOCK(&confs);
1017 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1021 return CLI_SHOWUSAGE;
1022 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
1023 if (strstr(a->argv[1], "lock")) {
1024 if (strcmp(a->argv[1], "lock") == 0) {
1026 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
1029 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
1031 } else if (strstr(a->argv[1], "mute")) {
1033 return CLI_SHOWUSAGE;
1034 if (strcmp(a->argv[1], "mute") == 0) {
1036 if (strcmp(a->argv[3], "all") == 0) {
1037 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
1039 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
1040 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1044 if (strcmp(a->argv[3], "all") == 0) {
1045 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
1047 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
1048 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1051 } else if (strcmp(a->argv[1], "kick") == 0) {
1053 return CLI_SHOWUSAGE;
1054 if (strcmp(a->argv[3], "all") == 0) {
1056 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1058 /* Kick a single user */
1059 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1060 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1062 } else if (strcmp(a->argv[1], "list") == 0) {
1063 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1064 /* List all the users in a conference */
1065 if (AST_LIST_EMPTY(&confs)) {
1067 ast_cli(a->fd, "No active conferences.\n");
1070 /* Find the right conference */
1071 AST_LIST_LOCK(&confs);
1072 AST_LIST_TRAVERSE(&confs, cnf, list) {
1073 if (strcmp(cnf->confno, a->argv[2]) == 0)
1078 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1079 AST_LIST_UNLOCK(&confs);
1082 /* Show all the users */
1084 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1085 hr = (now - user->jointime) / 3600;
1086 min = ((now - user->jointime) % 3600) / 60;
1087 sec = (now - user->jointime) % 60;
1089 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1091 S_OR(user->chan->cid.cid_num, "<unknown>"),
1092 S_OR(user->chan->cid.cid_name, "<no name>"),
1094 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1095 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1096 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1097 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1098 istalking(user->talking), hr, min, sec);
1100 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1102 S_OR(user->chan->cid.cid_num, ""),
1103 S_OR(user->chan->cid.cid_name, ""),
1105 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1106 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1107 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1108 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1109 user->talking, hr, min, sec);
1113 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1114 AST_LIST_UNLOCK(&confs);
1117 return CLI_SHOWUSAGE;
1119 ast_debug(1, "Cmdline: %s\n", cmdline);
1121 admin_exec(NULL, cmdline);
1126 static const char *sla_hold_str(unsigned int hold_access)
1128 const char *hold = "Unknown";
1130 switch (hold_access) {
1134 case SLA_HOLD_PRIVATE:
1143 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1145 const struct sla_trunk *trunk;
1149 e->command = "sla show trunks";
1151 "Usage: sla show trunks\n"
1152 " This will list all trunks defined in sla.conf\n";
1159 "=============================================================\n"
1160 "=== Configured SLA Trunks ===================================\n"
1161 "=============================================================\n"
1163 AST_RWLIST_RDLOCK(&sla_trunks);
1164 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1165 struct sla_station_ref *station_ref;
1166 char ring_timeout[16] = "(none)";
1167 if (trunk->ring_timeout)
1168 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1169 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1170 "=== Trunk Name: %s\n"
1171 "=== ==> Device: %s\n"
1172 "=== ==> AutoContext: %s\n"
1173 "=== ==> RingTimeout: %s\n"
1174 "=== ==> BargeAllowed: %s\n"
1175 "=== ==> HoldAccess: %s\n"
1176 "=== ==> Stations ...\n",
1177 trunk->name, trunk->device,
1178 S_OR(trunk->autocontext, "(none)"),
1180 trunk->barge_disabled ? "No" : "Yes",
1181 sla_hold_str(trunk->hold_access));
1182 AST_RWLIST_RDLOCK(&sla_stations);
1183 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1184 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1185 AST_RWLIST_UNLOCK(&sla_stations);
1186 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1188 AST_RWLIST_UNLOCK(&sla_trunks);
1189 ast_cli(a->fd, "=============================================================\n\n");
1194 static const char *trunkstate2str(enum sla_trunk_state state)
1196 #define S(e) case e: return # e;
1198 S(SLA_TRUNK_STATE_IDLE)
1199 S(SLA_TRUNK_STATE_RINGING)
1200 S(SLA_TRUNK_STATE_UP)
1201 S(SLA_TRUNK_STATE_ONHOLD)
1202 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1204 return "Uknown State";
1208 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1210 const struct sla_station *station;
1214 e->command = "sla show stations";
1216 "Usage: sla show stations\n"
1217 " This will list all stations defined in sla.conf\n";
1224 "=============================================================\n"
1225 "=== Configured SLA Stations =================================\n"
1226 "=============================================================\n"
1228 AST_RWLIST_RDLOCK(&sla_stations);
1229 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1230 struct sla_trunk_ref *trunk_ref;
1231 char ring_timeout[16] = "(none)";
1232 char ring_delay[16] = "(none)";
1233 if (station->ring_timeout) {
1234 snprintf(ring_timeout, sizeof(ring_timeout),
1235 "%u", station->ring_timeout);
1237 if (station->ring_delay) {
1238 snprintf(ring_delay, sizeof(ring_delay),
1239 "%u", station->ring_delay);
1241 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1242 "=== Station Name: %s\n"
1243 "=== ==> Device: %s\n"
1244 "=== ==> AutoContext: %s\n"
1245 "=== ==> RingTimeout: %s\n"
1246 "=== ==> RingDelay: %s\n"
1247 "=== ==> HoldAccess: %s\n"
1248 "=== ==> Trunks ...\n",
1249 station->name, station->device,
1250 S_OR(station->autocontext, "(none)"),
1251 ring_timeout, ring_delay,
1252 sla_hold_str(station->hold_access));
1253 AST_RWLIST_RDLOCK(&sla_trunks);
1254 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1255 if (trunk_ref->ring_timeout) {
1256 snprintf(ring_timeout, sizeof(ring_timeout),
1257 "%u", trunk_ref->ring_timeout);
1259 strcpy(ring_timeout, "(none)");
1260 if (trunk_ref->ring_delay) {
1261 snprintf(ring_delay, sizeof(ring_delay),
1262 "%u", trunk_ref->ring_delay);
1264 strcpy(ring_delay, "(none)");
1265 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1266 "=== ==> State: %s\n"
1267 "=== ==> RingTimeout: %s\n"
1268 "=== ==> RingDelay: %s\n",
1269 trunk_ref->trunk->name,
1270 trunkstate2str(trunk_ref->state),
1271 ring_timeout, ring_delay);
1273 AST_RWLIST_UNLOCK(&sla_trunks);
1274 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1277 AST_RWLIST_UNLOCK(&sla_stations);
1278 ast_cli(a->fd, "============================================================\n"
1284 static struct ast_cli_entry cli_meetme[] = {
1285 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1286 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1287 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1290 static void conf_flush(int fd, struct ast_channel *chan)
1294 /* read any frames that may be waiting on the channel
1298 struct ast_frame *f;
1300 /* when no frames are available, this will wait
1301 for 1 millisecond maximum
1303 while (ast_waitfor(chan, 1)) {
1307 else /* channel was hung up or something else happened */
1312 /* flush any data sitting in the pseudo channel */
1314 if (ioctl(fd, ZT_FLUSH, &x))
1315 ast_log(LOG_WARNING, "Error flushing channel\n");
1319 /* Remove the conference from the list and free it.
1320 We assume that this was called while holding conflock. */
1321 static int conf_free(struct ast_conference *conf)
1325 AST_LIST_REMOVE(&confs, conf, list);
1326 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1328 if (conf->recording == MEETME_RECORD_ACTIVE) {
1329 conf->recording = MEETME_RECORD_TERMINATE;
1330 AST_LIST_UNLOCK(&confs);
1333 AST_LIST_LOCK(&confs);
1334 if (conf->recording == MEETME_RECORD_OFF)
1336 AST_LIST_UNLOCK(&confs);
1340 for (x = 0; x < AST_FRAME_BITS; x++) {
1341 if (conf->transframe[x])
1342 ast_frfree(conf->transframe[x]);
1343 if (conf->transpath[x])
1344 ast_translator_free_path(conf->transpath[x]);
1346 if (conf->origframe)
1347 ast_frfree(conf->origframe);
1349 ast_hangup(conf->lchan);
1351 ast_hangup(conf->chan);
1355 ast_mutex_destroy(&conf->playlock);
1356 ast_mutex_destroy(&conf->listenlock);
1357 ast_mutex_destroy(&conf->recordthreadlock);
1363 static void conf_queue_dtmf(const struct ast_conference *conf,
1364 const struct ast_conf_user *sender, struct ast_frame *f)
1366 struct ast_conf_user *user;
1368 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1371 if (ast_write(user->chan, f) < 0)
1372 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1376 static void sla_queue_event_full(enum sla_event_type type,
1377 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1379 struct sla_event *event;
1381 if (!(event = ast_calloc(1, sizeof(*event))))
1385 event->trunk_ref = trunk_ref;
1386 event->station = station;
1389 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1393 ast_mutex_lock(&sla.lock);
1394 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1395 ast_cond_signal(&sla.cond);
1396 ast_mutex_unlock(&sla.lock);
1399 static void sla_queue_event_nolock(enum sla_event_type type)
1401 sla_queue_event_full(type, NULL, NULL, 0);
1404 static void sla_queue_event(enum sla_event_type type)
1406 sla_queue_event_full(type, NULL, NULL, 1);
1409 /*! \brief Queue a SLA event from the conference */
1410 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1411 struct ast_conference *conf)
1413 struct sla_station *station;
1414 struct sla_trunk_ref *trunk_ref = NULL;
1417 trunk_name = ast_strdupa(conf->confno);
1418 strsep(&trunk_name, "_");
1419 if (ast_strlen_zero(trunk_name)) {
1420 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1424 AST_RWLIST_RDLOCK(&sla_stations);
1425 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1426 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1427 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1433 AST_RWLIST_UNLOCK(&sla_stations);
1436 ast_debug(1, "Trunk not found for event!\n");
1440 sla_queue_event_full(type, trunk_ref, station, 1);
1443 /* Decrement reference counts, as incremented by find_conf() */
1444 static int dispose_conf(struct ast_conference *conf)
1449 AST_LIST_LOCK(&confs);
1450 if (ast_atomic_dec_and_test(&conf->refcount)) {
1451 /* Take the conference room number out of an inuse state */
1452 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1453 conf_map[confno_int] = 0;
1457 AST_LIST_UNLOCK(&confs);
1462 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1466 ast_channel_lock(chan);
1467 original_moh = ast_strdupa(chan->musicclass);
1468 ast_string_field_set(chan, musicclass, musicclass);
1469 ast_channel_unlock(chan);
1471 ast_moh_start(chan, original_moh, NULL);
1473 ast_channel_lock(chan);
1474 ast_string_field_set(chan, musicclass, original_moh);
1475 ast_channel_unlock(chan);
1478 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1480 struct ast_conf_user *user = NULL;
1481 struct ast_conf_user *usr = NULL;
1483 struct zt_confinfo ztc, ztc_empty;
1484 struct ast_frame *f;
1485 struct ast_channel *c;
1486 struct ast_frame fr;
1494 int musiconhold = 0;
1497 int currentmarked = 0;
1500 int menu_active = 0;
1501 int talkreq_manager = 0;
1502 int using_pseudo = 0;
1507 int announcement_played = 0;
1509 struct ast_dsp *dsp = NULL;
1510 struct ast_app *app;
1511 const char *agifile;
1512 const char *agifiledefault = "conf-background.agi";
1513 char meetmesecs[30] = "";
1514 char exitcontext[AST_MAX_CONTEXT] = "";
1515 char recordingtmp[AST_MAX_EXTENSION] = "";
1516 char members[10] = "";
1517 int dtmf, opt_waitmarked_timeout = 0;
1520 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1521 char *buf = __buf + AST_FRIENDLY_OFFSET;
1522 char *exitkeys = NULL;
1523 unsigned int calldurationlimit = 0;
1525 long play_warning = 0;
1526 long warning_freq = 0;
1527 const char *warning_sound = NULL;
1528 const char *end_sound = NULL;
1530 long time_left_ms = 0;
1531 struct timeval nexteventts = { 0, };
1533 int setusercount = 0;
1535 if (!(user = ast_calloc(1, sizeof(*user))))
1538 /* Possible timeout waiting for marked user */
1539 if ((confflags & CONFFLAG_WAITMARKED) &&
1540 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1541 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1542 (opt_waitmarked_timeout > 0)) {
1543 timeout = time(NULL) + opt_waitmarked_timeout;
1546 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1547 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1548 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1551 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1552 char *limit_str, *warning_str, *warnfreq_str;
1555 parse = optargs[OPT_ARG_DURATION_LIMIT];
1556 limit_str = strsep(&parse, ":");
1557 warning_str = strsep(&parse, ":");
1558 warnfreq_str = parse;
1560 timelimit = atol(limit_str);
1562 play_warning = atol(warning_str);
1564 warning_freq = atol(warnfreq_str);
1567 timelimit = play_warning = warning_freq = 0;
1568 warning_sound = NULL;
1569 } else if (play_warning > timelimit) {
1570 if (!warning_freq) {
1573 while (play_warning > timelimit)
1574 play_warning -= warning_freq;
1575 if (play_warning < 1)
1576 play_warning = warning_freq = 0;
1580 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE");
1581 warning_sound = var ? var : "timeleft";
1583 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE");
1584 end_sound = var ? var : NULL;
1586 /* undo effect of S(x) in case they are both used */
1587 calldurationlimit = 0;
1588 /* more efficient do it like S(x) does since no advanced opts */
1589 if (!play_warning && !end_sound && timelimit) {
1590 calldurationlimit = timelimit / 1000;
1591 timelimit = play_warning = warning_freq = 0;
1593 ast_debug(2, "Limit Data for this call:\n");
1594 ast_debug(2, "- timelimit = %ld\n", timelimit);
1595 ast_debug(2, "- play_warning = %ld\n", play_warning);
1596 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1597 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1598 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1603 if ((confflags & CONFFLAG_KEYEXIT)) {
1604 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1605 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1607 exitkeys = ast_strdupa("#"); /* Default */
1610 if (confflags & CONFFLAG_RECORDCONF) {
1611 if (!conf->recordingfilename) {
1612 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1613 if (!conf->recordingfilename) {
1614 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1615 conf->recordingfilename = ast_strdupa(recordingtmp);
1617 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1618 if (!conf->recordingformat) {
1619 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1620 conf->recordingformat = ast_strdupa(recordingtmp);
1622 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1623 conf->confno, conf->recordingfilename, conf->recordingformat);
1627 ast_mutex_lock(&conf->recordthreadlock);
1628 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1629 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1630 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1632 ztc.confno = conf->zapconf;
1633 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1634 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1635 ast_log(LOG_WARNING, "Error starting listen channel\n");
1636 ast_hangup(conf->lchan);
1639 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1642 ast_mutex_unlock(&conf->recordthreadlock);
1644 time(&user->jointime);
1646 user->timelimit = timelimit;
1647 user->play_warning = play_warning;
1648 user->warning_freq = warning_freq;
1649 user->warning_sound = warning_sound;
1650 user->end_sound = end_sound;
1652 if (calldurationlimit > 0) {
1653 time(&user->kicktime);
1654 user->kicktime = user->kicktime + calldurationlimit;
1657 if (ast_tvzero(user->start_time))
1658 user->start_time = ast_tvnow();
1659 time_left_ms = user->timelimit;
1661 if (user->timelimit) {
1662 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1663 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1666 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1667 /* Sorry, but this conference is locked! */
1668 if (!ast_streamfile(chan, "conf-locked", chan->language))
1669 ast_waitstream(chan, "");
1673 ast_mutex_lock(&conf->playlock);
1675 if (AST_LIST_EMPTY(&conf->userlist))
1678 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1680 if (rt_schedule && conf->maxusers)
1681 if (user->user_no > conf->maxusers) {
1682 /* Sorry, but this confernce has reached the participant limit! */
1683 if (!ast_streamfile(chan, "conf-full", chan->language))
1684 ast_waitstream(chan, "");
1688 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1691 user->userflags = confflags;
1692 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1695 ast_mutex_unlock(&conf->playlock);
1697 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1698 snprintf(user->namerecloc, sizeof(user->namerecloc),
1699 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1700 conf->confno, user->user_no);
1701 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1702 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1704 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1709 ast_mutex_lock(&conf->playlock);
1711 if (confflags & CONFFLAG_MARKEDUSER)
1712 conf->markedusers++;
1714 if (rt_log_members) {
1716 snprintf(members, sizeof(members), "%d", conf->users);
1717 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1721 /* This device changed state now - if this is the first user */
1722 if (conf->users == 1)
1723 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1725 ast_mutex_unlock(&conf->playlock);
1727 /* return the unique ID of the conference */
1728 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1730 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1731 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1732 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1733 else if (!ast_strlen_zero(chan->macrocontext))
1734 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1736 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1739 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1740 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1741 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1742 ast_waitstream(chan, "");
1743 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1744 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1745 ast_waitstream(chan, "");
1748 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1749 int keepplaying = 1;
1751 if (conf->users == 2) {
1752 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1753 res = ast_waitstream(chan, AST_DIGIT_ANY);
1754 ast_stopstream(chan);
1761 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1762 res = ast_waitstream(chan, AST_DIGIT_ANY);
1763 ast_stopstream(chan);
1770 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1776 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1777 res = ast_waitstream(chan, AST_DIGIT_ANY);
1778 ast_stopstream(chan);
1787 ast_indicate(chan, -1);
1789 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1790 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1794 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1795 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1799 retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1800 user->zapchannel = !retryzap;
1803 origfd = chan->fds[0];
1805 fd = open("/dev/zap/pseudo", O_RDWR);
1807 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1811 /* Make non-blocking */
1812 flags = fcntl(fd, F_GETFL);
1814 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1818 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1819 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1823 /* Setup buffering information */
1824 memset(&bi, 0, sizeof(bi));
1825 bi.bufsize = CONF_SIZE / 2;
1826 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1827 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1828 bi.numbufs = audio_buffers;
1829 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1830 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1835 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1836 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1842 /* XXX Make sure we're not running on a pseudo channel XXX */
1846 memset(&ztc, 0, sizeof(ztc));
1847 memset(&ztc_empty, 0, sizeof(ztc_empty));
1848 /* Check to see if we're in a conference... */
1850 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1851 ast_log(LOG_WARNING, "Error getting conference\n");
1856 /* Whoa, already in a conference... Retry... */
1858 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1863 memset(&ztc, 0, sizeof(ztc));
1864 /* Add us to the conference */
1866 ztc.confno = conf->zapconf;
1868 ast_mutex_lock(&conf->playlock);
1870 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1871 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1872 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1873 ast_waitstream(conf->chan, "");
1874 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1875 ast_waitstream(conf->chan, "");
1879 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
1880 ztc.confmode = ZT_CONF_CONF;
1881 else if (confflags & CONFFLAG_MONITOR)
1882 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1883 else if (confflags & CONFFLAG_TALKER)
1884 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1886 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1888 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1889 ast_log(LOG_WARNING, "Error setting conference\n");
1891 ast_mutex_unlock(&conf->playlock);
1894 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1897 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1902 "CallerIDnum: %s\r\n"
1903 "CallerIDname: %s\r\n",
1904 chan->name, chan->uniqueid, conf->confno,
1906 S_OR(user->chan->cid.cid_num, "<unknown>"),
1907 S_OR(user->chan->cid.cid_name, "<unknown>")
1912 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1914 if (!(confflags & CONFFLAG_QUIET))
1915 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1916 conf_play(chan, conf, ENTER);
1919 ast_mutex_unlock(&conf->playlock);
1921 conf_flush(fd, chan);
1923 if (confflags & CONFFLAG_AGI) {
1924 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1925 or use default filename of conf-background.agi */
1927 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1929 agifile = agifiledefault;
1931 if (user->zapchannel) {
1932 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1934 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1936 /* Find a pointer to the agi app and execute the script */
1937 app = pbx_findapp("agi");
1939 char *s = ast_strdupa(agifile);
1940 ret = pbx_exec(chan, app, s);
1942 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1945 if (user->zapchannel) {
1946 /* Remove CONFMUTE mode on Zap channel */
1948 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1951 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1952 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1954 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1956 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1957 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1961 int menu_was_active = 0;
1968 if (now.tv_sec % 60 == 0) {
1970 if (now.tv_sec > conf->endtime) {
1971 ast_verbose("Quitting time...\n");
1975 if (!announcement_played && conf->endalert) {
1976 if (now.tv_sec + conf->endalert > conf->endtime) {
1977 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1978 ast_waitstream(chan, "");
1979 ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
1980 if (!ast_streamfile(chan, "minutes", chan->language))
1981 ast_waitstream(chan, "");
1982 announcement_played = 1;
1993 if (user->kicktime && (user->kicktime <= now.tv_sec))
1997 if (user->timelimit) {
1998 int minutes = 0, seconds = 0, remain = 0;
2000 to = ast_tvdiff_ms(nexteventts, now);
2003 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2004 if (time_left_ms < to)
2007 if (time_left_ms <= 0) {
2008 if (user->end_sound) {
2009 res = ast_streamfile(chan, user->end_sound, chan->language);
2010 res = ast_waitstream(chan, "");
2016 if (time_left_ms >= 5000) {
2018 remain = (time_left_ms + 500) / 1000;
2019 if (remain / 60 >= 1) {
2020 minutes = remain / 60;
2021 seconds = remain % 60;
2026 /* force the time left to round up if appropriate */
2027 if (user->warning_sound && user->play_warning) {
2028 if (!strcmp(user->warning_sound, "timeleft")) {
2030 res = ast_streamfile(chan, "vm-youhave", chan->language);
2031 res = ast_waitstream(chan, "");
2033 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2034 res = ast_streamfile(chan, "queue-minutes", chan->language);
2035 res = ast_waitstream(chan, "");
2038 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2039 res = ast_streamfile(chan, "queue-seconds", chan->language);
2040 res = ast_waitstream(chan, "");
2043 res = ast_streamfile(chan, user->warning_sound, chan->language);
2044 res = ast_waitstream(chan, "");
2048 if (user->warning_freq)
2049 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2051 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2056 if (timeout && now.tv_sec >= timeout)
2059 /* if we have just exited from the menu, and the user had a channel-driver
2060 volume adjustment, restore it
2062 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2063 set_talk_volume(user, user->listen.desired);
2065 menu_was_active = menu_active;
2067 currentmarked = conf->markedusers;
2068 if (!(confflags & CONFFLAG_QUIET) &&
2069 (confflags & CONFFLAG_MARKEDUSER) &&
2070 (confflags & CONFFLAG_WAITMARKED) &&
2072 if (currentmarked == 1 && conf->users > 1) {
2073 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2074 if (conf->users - 1 == 1) {
2075 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2076 ast_waitstream(chan, "");
2078 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2079 ast_waitstream(chan, "");
2082 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2083 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2084 ast_waitstream(chan, "");
2087 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2089 /* Update the struct with the actual confflags */
2090 user->userflags = confflags;
2092 if (confflags & CONFFLAG_WAITMARKED) {
2093 if (currentmarked == 0) {
2094 if (lastmarked != 0) {
2095 if (!(confflags & CONFFLAG_QUIET))
2096 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2097 ast_waitstream(chan, "");
2098 if (confflags & CONFFLAG_MARKEDEXIT) {
2099 if (confflags & CONFFLAG_KICK_CONTINUE)
2103 ztc.confmode = ZT_CONF_CONF;
2104 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2105 ast_log(LOG_WARNING, "Error setting conference\n");
2111 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2112 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2115 } else if (currentmarked >= 1 && lastmarked == 0) {
2116 /* Marked user entered, so cancel timeout */
2118 if (confflags & CONFFLAG_MONITOR)
2119 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
2120 else if (confflags & CONFFLAG_TALKER)
2121 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
2123 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
2124 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2125 ast_log(LOG_WARNING, "Error setting conference\n");
2129 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2133 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2134 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2135 ast_waitstream(chan, "");
2136 conf_play(chan, conf, ENTER);
2141 /* trying to add moh for single person conf */
2142 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2143 if (conf->users == 1) {
2145 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2156 /* Leave if the last marked user left */
2157 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2158 if (confflags & CONFFLAG_KICK_CONTINUE)
2165 /* Check if my modes have changed */
2167 /* If I should be muted but am still talker, mute me */
2168 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
2169 ztc.confmode ^= ZT_CONF_TALKER;
2170 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2171 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2176 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2182 chan->name, chan->uniqueid, conf->confno, user->user_no);
2185 /* If I should be un-muted but am not talker, un-mute me */
2186 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
2187 ztc.confmode |= ZT_CONF_TALKER;
2188 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2189 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2194 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2200 chan->name, chan->uniqueid, conf->confno, user->user_no);
2203 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2204 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2205 talkreq_manager = 1;
2207 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2213 chan->name, chan->uniqueid, conf->confno, user->user_no);
2217 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2218 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2219 talkreq_manager = 0;
2220 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2226 chan->name, chan->uniqueid, conf->confno, user->user_no);
2229 /* If I have been kicked, exit the conference */
2230 if (user->adminflags & ADMINFLAG_KICKME) {
2231 /* You have been kicked. */
2232 if (!(confflags & CONFFLAG_QUIET) &&
2233 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2234 ast_waitstream(chan, "");
2240 /* Perform an extra hangup check just in case */
2241 if (ast_check_hangup(chan))
2245 if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
2247 /* Kill old pseudo */
2251 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2252 retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
2253 user->zapchannel = !retryzap;
2256 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2257 f = ast_read_noaudio(c);
2262 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2263 if (user->talk.actual)
2264 ast_frame_adjust_volume(f, user->talk.actual);
2266 if (!(confflags & CONFFLAG_MONITOR)) {
2269 if (user->talking == -1)
2272 res = ast_dsp_silence(dsp, f, &totalsilence);
2273 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2275 if (confflags & CONFFLAG_MONITORTALKER)
2276 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2282 chan->name, chan->uniqueid, conf->confno, user->user_no);
2284 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2286 if (confflags & CONFFLAG_MONITORTALKER)
2287 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2293 chan->name, chan->uniqueid, conf->confno, user->user_no);
2297 /* Absolutely do _not_ use careful_write here...
2298 it is important that we read data from the channel
2299 as fast as it arrives, and feed it into the conference.
2300 The buffering in the pseudo channel will take care of any
2301 timing differences, unless they are so drastic as to lose
2302 audio frames (in which case carefully writing would only
2303 have delayed the audio even further).
2305 /* As it turns out, we do want to use careful write. We just
2306 don't want to block, but we do want to at least *try*
2307 to write out all the samples.
2310 careful_write(fd, f->data, f->datalen, 0);
2312 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2315 if (confflags & CONFFLAG_PASS_DTMF)
2316 conf_queue_dtmf(conf, user, f);
2318 tmp[0] = f->subclass;
2320 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2321 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2326 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2328 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2331 exitkey[0] = f->subclass;
2334 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2336 if (confflags & CONFFLAG_PASS_DTMF)
2337 conf_queue_dtmf(conf, user, f);
2341 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2342 if (confflags & CONFFLAG_PASS_DTMF)
2343 conf_queue_dtmf(conf, user, f);
2344 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2345 ast_log(LOG_WARNING, "Error setting conference\n");
2351 /* if we are entering the menu, and the user has a channel-driver
2352 volume adjustment, clear it
2354 if (!menu_active && user->talk.desired && !user->talk.actual)
2355 set_talk_volume(user, 0);
2360 if ((confflags & CONFFLAG_ADMIN)) {
2364 /* Record this sound! */
2365 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2366 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2367 ast_stopstream(chan);
2374 case '1': /* Un/Mute */
2377 /* for admin, change both admin and use flags */
2378 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2379 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2381 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2383 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2384 if (!ast_streamfile(chan, "conf-muted", chan->language))
2385 ast_waitstream(chan, "");
2387 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2388 ast_waitstream(chan, "");
2391 case '2': /* Un/Lock the Conference */
2395 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2396 ast_waitstream(chan, "");
2399 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2400 ast_waitstream(chan, "");
2403 case '3': /* Eject last user */
2405 usr = AST_LIST_LAST(&conf->userlist);
2406 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2407 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2408 ast_waitstream(chan, "");
2410 usr->adminflags |= ADMINFLAG_KICKME;
2411 ast_stopstream(chan);
2414 tweak_listen_volume(user, VOL_DOWN);
2417 tweak_listen_volume(user, VOL_UP);
2420 tweak_talk_volume(user, VOL_DOWN);
2426 tweak_talk_volume(user, VOL_UP);
2430 /* Play an error message! */
2431 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2432 ast_waitstream(chan, "");
2440 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2441 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2442 ast_stopstream(chan);
2449 case '1': /* Un/Mute */
2452 /* user can only toggle the self-muted state */
2453 user->adminflags ^= ADMINFLAG_SELFMUTED;
2455 /* they can't override the admin mute state */
2456 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2457 if (!ast_streamfile(chan, "conf-muted", chan->language))
2458 ast_waitstream(chan, "");
2460 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2461 ast_waitstream(chan, "");
2466 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2467 user->adminflags |= ADMINFLAG_T_REQUEST;
2469 if (user->adminflags & ADMINFLAG_T_REQUEST)
2470 if (!ast_streamfile(chan, "beep", chan->language))
2471 ast_waitstream(chan, "");
2474 tweak_listen_volume(user, VOL_DOWN);
2477 tweak_listen_volume(user, VOL_UP);
2480 tweak_talk_volume(user, VOL_DOWN);
2486 tweak_talk_volume(user, VOL_UP);
2490 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2491 ast_waitstream(chan, "");
2497 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2499 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2500 ast_log(LOG_WARNING, "Error setting conference\n");
2506 conf_flush(fd, chan);
2507 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2508 && confflags & CONFFLAG_PASS_DTMF) {
2509 conf_queue_dtmf(conf, user, f);
2510 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2511 switch (f->subclass) {
2512 case AST_CONTROL_HOLD:
2513 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2518 } else if (f->frametype == AST_FRAME_NULL) {
2519 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2522 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2523 chan->name, f->frametype, f->subclass);
2526 } else if (outfd > -1) {
2527 res = read(outfd, buf, CONF_SIZE);
2529 memset(&fr, 0, sizeof(fr));
2530 fr.frametype = AST_FRAME_VOICE;
2531 fr.subclass = AST_FORMAT_SLINEAR;
2533 fr.samples = res / 2;
2535 fr.offset = AST_FRIENDLY_OFFSET;
2536 if (!user->listen.actual &&
2537 ((confflags & CONFFLAG_MONITOR) ||
2538 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2539 (!user->talking)) ) {
2541 for (index = 0; index < AST_FRAME_BITS; index++)
2542 if (chan->rawwriteformat & (1 << index))
2544 if (index >= AST_FRAME_BITS)
2545 goto bailoutandtrynormal;
2546 ast_mutex_lock(&conf->listenlock);
2547 if (!conf->transframe[index]) {
2548 if (conf->origframe) {
2549 if (!conf->transpath[index])
2550 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2551 if (conf->transpath[index]) {
2552 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2553 if (!conf->transframe[index])
2554 conf->transframe[index] = &ast_null_frame;
2558 if (conf->transframe[index]) {
2559 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2560 if (ast_write(chan, conf->transframe[index]))
2561 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2564 ast_mutex_unlock(&conf->listenlock);
2565 goto bailoutandtrynormal;
2567 ast_mutex_unlock(&conf->listenlock);
2569 bailoutandtrynormal:
2570 if (user->listen.actual)
2571 ast_frame_adjust_volume(&fr, user->listen.actual);
2572 if (ast_write(chan, &fr) < 0) {
2573 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2577 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2579 lastmarked = currentmarked;
2589 /* Take out of conference */
2593 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2594 ast_log(LOG_WARNING, "Error setting conference\n");
2598 reset_volumes(user);
2600 AST_LIST_LOCK(&confs);
2601 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2602 conf_play(chan, conf, LEAVE);
2604 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2605 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2606 if ((conf->chan) && (conf->users > 1)) {
2607 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2608 ast_waitstream(conf->chan, "");
2609 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2610 ast_waitstream(conf->chan, "");
2612 ast_filedelete(user->namerecloc, NULL);
2615 AST_LIST_UNLOCK(&confs);
2618 AST_LIST_LOCK(&confs);
2623 if (user->user_no) { /* Only cleanup users who really joined! */
2625 hr = (now.tv_sec - user->jointime) / 3600;
2626 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2627 sec = (now.tv_sec - user->jointime) % 60;
2630 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2635 "CallerIDNum: %s\r\n"
2636 "CallerIDName: %s\r\n"
2637 "Duration: %ld\r\n",
2638 chan->name, chan->uniqueid, conf->confno,
2640 S_OR(user->chan->cid.cid_num, "<unknown>"),
2641 S_OR(user->chan->cid.cid_name, "<unknown>"),
2642 (long)(now.tv_sec - user->jointime));
2647 if (rt_log_members) {
2649 snprintf(members, sizeof(members), "%d", conf->users);
2650 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2652 if (confflags & CONFFLAG_MARKEDUSER)
2653 conf->markedusers--;
2655 /* Remove ourselves from the list */
2656 AST_LIST_REMOVE(&conf->userlist, user, list);
2658 /* Change any states */
2660 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2662 /* Return the number of seconds the user was in the conf */
2663 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2664 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2667 AST_LIST_UNLOCK(&confs);
2672 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2673 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2674 char *optargs[], int *too_early)
2676 struct ast_variable *var;
2677 struct ast_conference *cnf;
2681 /* Check first in the conference list */
2682 AST_LIST_LOCK(&confs);
2683 AST_LIST_TRAVERSE(&confs, cnf, list) {
2684 if (!strcmp(confno, cnf->confno))
2688 cnf->refcount += refcount;
2690 AST_LIST_UNLOCK(&confs);
2693 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2696 char currenttime[19] = "";
2697 char eatime[19] = "";
2698 char useropts[32] = "";
2699 char adminopts[32] = "";
2700 struct ast_tm tm, etm;
2701 struct timeval starttime = { .tv_sec = 0 }, endtime = { .tv_sec = 0 };
2707 now.tv_sec += fuzzystart;
2709 ast_localtime(&now, &tm, NULL);
2710 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2713 now.tv_sec += earlyalert;
2714 ast_localtime(&now, &etm, NULL);
2715 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2717 ast_copy_string(eatime, currenttime, sizeof(eatime));
2720 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2722 var = ast_load_realtime("meetme", "confno",
2723 confno, "starttime <= ", eatime, "endtime >= ",
2726 var = ast_load_realtime("meetme", "confno", confno, NULL);
2732 if (!strcasecmp(var->name, "pin")) {
2733 pin = ast_strdupa(var->value);
2734 } else if (!strcasecmp(var->name, "adminpin")) {
2735 pinadmin = ast_strdupa(var->value);
2736 } else if (!strcasecmp(var->name, "opts")) {
2737 ast_copy_string(useropts, var->value, sizeof(useropts));
2738 } else if (!strcasecmp(var->name, "maxusers")) {
2739 maxusers = atoi(var->value);
2740 } else if (!strcasecmp(var->name, "adminopts")) {
2741 ast_copy_string(adminopts, var->value, sizeof(adminopts));
2742 } else if (!strcasecmp(var->name, "endtime")) {
2747 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
2748 endtime = ast_mktime(&t.atm, NULL);
2749 } else if (!strcasecmp(var->name, "starttime")) {
2754 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
2755 starttime = ast_mktime(&t.atm, NULL);
2760 ast_variables_destroy(var);
2765 if (now.tv_sec + fuzzystart < starttime.tv_sec) {
2766 /* Announce that the caller is early and exit */
2767 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2768 ast_waitstream(chan, "");
2774 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2777 cnf->maxusers = maxusers;
2778 cnf->endalert = endalert;
2779 cnf->endtime = endtime.tv_sec;
2784 if (confflags && !cnf->chan &&
2785 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2786 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2787 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2788 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2791 if (confflags && !cnf->chan &&
2792 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2793 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2794 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2802 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2803 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2805 struct ast_config *cfg;
2806 struct ast_variable *var;
2807 struct ast_flags config_flags = { 0 };
2808 struct ast_conference *cnf;
2810 AST_DECLARE_APP_ARGS(args,
2811 AST_APP_ARG(confno);
2813 AST_APP_ARG(pinadmin);
2816 /* Check first in the conference list */
2817 ast_debug(1, "The requested confno is '%s'?\n", confno);
2818 AST_LIST_LOCK(&confs);
2819 AST_LIST_TRAVERSE(&confs, cnf, list) {
2820 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
2821 if (!strcmp(confno, cnf->confno))
2825 cnf->refcount += refcount;
2827 AST_LIST_UNLOCK(&confs);
2831 /* No need to parse meetme.conf */
2832 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2834 if (dynamic_pin[0] == 'q') {
2835 /* Query the user to enter a PIN */
2836 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2839 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2841 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2844 /* Check the config */
2845 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2847 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2850 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2851 if (strcasecmp(var->name, "conf"))
2854 if (!(parse = ast_strdupa(var->value)))
2857 AST_STANDARD_APP_ARGS(args, parse);
2858 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2859 if (!strcasecmp(args.confno, confno)) {
2860 /* Bingo it's a valid conference */
2861 cnf = build_conf(args.confno,
2863 S_OR(args.pinadmin, ""),
2864 make, dynamic, refcount, chan);
2869 ast_debug(1, "%s isn't a valid conference\n", confno);
2871 ast_config_destroy(cfg);
2873 } else if (dynamic_pin) {
2874 /* Correct for the user selecting 'D' instead of 'd' to have
2875 someone join into a conference that has already been created
2877 if (dynamic_pin[0] == 'q')
2878 dynamic_pin[0] = '\0';
2882 if (confflags && !cnf->chan &&
2883 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2884 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2885 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2886 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2889 if (confflags && !cnf->chan &&
2890 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2891 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2892 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2899 /*! \brief The MeetmeCount application */
2900 static int count_exec(struct ast_channel *chan, void *data)
2903 struct ast_conference *conf;
2907 AST_DECLARE_APP_ARGS(args,
2908 AST_APP_ARG(confno);
2909 AST_APP_ARG(varname);
2912 if (ast_strlen_zero(data)) {
2913 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2917 if (!(localdata = ast_strdupa(data)))
2920 AST_STANDARD_APP_ARGS(args, localdata);
2922 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2925 count = conf->users;
2931 if (!ast_strlen_zero(args.varname)) {
2932 /* have var so load it and exit */
2933 snprintf(val, sizeof(val), "%d", count);
2934 pbx_builtin_setvar_helper(chan, args.varname, val);
2936 if (chan->_state != AST_STATE_UP)
2938 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2944 /*! \brief The meetme() application */
2945 static int conf_exec(struct ast_channel *chan, void *data)
2948 char confno[MAX_CONFNUM] = "";
2951 struct ast_conference *cnf = NULL;
2952 struct ast_flags confflags = {0}, config_flags = { 0 };
2954 int empty = 0, empty_no_pin = 0;
2955 int always_prompt = 0;
2956 char *notdata, *info, the_pin[MAX_PIN] = "";
2957 AST_DECLARE_APP_ARGS(args,
2958 AST_APP_ARG(confno);
2959 AST_APP_ARG(options);
2962 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2964 if (ast_strlen_zero(data)) {
2971 if (chan->_state != AST_STATE_UP)
2974 info = ast_strdupa(notdata);
2976 AST_STANDARD_APP_ARGS(args, info);
2979 ast_copy_string(confno, args.confno, sizeof(confno));
2980 if (ast_strlen_zero(confno)) {
2986 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2989 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2990 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2991 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2992 strcpy(the_pin, "q");
2994 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2995 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2996 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
3004 struct ast_config *cfg;
3005 struct ast_variable *var;
3008 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
3009 if ((empty_no_pin) || (!dynamic)) {
3010 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
3012 var = ast_variable_browse(cfg, "rooms");
3014 if (!strcasecmp(var->name, "conf")) {
3015 char *stringp = ast_strdupa(var->value);
3017 char *confno_tmp = strsep(&stringp, "|,");
3020 /* For static: run through the list and see if this conference is empty */
3021 AST_LIST_LOCK(&confs);
3022 AST_LIST_TRAVERSE(&confs, cnf, list) {
3023 if (!strcmp(confno_tmp, cnf->confno)) {
3024 /* The conference exists, therefore it's not empty */
3029 AST_LIST_UNLOCK(&confs);
3031 /* At this point, we have a confno_tmp (static conference) that is empty */
3032 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
3033 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
3034 * Case 2: empty_no_pin and pin is blank (but not NULL)
3035 * Case 3: not empty_no_pin
3037 ast_copy_string(confno, confno_tmp, sizeof(confno));
3039 /* XXX the map is not complete (but we do have a confno) */
3047 ast_config_destroy(cfg);