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 char endtime[19]; /*!< 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") && ( 0 == state ) )
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 *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
959 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
960 char cmdline[1024] = "";
964 e->command = "meetme";
966 "Usage: meetme concise|(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, 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, 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 = ( 4 == a->argc && ( !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;
1524 unsigned int calldurationlimit = 0;
1526 long play_warning = 0;
1527 long warning_freq = 0;
1528 const char *warning_sound = NULL;
1529 const char *end_sound = NULL;
1531 long time_left_ms = 0;
1532 struct timeval nexteventts = { 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 if (option_verbose > 2)
1549 ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit);
1552 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1553 char *limit_str, *warning_str, *warnfreq_str;
1556 parse = optargs[OPT_ARG_DURATION_LIMIT];
1557 limit_str = strsep(&parse, ":");
1558 warning_str = strsep(&parse, ":");
1559 warnfreq_str = parse;
1561 timelimit = atol(limit_str);
1563 play_warning = atol(warning_str);
1565 warning_freq = atol(warnfreq_str);
1568 timelimit = play_warning = warning_freq = 0;
1569 warning_sound = NULL;
1570 } else if (play_warning > timelimit) {
1571 if (!warning_freq) {
1574 while (play_warning > timelimit)
1575 play_warning -= warning_freq;
1576 if (play_warning < 1)
1577 play_warning = warning_freq = 0;
1581 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_WARNING_FILE");
1582 warning_sound = var ? var : "timeleft";
1584 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_TIMEOUT_FILE");
1585 end_sound = var ? var : NULL;
1587 /* undo effect of S(x) in case they are both used */
1588 calldurationlimit = 0;
1589 /* more efficient do it like S(x) does since no advanced opts */
1590 if (!play_warning && !end_sound && timelimit) {
1591 calldurationlimit = timelimit / 1000;
1592 timelimit = play_warning = warning_freq = 0;
1594 ast_debug(2, "Limit Data for this call:\n");
1595 ast_debug(2, "- timelimit = %ld\n", timelimit);
1596 ast_debug(2, "- play_warning = %ld\n", play_warning);
1597 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1598 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1599 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1604 if ((confflags & CONFFLAG_KEYEXIT)) {
1605 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1606 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1608 exitkeys = ast_strdupa("#"); /* Default */
1611 if (confflags & CONFFLAG_RECORDCONF) {
1612 if (!conf->recordingfilename) {
1613 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1614 if (!conf->recordingfilename) {
1615 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1616 conf->recordingfilename = ast_strdupa(recordingtmp);
1618 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1619 if (!conf->recordingformat) {
1620 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1621 conf->recordingformat = ast_strdupa(recordingtmp);
1623 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1624 conf->confno, conf->recordingfilename, conf->recordingformat);
1628 ast_mutex_lock(&conf->recordthreadlock);
1629 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1630 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1631 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1633 ztc.confno = conf->zapconf;
1634 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1635 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1636 ast_log(LOG_WARNING, "Error starting listen channel\n");
1637 ast_hangup(conf->lchan);
1640 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1643 ast_mutex_unlock(&conf->recordthreadlock);
1645 time(&user->jointime);
1647 user->timelimit = timelimit;
1648 user->play_warning = play_warning;
1649 user->warning_freq = warning_freq;
1650 user->warning_sound = warning_sound;
1651 user->end_sound = end_sound;
1653 if (calldurationlimit > 0) {
1654 time(&user->kicktime);
1655 user->kicktime = user->kicktime + calldurationlimit;
1658 if (ast_tvzero(user->start_time))
1659 user->start_time = ast_tvnow();
1660 time_left_ms = user->timelimit;
1662 if (user->timelimit) {
1663 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1664 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1667 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1668 /* Sorry, but this conference is locked! */
1669 if (!ast_streamfile(chan, "conf-locked", chan->language))
1670 ast_waitstream(chan, "");
1674 if (confflags & CONFFLAG_MARKEDUSER)
1675 conf->markedusers++;
1677 ast_mutex_lock(&conf->playlock);
1679 if (AST_LIST_EMPTY(&conf->userlist))
1682 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1684 if (rt_schedule && conf->maxusers)
1685 if (user->user_no > conf->maxusers){
1686 /* Sorry, but this confernce has reached the participant limit! */
1687 if (!ast_streamfile(chan, "conf-full", chan->language))
1688 ast_waitstream(chan, "");
1692 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1695 user->userflags = confflags;
1696 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1699 if (rt_log_members) {
1701 snprintf(members, sizeof(members), "%d", conf->users);
1702 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1704 /* This device changed state now - if this is the first user */
1705 if (conf->users == 1)
1706 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1708 ast_mutex_unlock(&conf->playlock);
1710 /* return the unique ID of the conference */
1711 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1713 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1714 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1715 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1716 else if (!ast_strlen_zero(chan->macrocontext))
1717 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1719 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1722 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1723 snprintf(user->namerecloc, sizeof(user->namerecloc),
1724 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1725 conf->confno, user->user_no);
1726 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1727 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1729 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1734 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1735 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1736 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1737 ast_waitstream(chan, "");
1738 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1739 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1740 ast_waitstream(chan, "");
1743 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1744 int keepplaying = 1;
1746 if (conf->users == 2) {
1747 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1748 res = ast_waitstream(chan, AST_DIGIT_ANY);
1749 ast_stopstream(chan);
1756 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1757 res = ast_waitstream(chan, AST_DIGIT_ANY);
1758 ast_stopstream(chan);
1765 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1771 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1772 res = ast_waitstream(chan, AST_DIGIT_ANY);
1773 ast_stopstream(chan);
1782 ast_indicate(chan, -1);
1784 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1785 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1789 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1790 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1794 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
1795 user->zapchannel = !retryzap;
1798 origfd = chan->fds[0];
1800 fd = open("/dev/zap/pseudo", O_RDWR);
1802 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1806 /* Make non-blocking */
1807 flags = fcntl(fd, F_GETFL);
1809 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1813 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1814 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1818 /* Setup buffering information */
1819 memset(&bi, 0, sizeof(bi));
1820 bi.bufsize = CONF_SIZE/2;
1821 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1822 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1823 bi.numbufs = audio_buffers;
1824 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1825 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1830 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1831 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1837 /* XXX Make sure we're not running on a pseudo channel XXX */
1841 memset(&ztc, 0, sizeof(ztc));
1842 memset(&ztc_empty, 0, sizeof(ztc_empty));
1843 /* Check to see if we're in a conference... */
1845 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1846 ast_log(LOG_WARNING, "Error getting conference\n");
1851 /* Whoa, already in a conference... Retry... */
1853 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1858 memset(&ztc, 0, sizeof(ztc));
1859 /* Add us to the conference */
1861 ztc.confno = conf->zapconf;
1863 ast_mutex_lock(&conf->playlock);
1865 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1866 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1867 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1868 ast_waitstream(conf->chan, "");
1869 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1870 ast_waitstream(conf->chan, "");
1874 if (confflags & CONFFLAG_MONITOR)
1875 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1876 else if (confflags & CONFFLAG_TALKER)
1877 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1879 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1881 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1882 ast_log(LOG_WARNING, "Error setting conference\n");
1884 ast_mutex_unlock(&conf->playlock);
1887 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1890 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1895 chan->name, chan->uniqueid, conf->confno, user->user_no);
1899 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1901 if (!(confflags & CONFFLAG_QUIET))
1902 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1903 conf_play(chan, conf, ENTER);
1906 ast_mutex_unlock(&conf->playlock);
1908 conf_flush(fd, chan);
1910 if (confflags & CONFFLAG_AGI) {
1911 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1912 or use default filename of conf-background.agi */
1914 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1916 agifile = agifiledefault;
1918 if (user->zapchannel) {
1919 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1921 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1923 /* Find a pointer to the agi app and execute the script */
1924 app = pbx_findapp("agi");
1926 char *s = ast_strdupa(agifile);
1927 ret = pbx_exec(chan, app, s);
1929 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1932 if (user->zapchannel) {
1933 /* Remove CONFMUTE mode on Zap channel */
1935 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1938 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1939 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1941 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1943 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1944 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1948 int menu_was_active = 0;
1954 char currenttime[32];
1958 if (now.tv_sec % 60 == 0) {
1960 ast_localtime(&now, &tm, NULL);
1961 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1962 if (strcmp(currenttime, conf->endtime) > 0){
1963 ast_verbose("Quitting time...\n");
1967 if (!announcement_played && conf->endalert) {
1968 now.tv_sec += conf->endalert;
1969 ast_localtime(&now, &tm, NULL);
1970 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1971 if (strcmp(currenttime, conf->endtime) > 0){
1972 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1973 ast_waitstream(chan, "");
1974 ast_say_digits(chan, conf->endalert / 60, "", chan->language);
1975 if (!ast_streamfile(chan, "minutes", chan->language))
1976 ast_waitstream(chan, "");
1977 announcement_played = 1;
1989 time(&myt); /* get current time */
1990 if (user->kicktime && (user->kicktime < myt))
1994 if (user->timelimit) {
1996 int minutes = 0, seconds = 0, remain = 0;
1999 to = ast_tvdiff_ms(nexteventts, now);
2002 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2003 if (time_left_ms < to)
2006 if (time_left_ms <= 0) {
2007 if (user->end_sound){
2008 res = ast_streamfile(chan, user->end_sound, chan->language);
2009 res = ast_waitstream(chan, "");
2015 if (time_left_ms >= 5000) {
2017 remain = (time_left_ms + 500) / 1000;
2018 if (remain / 60 >= 1) {
2019 minutes = remain / 60;
2020 seconds = remain % 60;
2025 /* force the time left to round up if appropriate */
2026 if (user->warning_sound && user->play_warning){
2027 if (!strcmp(user->warning_sound,"timeleft")) {
2029 res = ast_streamfile(chan, "vm-youhave", chan->language);
2030 res = ast_waitstream(chan, "");
2032 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2033 res = ast_streamfile(chan, "queue-minutes", chan->language);
2034 res = ast_waitstream(chan, "");
2037 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2038 res = ast_streamfile(chan, "queue-seconds", chan->language);
2039 res = ast_waitstream(chan, "");
2042 res = ast_streamfile(chan, user->warning_sound, chan->language);
2043 res = ast_waitstream(chan, "");
2047 if (user->warning_freq)
2048 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2050 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2054 if (timeout && time(NULL) >= timeout)
2057 /* if we have just exited from the menu, and the user had a channel-driver
2058 volume adjustment, restore it
2060 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2061 set_talk_volume(user, user->listen.desired);
2063 menu_was_active = menu_active;
2065 currentmarked = conf->markedusers;
2066 if (!(confflags & CONFFLAG_QUIET) &&
2067 (confflags & CONFFLAG_MARKEDUSER) &&
2068 (confflags & CONFFLAG_WAITMARKED) &&
2070 if (currentmarked == 1 && conf->users > 1) {
2071 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2072 if (conf->users - 1 == 1) {
2073 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2074 ast_waitstream(chan, "");
2076 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2077 ast_waitstream(chan, "");
2080 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2081 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2082 ast_waitstream(chan, "");
2085 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2088 /* Update the struct with the actual confflags */
2089 user->userflags = confflags;
2091 if (confflags & CONFFLAG_WAITMARKED) {
2092 if(currentmarked == 0) {
2093 if (lastmarked != 0) {
2094 if (!(confflags & CONFFLAG_QUIET))
2095 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2096 ast_waitstream(chan, "");
2097 if (confflags & CONFFLAG_MARKEDEXIT) {
2098 if (confflags & CONFFLAG_KICK_CONTINUE)
2102 ztc.confmode = ZT_CONF_CONF;
2103 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2104 ast_log(LOG_WARNING, "Error setting conference\n");
2110 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2111 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2114 } else if(currentmarked >= 1 && lastmarked == 0) {
2115 /* Marked user entered, so cancel timeout */
2117 if (confflags & CONFFLAG_MONITOR)
2118 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
2119 else if (confflags & CONFFLAG_TALKER)
2120 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
2122 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
2123 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2124 ast_log(LOG_WARNING, "Error setting conference\n");
2128 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2132 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2133 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2134 ast_waitstream(chan, "");
2135 conf_play(chan, conf, ENTER);
2140 /* trying to add moh for single person conf */
2141 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2142 if (conf->users == 1) {
2144 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2155 /* Leave if the last marked user left */
2156 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2157 if (confflags & CONFFLAG_KICK_CONTINUE)
2164 /* Check if my modes have changed */
2166 /* If I should be muted but am still talker, mute me */
2167 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
2168 ztc.confmode ^= ZT_CONF_TALKER;
2169 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2170 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2175 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2181 chan->name, chan->uniqueid, conf->confno, user->user_no);
2184 /* If I should be un-muted but am not talker, un-mute me */
2185 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
2186 ztc.confmode |= ZT_CONF_TALKER;
2187 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2188 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2193 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2199 chan->name, chan->uniqueid, conf->confno, user->user_no);
2202 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2203 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2204 talkreq_manager = 1;
2206 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2212 chan->name, chan->uniqueid, conf->confno, user->user_no);
2216 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2217 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2218 talkreq_manager = 0;
2219 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2225 chan->name, chan->uniqueid, conf->confno, user->user_no);
2228 /* If I have been kicked, exit the conference */
2229 if (user->adminflags & ADMINFLAG_KICKME) {
2230 //You have been kicked.
2231 if (!(confflags & CONFFLAG_QUIET) &&
2232 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2233 ast_waitstream(chan, "");
2239 /* Perform an extra hangup check just in case */
2240 if (ast_check_hangup(chan))
2244 if (c->fds[0] != origfd || (user->zapchannel && c->audiohooks)) {
2246 /* Kill old pseudo */
2250 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2251 retryzap = (strcasecmp(c->tech->type, "Zap") || c->audiohooks ? 1 : 0);
2252 user->zapchannel = !retryzap;
2255 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2256 f = ast_read_noaudio(c);
2261 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2262 if (user->talk.actual)
2263 ast_frame_adjust_volume(f, user->talk.actual);
2265 if (!(confflags & CONFFLAG_MONITOR)) {
2268 if (user->talking == -1)
2271 res = ast_dsp_silence(dsp, f, &totalsilence);
2272 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2274 if (confflags & CONFFLAG_MONITORTALKER)
2275 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2281 chan->name, chan->uniqueid, conf->confno, user->user_no);
2283 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2285 if (confflags & CONFFLAG_MONITORTALKER)
2286 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2292 chan->name, chan->uniqueid, conf->confno, user->user_no);
2296 /* Absolutely do _not_ use careful_write here...
2297 it is important that we read data from the channel
2298 as fast as it arrives, and feed it into the conference.
2299 The buffering in the pseudo channel will take care of any
2300 timing differences, unless they are so drastic as to lose
2301 audio frames (in which case carefully writing would only
2302 have delayed the audio even further).
2304 /* As it turns out, we do want to use careful write. We just
2305 don't want to block, but we do want to at least *try*
2306 to write out all the samples.
2309 careful_write(fd, f->data, f->datalen, 0);
2311 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2314 if (confflags & CONFFLAG_PASS_DTMF)
2315 conf_queue_dtmf(conf, user, f);
2317 tmp[0] = f->subclass;
2319 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2320 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2325 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2327 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2330 exitkey[0] = f->subclass;
2333 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2335 if (confflags & CONFFLAG_PASS_DTMF)
2336 conf_queue_dtmf(conf, user, f);
2340 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2341 if (confflags & CONFFLAG_PASS_DTMF)
2342 conf_queue_dtmf(conf, user, f);
2343 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2344 ast_log(LOG_WARNING, "Error setting conference\n");
2350 /* if we are entering the menu, and the user has a channel-driver
2351 volume adjustment, clear it
2353 if (!menu_active && user->talk.desired && !user->talk.actual)
2354 set_talk_volume(user, 0);
2359 if ((confflags & CONFFLAG_ADMIN)) {
2363 /* Record this sound! */
2364 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2365 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2366 ast_stopstream(chan);
2373 case '1': /* Un/Mute */
2376 /* for admin, change both admin and use flags */
2377 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2378 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2380 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2382 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2383 if (!ast_streamfile(chan, "conf-muted", chan->language))
2384 ast_waitstream(chan, "");
2386 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2387 ast_waitstream(chan, "");
2390 case '2': /* Un/Lock the Conference */
2394 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2395 ast_waitstream(chan, "");
2398 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2399 ast_waitstream(chan, "");
2402 case '3': /* Eject last user */
2404 usr = AST_LIST_LAST(&conf->userlist);
2405 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2406 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2407 ast_waitstream(chan, "");
2409 usr->adminflags |= ADMINFLAG_KICKME;
2410 ast_stopstream(chan);
2413 tweak_listen_volume(user, VOL_DOWN);
2416 tweak_listen_volume(user, VOL_UP);
2419 tweak_talk_volume(user, VOL_DOWN);
2425 tweak_talk_volume(user, VOL_UP);
2429 /* Play an error message! */
2430 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2431 ast_waitstream(chan, "");
2439 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2440 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2441 ast_stopstream(chan);
2448 case '1': /* Un/Mute */
2451 /* user can only toggle the self-muted state */
2452 user->adminflags ^= ADMINFLAG_SELFMUTED;
2454 /* they can't override the admin mute state */
2455 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2456 if (!ast_streamfile(chan, "conf-muted", chan->language))
2457 ast_waitstream(chan, "");
2459 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2460 ast_waitstream(chan, "");
2465 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2466 user->adminflags |= ADMINFLAG_T_REQUEST;
2468 if (user->adminflags & ADMINFLAG_T_REQUEST)
2469 if (!ast_streamfile(chan, "beep", chan->language))
2470 ast_waitstream(chan, "");
2473 tweak_listen_volume(user, VOL_DOWN);
2476 tweak_listen_volume(user, VOL_UP);
2479 tweak_talk_volume(user, VOL_DOWN);
2485 tweak_talk_volume(user, VOL_UP);
2489 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2490 ast_waitstream(chan, "");
2496 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2498 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2499 ast_log(LOG_WARNING, "Error setting conference\n");
2505 conf_flush(fd, chan);
2506 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2507 && confflags & CONFFLAG_PASS_DTMF) {
2508 conf_queue_dtmf(conf, user, f);
2509 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2510 switch (f->subclass) {
2511 case AST_CONTROL_HOLD:
2512 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2517 } else if (f->frametype == AST_FRAME_NULL) {
2518 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2521 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2522 chan->name, f->frametype, f->subclass);
2525 } else if (outfd > -1) {
2526 res = read(outfd, buf, CONF_SIZE);
2528 memset(&fr, 0, sizeof(fr));
2529 fr.frametype = AST_FRAME_VOICE;
2530 fr.subclass = AST_FORMAT_SLINEAR;
2534 fr.offset = AST_FRIENDLY_OFFSET;
2535 if ( !user->listen.actual &&
2536 ((confflags & CONFFLAG_MONITOR) ||
2537 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2538 (!user->talking)) ) {
2540 for (index=0;index<AST_FRAME_BITS;index++)
2541 if (chan->rawwriteformat & (1 << index))
2543 if (index >= AST_FRAME_BITS)
2544 goto bailoutandtrynormal;
2545 ast_mutex_lock(&conf->listenlock);
2546 if (!conf->transframe[index]) {
2547 if (conf->origframe) {
2548 if (!conf->transpath[index])
2549 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2550 if (conf->transpath[index]) {
2551 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2552 if (!conf->transframe[index])
2553 conf->transframe[index] = &ast_null_frame;
2557 if (conf->transframe[index]) {
2558 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2559 if (ast_write(chan, conf->transframe[index]))
2560 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2563 ast_mutex_unlock(&conf->listenlock);
2564 goto bailoutandtrynormal;
2566 ast_mutex_unlock(&conf->listenlock);
2568 bailoutandtrynormal:
2569 if (user->listen.actual)
2570 ast_frame_adjust_volume(&fr, user->listen.actual);
2571 if (ast_write(chan, &fr) < 0) {
2572 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2576 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2578 lastmarked = currentmarked;
2588 /* Take out of conference */
2592 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2593 ast_log(LOG_WARNING, "Error setting conference\n");
2597 reset_volumes(user);
2599 AST_LIST_LOCK(&confs);
2600 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2601 conf_play(chan, conf, LEAVE);
2603 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2604 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2605 if ((conf->chan) && (conf->users > 1)) {
2606 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2607 ast_waitstream(conf->chan, "");
2608 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2609 ast_waitstream(conf->chan, "");
2611 ast_filedelete(user->namerecloc, NULL);
2614 AST_LIST_UNLOCK(&confs);
2617 AST_LIST_LOCK(&confs);
2622 if (user->user_no) { /* Only cleanup users who really joined! */
2624 hr = (now.tv_sec - user->jointime) / 3600;
2625 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2626 sec = (now.tv_sec - user->jointime) % 60;
2629 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2634 "CallerIDNum: %s\r\n"
2635 "CallerIDName: %s\r\n"
2636 "Duration: %ld\r\n",
2637 chan->name, chan->uniqueid, conf->confno,
2639 S_OR(user->chan->cid.cid_num, "<unknown>"),
2640 S_OR(user->chan->cid.cid_name, "<unknown>"),
2641 (long)(now.tv_sec - user->jointime));
2645 if (rt_log_members){
2647 snprintf(members, sizeof(members), "%d", conf->users);
2648 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2650 if (confflags & CONFFLAG_MARKEDUSER)
2651 conf->markedusers--;
2652 /* Remove ourselves from the list */
2653 AST_LIST_REMOVE(&conf->userlist, user, list);
2655 /* Change any states */
2657 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2659 /* Return the number of seconds the user was in the conf */
2660 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2661 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2664 AST_LIST_UNLOCK(&confs);
2669 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2670 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2671 char *optargs[], int *too_early)
2673 struct ast_variable *var;
2674 struct ast_conference *cnf;
2678 /* Check first in the conference list */
2679 AST_LIST_LOCK(&confs);
2680 AST_LIST_TRAVERSE(&confs, cnf, list) {
2681 if (!strcmp(confno, cnf->confno))
2685 cnf->refcount += refcount;
2687 AST_LIST_UNLOCK(&confs);
2690 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2693 char currenttime[19] = "";
2694 char startTime[19] = "";
2695 char endtime[19] = "";
2696 char eatime[19] = "";
2697 char userOpts[32] = "";
2698 char adminOpts[32] = "";
2699 struct ast_tm tm, etm;
2705 now.tv_sec += fuzzystart;
2707 ast_localtime(&now, &tm, NULL);
2708 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2711 now.tv_sec += earlyalert;
2712 ast_localtime(&now, &etm, NULL);
2713 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2715 ast_copy_string(eatime, currenttime, sizeof(eatime));
2718 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2720 var = ast_load_realtime("meetme", "confno",
2721 confno, "startTime<= ", eatime, "endtime>= ",
2724 var = ast_load_realtime("meetme", "confno", confno, NULL);
2730 if (!strcasecmp(var->name, "pin")) {
2731 pin = ast_strdupa(var->value);
2732 } else if (!strcasecmp(var->name, "adminpin")) {
2733 pinadmin = ast_strdupa(var->value);
2734 } else if (!strcasecmp(var->name, "opts")) {
2735 ast_copy_string(userOpts, var->value, sizeof(userOpts));
2736 } else if (!strcasecmp(var->name, "maxusers")) {
2737 maxusers = atoi(var->value);
2738 } else if (!strcasecmp(var->name, "adminopts")) {
2739 ast_copy_string(adminOpts, var->value, sizeof(adminOpts));
2740 } else if (!strcasecmp(var->name, "endtime")) {
2741 ast_copy_string(endtime, var->value, sizeof(endtime));
2742 } else if (!strcasecmp(var->name, "starttime")) {
2743 ast_copy_string(startTime, var->value, sizeof(startTime));
2748 ast_variables_destroy(var);
2751 if (strcmp(startTime, currenttime) > 0) {
2752 /* Announce that the caller is early and exit */
2753 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2754 ast_waitstream(chan, "");
2760 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2763 cnf->maxusers = maxusers;
2764 cnf->endalert = endalert;
2765 ast_copy_string(cnf->endtime, endtime, sizeof(cnf->endtime));
2770 if (confflags && !cnf->chan &&
2771 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2772 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2773 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2774 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2777 if (confflags && !cnf->chan &&
2778 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2779 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2780 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2788 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2789 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2791 struct ast_config *cfg;
2792 struct ast_variable *var;
2793 struct ast_flags config_flags = { 0 };
2794 struct ast_conference *cnf;
2796 AST_DECLARE_APP_ARGS(args,
2797 AST_APP_ARG(confno);
2799 AST_APP_ARG(pinadmin);
2802 /* Check first in the conference list */
2803 ast_debug(1,"The requested confno is '%s'?\n", confno);
2804 AST_LIST_LOCK(&confs);
2805 AST_LIST_TRAVERSE(&confs, cnf, list) {
2806 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2807 if (!strcmp(confno, cnf->confno))
2811 cnf->refcount += refcount;
2813 AST_LIST_UNLOCK(&confs);
2817 /* No need to parse meetme.conf */
2818 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2820 if (dynamic_pin[0] == 'q') {
2821 /* Query the user to enter a PIN */
2822 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2825 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2827 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2830 /* Check the config */
2831 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2833 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2836 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2837 if (strcasecmp(var->name, "conf"))
2840 if (!(parse = ast_strdupa(var->value)))
2843 AST_STANDARD_APP_ARGS(args, parse);
2844 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2845 if (!strcasecmp(args.confno, confno)) {
2846 /* Bingo it's a valid conference */
2847 cnf = build_conf(args.confno,
2849 S_OR(args.pinadmin, ""),
2850 make, dynamic, refcount, chan);
2855 ast_debug(1, "%s isn't a valid conference\n", confno);
2857 ast_config_destroy(cfg);
2859 } else if (dynamic_pin) {
2860 /* Correct for the user selecting 'D' instead of 'd' to have
2861 someone join into a conference that has already been created
2863 if (dynamic_pin[0] == 'q')
2864 dynamic_pin[0] = '\0';
2868 if (confflags && !cnf->chan &&
2869 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2870 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2871 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2872 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2875 if (confflags && !cnf->chan &&
2876 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2877 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2878 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2885 /*! \brief The MeetmeCount application */
2886 static int count_exec(struct ast_channel *chan, void *data)
2889 struct ast_conference *conf;
2893 AST_DECLARE_APP_ARGS(args,
2894 AST_APP_ARG(confno);
2895 AST_APP_ARG(varname);
2898 if (ast_strlen_zero(data)) {
2899 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2903 if (!(localdata = ast_strdupa(data)))
2906 AST_STANDARD_APP_ARGS(args, localdata);
2908 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2911 count = conf->users;
2917 if (!ast_strlen_zero(args.varname)){
2918 /* have var so load it and exit */
2919 snprintf(val, sizeof(val), "%d",count);
2920 pbx_builtin_setvar_helper(chan, args.varname, val);
2922 if (chan->_state != AST_STATE_UP)
2924 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2930 /*! \brief The meetme() application */
2931 static int conf_exec(struct ast_channel *chan, void *data)
2934 char confno[MAX_CONFNUM] = "";
2937 struct ast_conference *cnf = NULL;
2938 struct ast_flags confflags = {0}, config_flags = { 0 };
2940 int empty = 0, empty_no_pin = 0;
2941 int always_prompt = 0;
2942 char *notdata, *info, the_pin[MAX_PIN] = "";
2943 AST_DECLARE_APP_ARGS(args,
2944 AST_APP_ARG(confno);
2945 AST_APP_ARG(options);
2948 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2950 if (ast_strlen_zero(data)) {
2957 if (chan->_state != AST_STATE_UP)
2960 info = ast_strdupa(notdata);
2962 AST_STANDARD_APP_ARGS(args, info);
2965 ast_copy_string(confno, args.confno, sizeof(confno));
2966 if (ast_strlen_zero(confno)) {
2972 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2975 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2976 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2977 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2978 strcpy(the_pin, "q");
2980 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2981 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2982 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2990 struct ast_config *cfg;
2991 struct ast_variable *var;
2994 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2995 if ((empty_no_pin) || (!dynamic)) {
2996 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2998 var = ast_variable_browse(cfg, "rooms");
3000 if (!strcasecmp(var->name, "conf")) {
3001 char *stringp = ast_strdupa(var->value);
3003 char *confno_tmp = strsep(&stringp, "|,");
3006 /* For static: run through the list and see if this conference is empty */
3007 AST_LIST_LOCK(&confs);
3008 AST_LIST_TRAVERSE(&confs, cnf, list) {
3009 if (!strcmp(confno_tmp, cnf->confno)) {
3010 /* The conference exists, therefore it's not empty */
3015 AST_LIST_UNLOCK(&confs);
3017 /* At this point, we have a confno_tmp (static conference) that is empty */
3018 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
3019 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
3020 * Case 2: empty_no_pin and pin is blank (but not NULL)
3021 * Case 3: not empty_no_pin
3023 ast_copy_string(confno, confno_tmp, sizeof(confno));
3025 /* XXX the map is not complete (but we do have a confno) */
3033 ast_config_destroy(cfg);
3037 /* Select first conference number not in use */
3038 if (ast_strlen_zero(confno) && dynamic) {
3039 AST_LIST_LOCK(&confs);
3040 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
3042 snprintf(confno, sizeof(confno), "%d", i);