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/devicestate.h"
58 #include "asterisk/dial.h"
59 #include "asterisk/causes.h"
60 #include "asterisk/paths.h"
65 #define CONFIG_FILE_NAME "meetme.conf"
66 #define SLA_CONFIG_FILE "sla.conf"
68 /*! each buffer is 20ms, so this is 640ms total */
69 #define DEFAULT_AUDIO_BUFFERS 32
71 /*! String format for scheduled conferences */
72 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
75 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
76 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
77 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
78 /*! User has requested to speak */
79 ADMINFLAG_T_REQUEST = (1 << 4),
82 #define MEETME_DELAYDETECTTALK 300
83 #define MEETME_DELAYDETECTENDTALK 1000
85 #define AST_FRAME_BITS 32
97 enum recording_state {
99 MEETME_RECORD_STARTED,
100 MEETME_RECORD_ACTIVE,
101 MEETME_RECORD_TERMINATE
104 #define CONF_SIZE 320
107 /*! user has admin access on the conference */
108 CONFFLAG_ADMIN = (1 << 0),
109 /*! If set the user can only receive audio from the conference */
110 CONFFLAG_MONITOR = (1 << 1),
111 /*! If set asterisk will exit conference when key defined in p() option is pressed */
112 CONFFLAG_KEYEXIT = (1 << 2),
113 /*! If set asterisk will provide a menu to the user when '*' is pressed */
114 CONFFLAG_STARMENU = (1 << 3),
115 /*! If set the use can only send audio to the conference */
116 CONFFLAG_TALKER = (1 << 4),
117 /*! If set there will be no enter or leave sounds */
118 CONFFLAG_QUIET = (1 << 5),
119 /*! If set, when user joins the conference, they will be told the number
120 * of users that are already in */
121 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
122 /*! Set to run AGI Script in Background */
123 CONFFLAG_AGI = (1 << 7),
124 /*! Set to have music on hold when user is alone in conference */
125 CONFFLAG_MOH = (1 << 8),
126 /*! If set the MeetMe will return if all marked with this flag left */
127 CONFFLAG_MARKEDEXIT = (1 << 9),
128 /*! If set, the MeetMe will wait until a marked user enters */
129 CONFFLAG_WAITMARKED = (1 << 10),
130 /*! If set, the MeetMe will exit to the specified context */
131 CONFFLAG_EXIT_CONTEXT = (1 << 11),
132 /*! If set, the user will be marked */
133 CONFFLAG_MARKEDUSER = (1 << 12),
134 /*! If set, user will be ask record name on entry of conference */
135 CONFFLAG_INTROUSER = (1 << 13),
136 /*! If set, the MeetMe will be recorded */
137 CONFFLAG_RECORDCONF = (1<< 14),
138 /*! If set, the user will be monitored if the user is talking or not */
139 CONFFLAG_MONITORTALKER = (1 << 15),
140 CONFFLAG_DYNAMIC = (1 << 16),
141 CONFFLAG_DYNAMICPIN = (1 << 17),
142 CONFFLAG_EMPTY = (1 << 18),
143 CONFFLAG_EMPTYNOPIN = (1 << 19),
144 CONFFLAG_ALWAYSPROMPT = (1 << 20),
145 /*! If set, won't speak the extra prompt when the first person
146 * enters the conference */
147 CONFFLAG_NOONLYPERSON = (1 << 22),
148 /*! If set, user will be asked to record name on entry of conference
150 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
151 /*! If set, the user will be initially self-muted */
152 CONFFLAG_STARTMUTED = (1 << 24),
153 /*! Pass DTMF through the conference */
154 CONFFLAG_PASS_DTMF = (1 << 25),
155 CONFFLAG_SLA_STATION = (1 << 26),
156 CONFFLAG_SLA_TRUNK = (1 << 27),
157 /*! If set, the user should continue in the dialplan if kicked out */
158 CONFFLAG_KICK_CONTINUE = (1 << 28),
159 CONFFLAG_DURATION_STOP = (1 << 29),
160 CONFFLAG_DURATION_LIMIT = (1 << 30),
164 OPT_ARG_WAITMARKED = 0,
165 OPT_ARG_EXITKEYS = 1,
166 OPT_ARG_DURATION_STOP = 2,
167 OPT_ARG_DURATION_LIMIT = 3,
168 OPT_ARG_MOH_CLASS = 4,
169 OPT_ARG_ARRAY_SIZE = 5,
172 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
173 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
174 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
175 AST_APP_OPTION('b', CONFFLAG_AGI ),
176 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
177 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
178 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
179 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
180 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
181 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
182 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
183 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
184 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
185 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
186 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
187 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
188 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
189 AST_APP_OPTION('q', CONFFLAG_QUIET ),
190 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
191 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
192 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
193 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
194 AST_APP_OPTION('t', CONFFLAG_TALKER ),
195 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
196 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
197 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
198 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
199 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
200 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
203 static const char *app = "MeetMe";
204 static const char *app2 = "MeetMeCount";
205 static const char *app3 = "MeetMeAdmin";
206 static const char *app4 = "MeetMeChannelAdmin";
207 static const char *slastation_app = "SLAStation";
208 static const char *slatrunk_app = "SLATrunk";
210 static const char *synopsis = "MeetMe conference bridge";
211 static const char *synopsis2 = "MeetMe participant count";
212 static const char *synopsis3 = "MeetMe conference Administration";
213 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
214 static const char *slastation_synopsis = "Shared Line Appearance Station";
215 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
217 /* Lookup RealTime conferences based on confno and current time */
218 static int rt_schedule;
219 static int fuzzystart;
220 static int earlyalert;
223 /* Log participant count to the RealTime backend */
224 static int rt_log_members;
226 static const char *descrip =
227 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
228 "conference. If the conference number is omitted, the user will be prompted\n"
229 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
230 "is specified, by pressing '#'.\n"
231 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
232 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
233 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
234 "The option string may contain zero or more of the following characters:\n"
235 " 'a' -- set admin mode\n"
236 " 'A' -- set marked mode\n"
237 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
238 " Default: conf-background.agi (Note: This does not work with\n"
239 " non-Zap channels in the same conference)\n"
240 " 'c' -- announce user(s) count on joining a conference\n"
241 " 'C' -- continue in dialplan when kicked out of conference\n"
242 " 'd' -- dynamically add conference\n"
243 " 'D' -- dynamically add conference, prompting for a PIN\n"
244 " 'e' -- select an empty conference\n"
245 " 'E' -- select an empty pinless conference\n"
246 " 'F' -- Pass DTMF through the conference.\n"
247 " 'i' -- announce user join/leave with review\n"
248 " 'I' -- announce user join/leave without review\n"
249 " 'l' -- set listen only mode (Listen only, no talking)\n"
250 " 'm' -- set initially muted\n"
252 " -- enable music on hold when the conference has a single caller.\n"
253 " Optionally, specify a musiconhold class to use. If one is not\n"
254 " provided, it will use the channel's currently set music class,\n"
256 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
257 " being muted, meaning (a) No encode is done on transmission and\n"
258 " (b) Received audio that is not registered as talking is omitted\n"
259 " causing no buildup in background noise\n"
261 " -- allow user to exit the conference by pressing '#' (default)\n"
262 " or any of the defined keys. If keys contain '*' this will override\n"
263 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
264 " 'P' -- always prompt for the pin even if it is specified\n"
265 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
266 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
267 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
268 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
270 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
271 " 't' -- set talk only mode. (Talk only, no listening)\n"
272 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
274 " -- wait until the marked user enters the conference\n"
275 " 'x' -- close the conference when last marked user exits\n"
276 " 'X' -- allow user to exit the conference by entering a valid single\n"
277 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
278 " if that variable is not defined.\n"
279 " '1' -- do not play message when first person enters\n"
280 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
281 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
282 " left. Repeat the warning every 'z' ms. The following special\n"
283 " variables can be used with this option:\n"
284 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
285 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
286 " The default is to say the time remaining.\n"
289 static const char *descrip2 =
290 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
291 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
292 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
293 "the channel, unless priority n+1 exists, in which case priority progress will\n"
295 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
297 static const char *descrip3 =
298 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
299 " 'e' -- Eject last user that joined\n"
300 " 'k' -- Kick one user out of conference\n"
301 " 'K' -- Kick all users out of conference\n"
302 " 'l' -- Unlock conference\n"
303 " 'L' -- Lock conference\n"
304 " 'm' -- Unmute one user\n"
305 " 'M' -- Mute one user\n"
306 " 'n' -- Unmute all users in the conference\n"
307 " 'N' -- Mute all non-admin users in the conference\n"
308 " 'r' -- Reset one user's volume settings\n"
309 " 'R' -- Reset all users volume settings\n"
310 " 's' -- Lower entire conference speaking volume\n"
311 " 'S' -- Raise entire conference speaking volume\n"
312 " 't' -- Lower one user's talk volume\n"
313 " 'T' -- Raise one user's talk volume\n"
314 " 'u' -- Lower one user's listen volume\n"
315 " 'U' -- Raise one user's listen volume\n"
316 " 'v' -- Lower entire conference listening volume\n"
317 " 'V' -- Raise entire conference listening volume\n"
320 static const char *descrip4 =
321 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
322 "channel in any coference.\n"
323 " 'k' -- Kick the specified user out of the conference he is in\n"
324 " 'm' -- Unmute the specified user\n"
325 " 'M' -- Mute the specified user\n"
328 static const char *slastation_desc =
329 " SLAStation(<station name>):\n"
330 "This application should be executed by an SLA station. The argument depends\n"
331 "on how the call was initiated. If the phone was just taken off hook, then\n"
332 "the argument \"station\" should be just the station name. If the call was\n"
333 "initiated by pressing a line key, then the station name should be preceded\n"
334 "by an underscore and the trunk name associated with that line button.\n"
335 "For example: \"station1_line1\"."
336 " On exit, this application will set the variable SLASTATION_STATUS to\n"
337 "one of the following values:\n"
338 " FAILURE | CONGESTION | SUCCESS\n"
341 static const char *slatrunk_desc =
342 " SLATrunk(<trunk name>[,options]):\n"
343 "This application should be executed by an SLA trunk on an inbound call.\n"
344 "The channel calling this application should correspond to the SLA trunk\n"
345 "with the name \"trunk\" that is being passed as an argument.\n"
346 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
347 "one of the following values:\n"
348 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
349 " The available options are:\n"
350 " M[(<class>)] - Play back the specified MOH class instead of ringing\n"
353 #define MAX_CONFNUM 80
356 /*! \brief The MeetMe Conference object */
357 struct ast_conference {
358 ast_mutex_t playlock; /*!< Conference specific lock (players) */
359 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
360 char confno[MAX_CONFNUM]; /*!< Conference */
361 struct ast_channel *chan; /*!< Announcements channel */
362 struct ast_channel *lchan; /*!< Listen/Record channel */
363 int fd; /*!< Announcements fd */
364 int zapconf; /*!< Zaptel Conf # */
365 int users; /*!< Number of active users */
366 int markedusers; /*!< Number of marked users */
367 int maxusers; /*!< Participant limit if scheduled */
368 int endalert; /*!< When to play conf ending message */
369 time_t start; /*!< Start time (s) */
370 int refcount; /*!< reference count of usage */
371 enum recording_state recording:2; /*!< recording status */
372 unsigned int isdynamic:1; /*!< Created on the fly? */
373 unsigned int locked:1; /*!< Is the conference locked? */
374 pthread_t recordthread; /*!< thread for recording */
375 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
376 pthread_attr_t attr; /*!< thread attribute */
377 const char *recordingfilename; /*!< Filename to record the Conference into */
378 const char *recordingformat; /*!< Format to record the Conference in */
379 char pin[MAX_PIN]; /*!< If protected by a PIN */
380 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
382 long endtime; /*!< When to end the conf if scheduled */
383 struct ast_frame *transframe[32];
384 struct ast_frame *origframe;
385 struct ast_trans_pvt *transpath[32];
386 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
387 AST_LIST_ENTRY(ast_conference) list;
390 static AST_LIST_HEAD_STATIC(confs, ast_conference);
392 static unsigned int conf_map[1024] = {0, };
395 int desired; /*!< Desired volume adjustment */
396 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
399 /*! \brief The MeetMe User object */
400 struct ast_conf_user {
401 int user_no; /*!< User Number */
402 int userflags; /*!< Flags as set in the conference */
403 int adminflags; /*!< Flags set by the Admin */
404 struct ast_channel *chan; /*!< Connected channel */
405 int talking; /*!< Is user talking */
406 int zapchannel; /*!< Is a Zaptel channel */
407 char usrvalue[50]; /*!< Custom User Value */
408 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
409 time_t jointime; /*!< Time the user joined the conference */
410 time_t kicktime; /*!< Time the user will be kicked from the conference */
411 struct timeval start_time; /*!< Time the user entered into the conference */
412 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
413 long play_warning; /*!< Play a warning when 'y' ms are left */
414 long warning_freq; /*!< Repeat the warning every 'z' ms */
415 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
416 const char *end_sound; /*!< File to play when time is up. */
418 struct volume listen;
419 AST_LIST_ENTRY(ast_conf_user) list;
422 enum sla_which_trunk_refs {
427 enum sla_trunk_state {
428 SLA_TRUNK_STATE_IDLE,
429 SLA_TRUNK_STATE_RINGING,
431 SLA_TRUNK_STATE_ONHOLD,
432 SLA_TRUNK_STATE_ONHOLD_BYME,
435 enum sla_hold_access {
436 /*! This means that any station can put it on hold, and any station
437 * can retrieve the call from hold. */
439 /*! This means that only the station that put the call on hold may
440 * retrieve it from hold. */
444 struct sla_trunk_ref;
447 AST_RWLIST_ENTRY(sla_station) entry;
448 AST_DECLARE_STRING_FIELDS(
449 AST_STRING_FIELD(name);
450 AST_STRING_FIELD(device);
451 AST_STRING_FIELD(autocontext);
453 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
454 struct ast_dial *dial;
455 /*! Ring timeout for this station, for any trunk. If a ring timeout
456 * is set for a specific trunk on this station, that will take
457 * priority over this value. */
458 unsigned int ring_timeout;
459 /*! Ring delay for this station, for any trunk. If a ring delay
460 * is set for a specific trunk on this station, that will take
461 * priority over this value. */
462 unsigned int ring_delay;
463 /*! This option uses the values in the sla_hold_access enum and sets the
464 * access control type for hold on this station. */
465 unsigned int hold_access:1;
466 /*! Use count for inside sla_station_exec */
467 unsigned int ref_count;
470 struct sla_station_ref {
471 AST_LIST_ENTRY(sla_station_ref) entry;
472 struct sla_station *station;
476 AST_RWLIST_ENTRY(sla_trunk) entry;
477 AST_DECLARE_STRING_FIELDS(
478 AST_STRING_FIELD(name);
479 AST_STRING_FIELD(device);
480 AST_STRING_FIELD(autocontext);
482 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
483 /*! Number of stations that use this trunk */
484 unsigned int num_stations;
485 /*! Number of stations currently on a call with this trunk */
486 unsigned int active_stations;
487 /*! Number of stations that have this trunk on hold. */
488 unsigned int hold_stations;
489 struct ast_channel *chan;
490 unsigned int ring_timeout;
491 /*! If set to 1, no station will be able to join an active call with
493 unsigned int barge_disabled:1;
494 /*! This option uses the values in the sla_hold_access enum and sets the
495 * access control type for hold on this trunk. */
496 unsigned int hold_access:1;
497 /*! Whether this trunk is currently on hold, meaning that once a station
498 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
499 unsigned int on_hold:1;
500 /*! Use count for inside sla_trunk_exec */
501 unsigned int ref_count;
504 struct sla_trunk_ref {
505 AST_LIST_ENTRY(sla_trunk_ref) entry;
506 struct sla_trunk *trunk;
507 enum sla_trunk_state state;
508 struct ast_channel *chan;
509 /*! Ring timeout to use when this trunk is ringing on this specific
510 * station. This takes higher priority than a ring timeout set at
511 * the station level. */
512 unsigned int ring_timeout;
513 /*! Ring delay to use when this trunk is ringing on this specific
514 * station. This takes higher priority than a ring delay set at
515 * the station level. */
516 unsigned int ring_delay;
519 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
520 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
522 static const char sla_registrar[] = "SLA";
524 /*! \brief Event types that can be queued up for the SLA thread */
525 enum sla_event_type {
526 /*! A station has put the call on hold */
528 /*! The state of a dial has changed */
529 SLA_EVENT_DIAL_STATE,
530 /*! The state of a ringing trunk has changed */
531 SLA_EVENT_RINGING_TRUNK,
532 /*! A reload of configuration has been requested */
534 /*! Poke the SLA thread so it can check if it can perform a reload */
535 SLA_EVENT_CHECK_RELOAD,
539 enum sla_event_type type;
540 struct sla_station *station;
541 struct sla_trunk_ref *trunk_ref;
542 AST_LIST_ENTRY(sla_event) entry;
545 /*! \brief A station that failed to be dialed
546 * \note Only used by the SLA thread. */
547 struct sla_failed_station {
548 struct sla_station *station;
549 struct timeval last_try;
550 AST_LIST_ENTRY(sla_failed_station) entry;
553 /*! \brief A trunk that is ringing */
554 struct sla_ringing_trunk {
555 struct sla_trunk *trunk;
556 /*! The time that this trunk started ringing */
557 struct timeval ring_begin;
558 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
559 AST_LIST_ENTRY(sla_ringing_trunk) entry;
562 enum sla_station_hangup {
563 SLA_STATION_HANGUP_NORMAL,
564 SLA_STATION_HANGUP_TIMEOUT,
567 /*! \brief A station that is ringing */
568 struct sla_ringing_station {
569 struct sla_station *station;
570 /*! The time that this station started ringing */
571 struct timeval ring_begin;
572 AST_LIST_ENTRY(sla_ringing_station) entry;
576 * \brief A structure for data used by the sla thread
579 /*! The SLA thread ID */
583 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
584 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
585 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
586 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
588 /*! Attempt to handle CallerID, even though it is known not to work
589 * properly in some situations. */
590 unsigned int attempt_callerid:1;
591 /*! A reload has been requested */
592 unsigned int reload:1;
594 .thread = AST_PTHREADT_NULL,
597 /*! The number of audio buffers to be allocated on pseudo channels
598 * when in a conference */
599 static int audio_buffers;
601 /*! Map 'volume' levels from -5 through +5 into
602 * decibel (dB) settings for channel drivers
603 * Note: these are not a straight linear-to-dB
604 * conversion... the numbers have been modified
605 * to give the user a better level of adjustability
607 static char const gain_map[] = {
622 static int admin_exec(struct ast_channel *chan, void *data);
623 static void *recordthread(void *args);
625 static char *istalking(int x)
630 return "(unmonitored)";
632 return "(not talking)";
635 static int careful_write(int fd, unsigned char *data, int len, int block)
642 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
643 res = ioctl(fd, ZT_IOMUX, &x);
647 res = write(fd, data, len);
649 if (errno != EAGAIN) {
650 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
662 static int set_talk_volume(struct ast_conf_user *user, int volume)
666 /* attempt to make the adjustment in the channel driver;
667 if successful, don't adjust in the frame reading routine
669 gain_adjust = gain_map[volume + 5];
671 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
674 static int set_listen_volume(struct ast_conf_user *user, int volume)
678 /* attempt to make the adjustment in the channel driver;
679 if successful, don't adjust in the frame reading routine
681 gain_adjust = gain_map[volume + 5];
683 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
686 static void tweak_volume(struct volume *vol, enum volume_action action)
690 switch (vol->desired) {
705 switch (vol->desired) {
721 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
723 tweak_volume(&user->talk, action);
724 /* attempt to make the adjustment in the channel driver;
725 if successful, don't adjust in the frame reading routine
727 if (!set_talk_volume(user, user->talk.desired))
728 user->talk.actual = 0;
730 user->talk.actual = user->talk.desired;
733 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
735 tweak_volume(&user->listen, action);
736 /* attempt to make the adjustment in the channel driver;
737 if successful, don't adjust in the frame reading routine
739 if (!set_listen_volume(user, user->listen.desired))
740 user->listen.actual = 0;
742 user->listen.actual = user->listen.desired;
745 static void reset_volumes(struct ast_conf_user *user)
747 signed char zero_volume = 0;
749 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
750 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
753 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
759 if (!ast_check_hangup(chan))
760 res = ast_autoservice_start(chan);
762 AST_LIST_LOCK(&confs);
778 careful_write(conf->fd, data, len, 1);
781 AST_LIST_UNLOCK(&confs);
784 ast_autoservice_stop(chan);
788 * \brief Find or create a conference
790 * \param confno The conference name/number
791 * \param pin The regular user pin
792 * \param pinadmin The admin pin
793 * \param make Make the conf if it doesn't exist
794 * \param dynamic Mark the newly created conference as dynamic
795 * \param refcount How many references to mark on the conference
796 * \param chan The asterisk channel
798 * \return A pointer to the conference struct, or NULL if it wasn't found and
799 * make or dynamic were not set.
801 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
803 struct ast_conference *cnf;
804 struct zt_confinfo ztc = { 0, };
807 AST_LIST_LOCK(&confs);
809 AST_LIST_TRAVERSE(&confs, cnf, list) {
810 if (!strcmp(confno, cnf->confno))
814 if (cnf || (!make && !dynamic))
818 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
821 ast_mutex_init(&cnf->playlock);
822 ast_mutex_init(&cnf->listenlock);
823 cnf->recordthread = AST_PTHREADT_NULL;
824 ast_mutex_init(&cnf->recordthreadlock);
825 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
826 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
827 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
828 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
830 /* Setup a new zap conference */
832 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
833 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
834 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
835 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
843 cnf->zapconf = ztc.confno;
845 /* Setup a new channel for playback of audio files */
846 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
848 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
849 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
851 ztc.confno = cnf->zapconf;
852 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
853 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
854 ast_log(LOG_WARNING, "Error setting conference\n");
856 ast_hangup(cnf->chan);
866 /* Fill the conference struct */
867 cnf->start = time(NULL);
868 cnf->maxusers = 0x7fffffff;
869 cnf->isdynamic = dynamic ? 1 : 0;
870 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
871 AST_LIST_INSERT_HEAD(&confs, cnf, list);
873 /* Reserve conference number in map */
874 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
875 conf_map[confno_int] = 1;
879 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
881 AST_LIST_UNLOCK(&confs);
887 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
889 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
891 int len = strlen(word);
893 struct ast_conference *cnf = NULL;
894 struct ast_conf_user *usr = NULL;
897 char *myline, *ret = NULL;
899 if (pos == 1) { /* Command */
900 return ast_cli_complete(word, cmds, state);
901 } else if (pos == 2) { /* Conference Number */
902 AST_LIST_LOCK(&confs);
903 AST_LIST_TRAVERSE(&confs, cnf, list) {
904 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
909 ret = ast_strdup(ret); /* dup before releasing the lock */
910 AST_LIST_UNLOCK(&confs);
912 } else if (pos == 3) {
913 /* User Number || Conf Command option*/
914 if (strstr(line, "mute") || strstr(line, "kick")) {
915 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
916 return ast_strdup("all");
918 AST_LIST_LOCK(&confs);
920 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
921 myline = ast_strdupa(line);
922 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
923 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
927 AST_LIST_TRAVERSE(&confs, cnf, list) {
928 if (!strcmp(confno, cnf->confno))
933 /* Search for the user */
934 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
935 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
936 if (!strncasecmp(word, usrno, len) && ++which > state)
940 AST_LIST_UNLOCK(&confs);
941 return usr ? ast_strdup(usrno) : NULL;
942 } else if (strstr(line, "list") && (state == 0))
943 return ast_strdup("concise");
949 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
951 /* Process the command */
952 struct ast_conference *cnf;
953 struct ast_conf_user *user;
955 int i = 0, total = 0;
957 char cmdline[1024] = "";
958 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
959 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
963 e->command = "meetme";
965 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
966 " Executes a command for the conference or on a conferee\n";
969 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
973 ast_cli(a->fd, "Invalid Arguments.\n");
974 /* Check for length so no buffer will overflow... */
975 for (i = 0; i < a->argc; i++) {
976 if (strlen(a->argv[i]) > 100)
977 ast_cli(a->fd, "Invalid Arguments.\n");
979 if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
980 /* 'MeetMe': List all the conferences */
981 int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
983 AST_LIST_LOCK(&confs);
984 if (AST_LIST_EMPTY(&confs)) {
986 ast_cli(a->fd, "No active MeetMe conferences.\n");
987 AST_LIST_UNLOCK(&confs);
991 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
992 AST_LIST_TRAVERSE(&confs, cnf, list) {
993 if (cnf->markedusers == 0)
994 strcpy(cmdline, "N/A ");
996 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
997 hr = (now - cnf->start) / 3600;
998 min = ((now - cnf->start) % 3600) / 60;
999 sec = (now - cnf->start) % 60;
1001 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1003 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1012 total += cnf->users;
1014 AST_LIST_UNLOCK(&confs);
1016 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1020 return CLI_SHOWUSAGE;
1021 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
1022 if (strstr(a->argv[1], "lock")) {
1023 if (strcmp(a->argv[1], "lock") == 0) {
1025 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
1028 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
1030 } else if (strstr(a->argv[1], "mute")) {
1032 return CLI_SHOWUSAGE;
1033 if (strcmp(a->argv[1], "mute") == 0) {
1035 if (strcmp(a->argv[3], "all") == 0) {
1036 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
1038 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
1039 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1043 if (strcmp(a->argv[3], "all") == 0) {
1044 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
1046 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
1047 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1050 } else if (strcmp(a->argv[1], "kick") == 0) {
1052 return CLI_SHOWUSAGE;
1053 if (strcmp(a->argv[3], "all") == 0) {
1055 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
1057 /* Kick a single user */
1058 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
1059 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
1061 } else if (strcmp(a->argv[1], "list") == 0) {
1062 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1063 /* List all the users in a conference */
1064 if (AST_LIST_EMPTY(&confs)) {
1066 ast_cli(a->fd, "No active conferences.\n");
1069 /* Find the right conference */
1070 AST_LIST_LOCK(&confs);
1071 AST_LIST_TRAVERSE(&confs, cnf, list) {
1072 if (strcmp(cnf->confno, a->argv[2]) == 0)
1077 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1078 AST_LIST_UNLOCK(&confs);
1081 /* Show all the users */
1083 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1084 hr = (now - user->jointime) / 3600;
1085 min = ((now - user->jointime) % 3600) / 60;
1086 sec = (now - user->jointime) % 60;
1088 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1090 S_OR(user->chan->cid.cid_num, "<unknown>"),
1091 S_OR(user->chan->cid.cid_name, "<no name>"),
1093 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1094 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1095 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1096 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1097 istalking(user->talking), hr, min, sec);
1099 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1101 S_OR(user->chan->cid.cid_num, ""),
1102 S_OR(user->chan->cid.cid_name, ""),
1104 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1105 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1106 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1107 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1108 user->talking, hr, min, sec);
1112 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1113 AST_LIST_UNLOCK(&confs);
1116 return CLI_SHOWUSAGE;
1118 ast_debug(1, "Cmdline: %s\n", cmdline);
1120 admin_exec(NULL, cmdline);
1125 static const char *sla_hold_str(unsigned int hold_access)
1127 const char *hold = "Unknown";
1129 switch (hold_access) {
1133 case SLA_HOLD_PRIVATE:
1142 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1144 const struct sla_trunk *trunk;
1148 e->command = "sla show trunks";
1150 "Usage: sla show trunks\n"
1151 " This will list all trunks defined in sla.conf\n";
1158 "=============================================================\n"
1159 "=== Configured SLA Trunks ===================================\n"
1160 "=============================================================\n"
1162 AST_RWLIST_RDLOCK(&sla_trunks);
1163 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1164 struct sla_station_ref *station_ref;
1165 char ring_timeout[16] = "(none)";
1166 if (trunk->ring_timeout)
1167 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1168 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1169 "=== Trunk Name: %s\n"
1170 "=== ==> Device: %s\n"
1171 "=== ==> AutoContext: %s\n"
1172 "=== ==> RingTimeout: %s\n"
1173 "=== ==> BargeAllowed: %s\n"
1174 "=== ==> HoldAccess: %s\n"
1175 "=== ==> Stations ...\n",
1176 trunk->name, trunk->device,
1177 S_OR(trunk->autocontext, "(none)"),
1179 trunk->barge_disabled ? "No" : "Yes",
1180 sla_hold_str(trunk->hold_access));
1181 AST_RWLIST_RDLOCK(&sla_stations);
1182 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1183 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1184 AST_RWLIST_UNLOCK(&sla_stations);
1185 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1187 AST_RWLIST_UNLOCK(&sla_trunks);
1188 ast_cli(a->fd, "=============================================================\n\n");
1193 static const char *trunkstate2str(enum sla_trunk_state state)
1195 #define S(e) case e: return # e;
1197 S(SLA_TRUNK_STATE_IDLE)
1198 S(SLA_TRUNK_STATE_RINGING)
1199 S(SLA_TRUNK_STATE_UP)
1200 S(SLA_TRUNK_STATE_ONHOLD)
1201 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1203 return "Uknown State";
1207 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1209 const struct sla_station *station;
1213 e->command = "sla show stations";
1215 "Usage: sla show stations\n"
1216 " This will list all stations defined in sla.conf\n";
1223 "=============================================================\n"
1224 "=== Configured SLA Stations =================================\n"
1225 "=============================================================\n"
1227 AST_RWLIST_RDLOCK(&sla_stations);
1228 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1229 struct sla_trunk_ref *trunk_ref;
1230 char ring_timeout[16] = "(none)";
1231 char ring_delay[16] = "(none)";
1232 if (station->ring_timeout) {
1233 snprintf(ring_timeout, sizeof(ring_timeout),
1234 "%u", station->ring_timeout);
1236 if (station->ring_delay) {
1237 snprintf(ring_delay, sizeof(ring_delay),
1238 "%u", station->ring_delay);
1240 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1241 "=== Station Name: %s\n"
1242 "=== ==> Device: %s\n"
1243 "=== ==> AutoContext: %s\n"
1244 "=== ==> RingTimeout: %s\n"
1245 "=== ==> RingDelay: %s\n"
1246 "=== ==> HoldAccess: %s\n"
1247 "=== ==> Trunks ...\n",
1248 station->name, station->device,
1249 S_OR(station->autocontext, "(none)"),
1250 ring_timeout, ring_delay,
1251 sla_hold_str(station->hold_access));
1252 AST_RWLIST_RDLOCK(&sla_trunks);
1253 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1254 if (trunk_ref->ring_timeout) {
1255 snprintf(ring_timeout, sizeof(ring_timeout),
1256 "%u", trunk_ref->ring_timeout);
1258 strcpy(ring_timeout, "(none)");
1259 if (trunk_ref->ring_delay) {
1260 snprintf(ring_delay, sizeof(ring_delay),
1261 "%u", trunk_ref->ring_delay);
1263 strcpy(ring_delay, "(none)");
1264 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1265 "=== ==> State: %s\n"
1266 "=== ==> RingTimeout: %s\n"
1267 "=== ==> RingDelay: %s\n",
1268 trunk_ref->trunk->name,
1269 trunkstate2str(trunk_ref->state),
1270 ring_timeout, ring_delay);
1272 AST_RWLIST_UNLOCK(&sla_trunks);
1273 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1276 AST_RWLIST_UNLOCK(&sla_stations);
1277 ast_cli(a->fd, "============================================================\n"
1283 static struct ast_cli_entry cli_meetme[] = {
1284 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1285 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1286 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1289 static void conf_flush(int fd, struct ast_channel *chan)
1293 /* read any frames that may be waiting on the channel
1297 struct ast_frame *f;
1299 /* when no frames are available, this will wait
1300 for 1 millisecond maximum
1302 while (ast_waitfor(chan, 1)) {
1306 else /* channel was hung up or something else happened */
1311 /* flush any data sitting in the pseudo channel */
1313 if (ioctl(fd, ZT_FLUSH, &x))
1314 ast_log(LOG_WARNING, "Error flushing channel\n");
1318 /* Remove the conference from the list and free it.
1319 We assume that this was called while holding conflock. */
1320 static int conf_free(struct ast_conference *conf)
1324 AST_LIST_REMOVE(&confs, conf, list);
1325 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1327 if (conf->recording == MEETME_RECORD_ACTIVE) {
1328 conf->recording = MEETME_RECORD_TERMINATE;
1329 AST_LIST_UNLOCK(&confs);
1332 AST_LIST_LOCK(&confs);
1333 if (conf->recording == MEETME_RECORD_OFF)
1335 AST_LIST_UNLOCK(&confs);
1339 for (x = 0; x < AST_FRAME_BITS; x++) {
1340 if (conf->transframe[x])
1341 ast_frfree(conf->transframe[x]);
1342 if (conf->transpath[x])
1343 ast_translator_free_path(conf->transpath[x]);
1345 if (conf->origframe)
1346 ast_frfree(conf->origframe);
1348 ast_hangup(conf->lchan);
1350 ast_hangup(conf->chan);
1354 ast_mutex_destroy(&conf->playlock);
1355 ast_mutex_destroy(&conf->listenlock);
1356 ast_mutex_destroy(&conf->recordthreadlock);
1362 static void conf_queue_dtmf(const struct ast_conference *conf,
1363 const struct ast_conf_user *sender, struct ast_frame *f)
1365 struct ast_conf_user *user;
1367 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1370 if (ast_write(user->chan, f) < 0)
1371 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1375 static void sla_queue_event_full(enum sla_event_type type,
1376 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1378 struct sla_event *event;
1380 if (!(event = ast_calloc(1, sizeof(*event))))
1384 event->trunk_ref = trunk_ref;
1385 event->station = station;
1388 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1392 ast_mutex_lock(&sla.lock);
1393 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1394 ast_cond_signal(&sla.cond);
1395 ast_mutex_unlock(&sla.lock);
1398 static void sla_queue_event_nolock(enum sla_event_type type)
1400 sla_queue_event_full(type, NULL, NULL, 0);
1403 static void sla_queue_event(enum sla_event_type type)
1405 sla_queue_event_full(type, NULL, NULL, 1);
1408 /*! \brief Queue a SLA event from the conference */
1409 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1410 struct ast_conference *conf)
1412 struct sla_station *station;
1413 struct sla_trunk_ref *trunk_ref = NULL;
1416 trunk_name = ast_strdupa(conf->confno);
1417 strsep(&trunk_name, "_");
1418 if (ast_strlen_zero(trunk_name)) {
1419 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1423 AST_RWLIST_RDLOCK(&sla_stations);
1424 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1425 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1426 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1432 AST_RWLIST_UNLOCK(&sla_stations);
1435 ast_debug(1, "Trunk not found for event!\n");
1439 sla_queue_event_full(type, trunk_ref, station, 1);
1442 /* Decrement reference counts, as incremented by find_conf() */
1443 static int dispose_conf(struct ast_conference *conf)
1448 AST_LIST_LOCK(&confs);
1449 if (ast_atomic_dec_and_test(&conf->refcount)) {
1450 /* Take the conference room number out of an inuse state */
1451 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1452 conf_map[confno_int] = 0;
1456 AST_LIST_UNLOCK(&confs);
1461 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1465 ast_channel_lock(chan);
1466 original_moh = ast_strdupa(chan->musicclass);
1467 ast_string_field_set(chan, musicclass, musicclass);
1468 ast_channel_unlock(chan);
1470 ast_moh_start(chan, original_moh, NULL);
1472 ast_channel_lock(chan);
1473 ast_string_field_set(chan, musicclass, original_moh);
1474 ast_channel_unlock(chan);
1477 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1479 struct ast_conf_user *user = NULL;
1480 struct ast_conf_user *usr = NULL;
1482 struct zt_confinfo ztc, ztc_empty;
1483 struct ast_frame *f;
1484 struct ast_channel *c;
1485 struct ast_frame fr;
1493 int musiconhold = 0;
1496 int currentmarked = 0;
1499 int menu_active = 0;
1500 int talkreq_manager = 0;
1501 int using_pseudo = 0;
1506 int announcement_played = 0;
1508 struct ast_dsp *dsp = NULL;
1509 struct ast_app *app;
1510 const char *agifile;
1511 const char *agifiledefault = "conf-background.agi";
1512 char meetmesecs[30] = "";
1513 char exitcontext[AST_MAX_CONTEXT] = "";
1514 char recordingtmp[AST_MAX_EXTENSION] = "";
1515 char members[10] = "";
1516 int dtmf, opt_waitmarked_timeout = 0;
1519 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1520 char *buf = __buf + AST_FRIENDLY_OFFSET;
1521 char *exitkeys = NULL;
1522 unsigned int calldurationlimit = 0;
1524 long play_warning = 0;
1525 long warning_freq = 0;
1526 const char *warning_sound = NULL;
1527 const char *end_sound = NULL;
1529 long time_left_ms = 0;
1530 struct timeval nexteventts = { 0, };
1532 int setusercount = 0;
1534 if (!(user = ast_calloc(1, sizeof(*user))))
1537 /* Possible timeout waiting for marked user */
1538 if ((confflags & CONFFLAG_WAITMARKED) &&
1539 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1540 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1541 (opt_waitmarked_timeout > 0)) {
1542 timeout = time(NULL) + opt_waitmarked_timeout;
1545 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1546 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1547 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1550 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1551 char *limit_str, *warning_str, *warnfreq_str;
1554 parse = optargs[OPT_ARG_DURATION_LIMIT];
1555 limit_str = strsep(&parse, ":");
1556 warning_str = strsep(&parse, ":");
1557 warnfreq_str = parse;
1559 timelimit = atol(limit_str);
1561 play_warning = atol(warning_str);
1563 warning_freq = atol(warnfreq_str);
1566 timelimit = play_warning = warning_freq = 0;
1567 warning_sound = NULL;
1568 } else if (play_warning > timelimit) {
1569 if (!warning_freq) {
1572 while (play_warning > timelimit)
1573 play_warning -= warning_freq;
1574 if (play_warning < 1)
1575 play_warning = warning_freq = 0;
1579 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE");
1580 warning_sound = var ? var : "timeleft";
1582 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE");
1583 end_sound = var ? var : NULL;
1585 /* undo effect of S(x) in case they are both used */
1586 calldurationlimit = 0;
1587 /* more efficient do it like S(x) does since no advanced opts */
1588 if (!play_warning && !end_sound && timelimit) {
1589 calldurationlimit = timelimit / 1000;
1590 timelimit = play_warning = warning_freq = 0;
1592 ast_debug(2, "Limit Data for this call:\n");
1593 ast_debug(2, "- timelimit = %ld\n", timelimit);
1594 ast_debug(2, "- play_warning = %ld\n", play_warning);
1595 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1596 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1597 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1602 if ((confflags & CONFFLAG_KEYEXIT)) {
1603 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1604 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1606 exitkeys = ast_strdupa("#"); /* Default */
1609 if (confflags & CONFFLAG_RECORDCONF) {
1610 if (!conf->recordingfilename) {
1611 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1612 if (!conf->recordingfilename) {
1613 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1614 conf->recordingfilename = ast_strdupa(recordingtmp);
1616 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1617 if (!conf->recordingformat) {
1618 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1619 conf->recordingformat = ast_strdupa(recordingtmp);
1621 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1622 conf->confno, conf->recordingfilename, conf->recordingformat);
1626 ast_mutex_lock(&conf->recordthreadlock);
1627 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1628 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1629 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1631 ztc.confno = conf->zapconf;
1632 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1633 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1634 ast_log(LOG_WARNING, "Error starting listen channel\n");
1635 ast_hangup(conf->lchan);
1638 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1641 ast_mutex_unlock(&conf->recordthreadlock);
1643 time(&user->jointime);
1645 user->timelimit = timelimit;
1646 user->play_warning = play_warning;
1647 user->warning_freq = warning_freq;
1648 user->warning_sound = warning_sound;
1649 user->end_sound = end_sound;
1651 if (calldurationlimit > 0) {
1652 time(&user->kicktime);
1653 user->kicktime = user->kicktime + calldurationlimit;
1656 if (ast_tvzero(user->start_time))
1657 user->start_time = ast_tvnow();
1658 time_left_ms = user->timelimit;
1660 if (user->timelimit) {
1661 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1662 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1665 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1666 /* Sorry, but this conference is locked! */
1667 if (!ast_streamfile(chan, "conf-locked", chan->language))
1668 ast_waitstream(chan, "");
1672 ast_mutex_lock(&conf->playlock);
1674 if (AST_LIST_EMPTY(&conf->userlist))
1677 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1679 if (rt_schedule && conf->maxusers)
1680 if (user->user_no > conf->maxusers) {
1681 /* Sorry, but this confernce has reached the participant limit! */
1682 if (!ast_streamfile(chan, "conf-full", chan->language))
1683 ast_waitstream(chan, "");
1687 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1690 user->userflags = confflags;
1691 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1694 ast_mutex_unlock(&conf->playlock);
1696 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1697 snprintf(user->namerecloc, sizeof(user->namerecloc),
1698 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1699 conf->confno, user->user_no);
1700 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1701 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1703 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1708 ast_mutex_lock(&conf->playlock);
1710 if (confflags & CONFFLAG_MARKEDUSER)
1711 conf->markedusers++;
1713 if (rt_log_members) {
1715 snprintf(members, sizeof(members), "%d", conf->users);
1716 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1720 /* This device changed state now - if this is the first user */
1721 if (conf->users == 1)
1722 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1724 ast_mutex_unlock(&conf->playlock);
1726 /* return the unique ID of the conference */
1727 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1729 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1730 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1731 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1732 else if (!ast_strlen_zero(chan->macrocontext))
1733 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1735 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1738 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1739 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1740 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1741 ast_waitstream(chan, "");
1742 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1743 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1744 ast_waitstream(chan, "");
1747 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1748 int keepplaying = 1;
1750 if (conf->users == 2) {
1751 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1752 res = ast_waitstream(chan, AST_DIGIT_ANY);
1753 ast_stopstream(chan);
1760 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1761 res = ast_waitstream(chan, AST_DIGIT_ANY);
1762 ast_stopstream(chan);
1769 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1775 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1776 res = ast_waitstream(chan, AST_DIGIT_ANY);
1777 ast_stopstream(chan);
1786 ast_indicate(chan, -1);
1788 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1789 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1793 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1794 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1798 retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1799 user->zapchannel = !retryzap;
1802 origfd = chan->fds[0];
1804 fd = open("/dev/zap/pseudo", O_RDWR);
1806 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1810 /* Make non-blocking */
1811 flags = fcntl(fd, F_GETFL);
1813 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1817 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1818 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1822 /* Setup buffering information */
1823 memset(&bi, 0, sizeof(bi));
1824 bi.bufsize = CONF_SIZE / 2;
1825 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1826 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1827 bi.numbufs = audio_buffers;
1828 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1829 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1834 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1835 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1841 /* XXX Make sure we're not running on a pseudo channel XXX */
1845 memset(&ztc, 0, sizeof(ztc));
1846 memset(&ztc_empty, 0, sizeof(ztc_empty));
1847 /* Check to see if we're in a conference... */
1849 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1850 ast_log(LOG_WARNING, "Error getting conference\n");
1855 /* Whoa, already in a conference... Retry... */
1857 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1862 memset(&ztc, 0, sizeof(ztc));
1863 /* Add us to the conference */
1865 ztc.confno = conf->zapconf;
1867 ast_mutex_lock(&conf->playlock);
1869 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1870 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1871 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1872 ast_waitstream(conf->chan, "");
1873 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1874 ast_waitstream(conf->chan, "");
1878 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
1879 ztc.confmode = ZT_CONF_CONF;
1880 else if (confflags & CONFFLAG_MONITOR)
1881 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1882 else if (confflags & CONFFLAG_TALKER)
1883 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1885 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1887 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1888 ast_log(LOG_WARNING, "Error setting conference\n");
1890 ast_mutex_unlock(&conf->playlock);
1893 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1896 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1901 "CallerIDnum: %s\r\n"
1902 "CallerIDname: %s\r\n",
1903 chan->name, chan->uniqueid, conf->confno,
1905 S_OR(user->chan->cid.cid_num, "<unknown>"),
1906 S_OR(user->chan->cid.cid_name, "<unknown>")
1911 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1913 if (!(confflags & CONFFLAG_QUIET))
1914 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1915 conf_play(chan, conf, ENTER);
1918 ast_mutex_unlock(&conf->playlock);
1920 conf_flush(fd, chan);
1922 if (confflags & CONFFLAG_AGI) {
1923 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1924 or use default filename of conf-background.agi */
1926 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1928 agifile = agifiledefault;
1930 if (user->zapchannel) {
1931 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1933 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1935 /* Find a pointer to the agi app and execute the script */
1936 app = pbx_findapp("agi");
1938 char *s = ast_strdupa(agifile);
1939 ret = pbx_exec(chan, app, s);
1941 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1944 if (user->zapchannel) {
1945 /* Remove CONFMUTE mode on Zap channel */
1947 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1950 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1951 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1953 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1955 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1956 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1960 int menu_was_active = 0;
1967 if (now.tv_sec % 60 == 0) {
1969 if (now.tv_sec > conf->endtime) {
1970 ast_verbose("Quitting time...\n");
1974 if (!announcement_played && conf->endalert) {
1975 if (now.tv_sec + conf->endalert > conf->endtime) {
1976 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1977 ast_waitstream(chan, "");
1978 ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
1979 if (!ast_streamfile(chan, "minutes", chan->language))
1980 ast_waitstream(chan, "");
1981 announcement_played = 1;
1992 if (user->kicktime && (user->kicktime <= now.tv_sec))
1996 if (user->timelimit) {
1997 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));
2055 if (timeout && now.tv_sec >= timeout)
2058 /* if we have just exited from the menu, and the user had a channel-driver
2059 volume adjustment, restore it
2061 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2062 set_talk_volume(user, user->listen.desired);
2064 menu_was_active = menu_active;
2066 currentmarked = conf->markedusers;
2067 if (!(confflags & CONFFLAG_QUIET) &&
2068 (confflags & CONFFLAG_MARKEDUSER) &&
2069 (confflags & CONFFLAG_WAITMARKED) &&
2071 if (currentmarked == 1 && conf->users > 1) {
2072 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2073 if (conf->users - 1 == 1) {
2074 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2075 ast_waitstream(chan, "");
2077 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2078 ast_waitstream(chan, "");
2081 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2082 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2083 ast_waitstream(chan, "");
2086 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 || c->monitor))) {
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 || c->monitor) ? 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;
2532 fr.samples = res / 2;
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));
2646 if (rt_log_members) {
2648 snprintf(members, sizeof(members), "%d", conf->users);
2649 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2651 if (confflags & CONFFLAG_MARKEDUSER)
2652 conf->markedusers--;
2654 /* Remove ourselves from the list */
2655 AST_LIST_REMOVE(&conf->userlist, user, list);
2657 /* Change any states */
2659 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2661 /* Return the number of seconds the user was in the conf */
2662 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2663 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2666 AST_LIST_UNLOCK(&confs);
2671 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2672 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2673 char *optargs[], int *too_early)
2675 struct ast_variable *var;
2676 struct ast_conference *cnf;
2680 /* Check first in the conference list */
2681 AST_LIST_LOCK(&confs);
2682 AST_LIST_TRAVERSE(&confs, cnf, list) {
2683 if (!strcmp(confno, cnf->confno))
2687 cnf->refcount += refcount;
2689 AST_LIST_UNLOCK(&confs);
2692 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2695 char currenttime[19] = "";
2696 char eatime[19] = "";
2697 char useropts[32] = "";
2698 char adminopts[32] = "";
2699 struct ast_tm tm, etm;
2700 struct timeval starttime = { .tv_sec = 0 }, endtime = { .tv_sec = 0 };
2706 now.tv_sec += fuzzystart;
2708 ast_localtime(&now, &tm, NULL);
2709 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2712 now.tv_sec += earlyalert;
2713 ast_localtime(&now, &etm, NULL);
2714 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2716 ast_copy_string(eatime, currenttime, sizeof(eatime));
2719 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2721 var = ast_load_realtime("meetme", "confno",
2722 confno, "starttime <= ", eatime, "endtime >= ",
2725 var = ast_load_realtime("meetme", "confno", confno, NULL);
2731 if (!strcasecmp(var->name, "pin")) {
2732 pin = ast_strdupa(var->value);
2733 } else if (!strcasecmp(var->name, "adminpin")) {
2734 pinadmin = ast_strdupa(var->value);
2735 } else if (!strcasecmp(var->name, "opts")) {
2736 ast_copy_string(useropts, var->value, sizeof(useropts));
2737 } else if (!strcasecmp(var->name, "maxusers")) {
2738 maxusers = atoi(var->value);
2739 } else if (!strcasecmp(var->name, "adminopts")) {
2740 ast_copy_string(adminopts, var->value, sizeof(adminopts));
2741 } else if (!strcasecmp(var->name, "endtime")) {
2746 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
2747 endtime = ast_mktime(&t.atm, NULL);
2748 } else if (!strcasecmp(var->name, "starttime")) {
2753 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
2754 starttime = ast_mktime(&t.atm, NULL);
2759 ast_variables_destroy(var);
2764 if (now.tv_sec + fuzzystart < starttime.tv_sec) {
2765 /* Announce that the caller is early and exit */
2766 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2767 ast_waitstream(chan, "");
2773 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2776 cnf->maxusers = maxusers;
2777 cnf->endalert = endalert;
2778 cnf->endtime = endtime.tv_sec;
2783 if (confflags && !cnf->chan &&
2784 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2785 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2786 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2787 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2790 if (confflags && !cnf->chan &&
2791 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2792 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2793 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2801 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2802 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2804 struct ast_config *cfg;
2805 struct ast_variable *var;
2806 struct ast_flags config_flags = { 0 };
2807 struct ast_conference *cnf;
2809 AST_DECLARE_APP_ARGS(args,
2810 AST_APP_ARG(confno);
2812 AST_APP_ARG(pinadmin);
2815 /* Check first in the conference list */
2816 ast_debug(1, "The requested confno is '%s'?\n", confno);
2817 AST_LIST_LOCK(&confs);
2818 AST_LIST_TRAVERSE(&confs, cnf, list) {
2819 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
2820 if (!strcmp(confno, cnf->confno))
2824 cnf->refcount += refcount;
2826 AST_LIST_UNLOCK(&confs);
2830 /* No need to parse meetme.conf */
2831 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2833 if (dynamic_pin[0] == 'q') {
2834 /* Query the user to enter a PIN */
2835 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2838 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2840 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2843 /* Check the config */
2844 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2846 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2849 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2850 if (strcasecmp(var->name, "conf"))
2853 if (!(parse = ast_strdupa(var->value)))
2856 AST_STANDARD_APP_ARGS(args, parse);
2857 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2858 if (!strcasecmp(args.confno, confno)) {
2859 /* Bingo it's a valid conference */
2860 cnf = build_conf(args.confno,
2862 S_OR(args.pinadmin, ""),
2863 make, dynamic, refcount, chan);
2868 ast_debug(1, "%s isn't a valid conference\n", confno);
2870 ast_config_destroy(cfg);
2872 } else if (dynamic_pin) {
2873 /* Correct for the user selecting 'D' instead of 'd' to have
2874 someone join into a conference that has already been created
2876 if (dynamic_pin[0] == 'q')
2877 dynamic_pin[0] = '\0';
2881 if (confflags && !cnf->chan &&
2882 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2883 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2884 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2885 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2888 if (confflags && !cnf->chan &&
2889 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2890 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2891 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2898 /*! \brief The MeetmeCount application */
2899 static int count_exec(struct ast_channel *chan, void *data)
2902 struct ast_conference *conf;
2906 AST_DECLARE_APP_ARGS(args,
2907 AST_APP_ARG(confno);
2908 AST_APP_ARG(varname);
2911 if (ast_strlen_zero(data)) {
2912 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2916 if (!(localdata = ast_strdupa(data)))
2919 AST_STANDARD_APP_ARGS(args, localdata);
2921 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2924 count = conf->users;
2930 if (!ast_strlen_zero(args.varname)) {
2931 /* have var so load it and exit */
2932 snprintf(val, sizeof(val), "%d", count);
2933 pbx_builtin_setvar_helper(chan, args.varname, val);
2935 if (chan->_state != AST_STATE_UP)
2937 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2943 /*! \brief The meetme() application */
2944 static int conf_exec(struct ast_channel *chan, void *data)
2947 char confno[MAX_CONFNUM] = "";
2950 struct ast_conference *cnf = NULL;
2951 struct ast_flags confflags = {0}, config_flags = { 0 };
2953 int empty = 0, empty_no_pin = 0;
2954 int always_prompt = 0;
2955 char *notdata, *info, the_pin[MAX_PIN] = "";
2956 AST_DECLARE_APP_ARGS(args,
2957 AST_APP_ARG(confno);
2958 AST_APP_ARG(options);
2961 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2963 if (ast_strlen_zero(data)) {
2970 if (chan->_state != AST_STATE_UP)
2973 info = ast_strdupa(notdata);
2975 AST_STANDARD_APP_ARGS(args, info);
2978 ast_copy_string(confno, args.confno, sizeof(confno));
2979 if (ast_strlen_zero(confno)) {
2985 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2988 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2989 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2990 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2991 strcpy(the_pin, "q");
2993 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2994 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2995 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
3003 struct ast_config *cfg;
3004 struct ast_variable *var;
3007 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
3008 if ((empty_no_pin) || (!dynamic)) {
3009 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
3011 var = ast_variable_browse(cfg, "rooms");
3013 if (!strcasecmp(var->name, "conf")) {
3014 char *stringp = ast_strdupa(var->value);
3016 char *confno_tmp = strsep(&stringp, "|,");
3019 /* For static: run through the list and see if this conference is empty */
3020 AST_LIST_LOCK(&confs);
3021 AST_LIST_TRAVERSE(&confs, cnf, list) {
3022 if (!strcmp(confno_tmp, cnf->confno)) {
3023 /* The conference exists, therefore it's not empty */
3028 AST_LIST_UNLOCK(&confs);
3030 /* At this point, we have a confno_tmp (static conference) that is empty */
3031 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
3032 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
3033 * Case 2: empty_no_pin and pin is blank (but not NULL)
3034 * Case 3: not empty_no_pin
3036 ast_copy_string(confno, confno_tmp, sizeof(confno));
3038 /* XXX the map is not complete (but we do have a confno) */
3046 ast_config_destroy(cfg);