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$")
46 #include "asterisk/zapata.h"
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/dsp.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/options.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65 #include "asterisk/astobj.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/dial.h"
68 #include "asterisk/causes.h"
73 #define CONFIG_FILE_NAME "meetme.conf"
74 #define SLA_CONFIG_FILE "sla.conf"
76 /*! each buffer is 20ms, so this is 640ms total */
77 #define DEFAULT_AUDIO_BUFFERS 32
79 /*! String format for scheduled conferences */
80 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
83 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
84 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
85 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
86 /*! User has requested to speak */
87 ADMINFLAG_T_REQUEST = (1 << 4),
90 #define MEETME_DELAYDETECTTALK 300
91 #define MEETME_DELAYDETECTENDTALK 1000
93 #define AST_FRAME_BITS 32
100 enum entrance_sound {
105 enum recording_state {
107 MEETME_RECORD_STARTED,
108 MEETME_RECORD_ACTIVE,
109 MEETME_RECORD_TERMINATE
112 #define CONF_SIZE 320
115 /*! user has admin access on the conference */
116 CONFFLAG_ADMIN = (1 << 0),
117 /*! If set the user can only receive audio from the conference */
118 CONFFLAG_MONITOR = (1 << 1),
119 /*! If set asterisk will exit conference when key defined in p() option is pressed */
120 CONFFLAG_KEYEXIT = (1 << 2),
121 /*! If set asterisk will provide a menu to the user when '*' is pressed */
122 CONFFLAG_STARMENU = (1 << 3),
123 /*! If set the use can only send audio to the conference */
124 CONFFLAG_TALKER = (1 << 4),
125 /*! If set there will be no enter or leave sounds */
126 CONFFLAG_QUIET = (1 << 5),
127 /*! If set, when user joins the conference, they will be told the number
128 * of users that are already in */
129 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
130 /*! Set to run AGI Script in Background */
131 CONFFLAG_AGI = (1 << 7),
132 /*! Set to have music on hold when user is alone in conference */
133 CONFFLAG_MOH = (1 << 8),
134 /*! If set the MeetMe will return if all marked with this flag left */
135 CONFFLAG_MARKEDEXIT = (1 << 9),
136 /*! If set, the MeetMe will wait until a marked user enters */
137 CONFFLAG_WAITMARKED = (1 << 10),
138 /*! If set, the MeetMe will exit to the specified context */
139 CONFFLAG_EXIT_CONTEXT = (1 << 11),
140 /*! If set, the user will be marked */
141 CONFFLAG_MARKEDUSER = (1 << 12),
142 /*! If set, user will be ask record name on entry of conference */
143 CONFFLAG_INTROUSER = (1 << 13),
144 /*! If set, the MeetMe will be recorded */
145 CONFFLAG_RECORDCONF = (1<< 14),
146 /*! If set, the user will be monitored if the user is talking or not */
147 CONFFLAG_MONITORTALKER = (1 << 15),
148 CONFFLAG_DYNAMIC = (1 << 16),
149 CONFFLAG_DYNAMICPIN = (1 << 17),
150 CONFFLAG_EMPTY = (1 << 18),
151 CONFFLAG_EMPTYNOPIN = (1 << 19),
152 CONFFLAG_ALWAYSPROMPT = (1 << 20),
153 /*! If set, won't speak the extra prompt when the first person
154 * enters the conference */
155 CONFFLAG_NOONLYPERSON = (1 << 22),
156 /*! If set, user will be asked to record name on entry of conference
158 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
159 /*! If set, the user will be initially self-muted */
160 CONFFLAG_STARTMUTED = (1 << 24),
161 /*! Pass DTMF through the conference */
162 CONFFLAG_PASS_DTMF = (1 << 25),
163 CONFFLAG_SLA_STATION = (1 << 26),
164 CONFFLAG_SLA_TRUNK = (1 << 27),
165 /*! If set, the user should continue in the dialplan if kicked out */
166 CONFFLAG_KICK_CONTINUE = (1 << 28),
167 CONFFLAG_DURATION_STOP = (1 << 29),
168 CONFFLAG_DURATION_LIMIT = (1 << 30),
172 OPT_ARG_WAITMARKED = 0,
173 OPT_ARG_EXITKEYS = 1,
174 OPT_ARG_DURATION_STOP = 2,
175 OPT_ARG_DURATION_LIMIT = 3,
176 OPT_ARG_ARRAY_SIZE = 4,
179 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
180 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
181 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
182 AST_APP_OPTION('b', CONFFLAG_AGI ),
183 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
184 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
185 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
186 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
187 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
188 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
189 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
190 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
191 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
192 AST_APP_OPTION('M', CONFFLAG_MOH ),
193 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
194 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
195 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
196 AST_APP_OPTION('q', CONFFLAG_QUIET ),
197 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
198 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
199 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
200 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
201 AST_APP_OPTION('t', CONFFLAG_TALKER ),
202 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
203 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
204 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
205 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
206 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
207 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
210 static const char *app = "MeetMe";
211 static const char *app2 = "MeetMeCount";
212 static const char *app3 = "MeetMeAdmin";
213 static const char *app4 = "MeetMeChannelAdmin";
214 static const char *slastation_app = "SLAStation";
215 static const char *slatrunk_app = "SLATrunk";
217 static const char *synopsis = "MeetMe conference bridge";
218 static const char *synopsis2 = "MeetMe participant count";
219 static const char *synopsis3 = "MeetMe conference Administration";
220 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
221 static const char *slastation_synopsis = "Shared Line Appearance Station";
222 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
224 /* Lookup RealTime conferences based on confno and current time */
225 static int rt_schedule;
226 static int fuzzystart;
227 static int earlyalert;
230 /* Log participant count to the RealTime backend */
231 static int rt_log_members;
233 static const char *descrip =
234 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
235 "conference. If the conference number is omitted, the user will be prompted\n"
236 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
237 "is specified, by pressing '#'.\n"
238 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
239 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
240 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
241 "The option string may contain zero or more of the following characters:\n"
242 " 'a' -- set admin mode\n"
243 " 'A' -- set marked mode\n"
244 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
245 " Default: conf-background.agi (Note: This does not work with\n"
246 " non-Zap channels in the same conference)\n"
247 " 'c' -- announce user(s) count on joining a conference\n"
248 " 'C' -- continue in dialplan when kicked out of conference\n"
249 " 'd' -- dynamically add conference\n"
250 " 'D' -- dynamically add conference, prompting for a PIN\n"
251 " 'e' -- select an empty conference\n"
252 " 'E' -- select an empty pinless conference\n"
253 " 'F' -- Pass DTMF through the conference.\n"
254 " 'i' -- announce user join/leave with review\n"
255 " 'I' -- announce user join/leave without review\n"
256 " 'l' -- set listen only mode (Listen only, no talking)\n"
257 " 'm' -- set initially muted\n"
258 " 'M' -- enable music on hold when the conference has a single caller\n"
259 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
260 " being muted, meaning (a) No encode is done on transmission and\n"
261 " (b) Received audio that is not registered as talking is omitted\n"
262 " causing no buildup in background noise\n"
264 " -- allow user to exit the conference by pressing '#' (default)\n"
265 " or any of the defined keys. If keys contain '*' this will override\n"
266 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
267 " 'P' -- always prompt for the pin even if it is specified\n"
268 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
269 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
270 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
271 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
273 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
274 " 't' -- set talk only mode. (Talk only, no listening)\n"
275 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
277 " -- wait until the marked user enters the conference\n"
278 " 'x' -- close the conference when last marked user exits\n"
279 " 'X' -- allow user to exit the conference by entering a valid single\n"
280 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
281 " if that variable is not defined.\n"
282 " '1' -- do not play message when first person enters\n"
283 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
284 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
285 " left. Repeat the warning every 'z' ms. The following special\n"
286 " variables can be used with this option:\n"
287 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
288 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
289 " The default is to say the time remaining.\n"
292 static const char *descrip2 =
293 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
294 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
295 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
296 "the channel, unless priority n+1 exists, in which case priority progress will\n"
298 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
300 static const char *descrip3 =
301 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
302 " 'e' -- Eject last user that joined\n"
303 " 'k' -- Kick one user out of conference\n"
304 " 'K' -- Kick all users out of conference\n"
305 " 'l' -- Unlock conference\n"
306 " 'L' -- Lock conference\n"
307 " 'm' -- Unmute one user\n"
308 " 'M' -- Mute one user\n"
309 " 'n' -- Unmute all users in the conference\n"
310 " 'N' -- Mute all non-admin users in the conference\n"
311 " 'r' -- Reset one user's volume settings\n"
312 " 'R' -- Reset all users volume settings\n"
313 " 's' -- Lower entire conference speaking volume\n"
314 " 'S' -- Raise entire conference speaking volume\n"
315 " 't' -- Lower one user's talk volume\n"
316 " 'T' -- Raise one user's talk volume\n"
317 " 'u' -- Lower one user's listen volume\n"
318 " 'U' -- Raise one user's listen volume\n"
319 " 'v' -- Lower entire conference listening volume\n"
320 " 'V' -- Raise entire conference listening volume\n"
323 static const char *descrip4 =
324 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
325 "channel in any coference.\n"
326 " 'k' -- Kick the specified user out of the conference he is in\n"
327 " 'm' -- Unmute the specified user\n"
328 " 'M' -- Mute the specified user\n"
331 static const char *slastation_desc =
332 " SLAStation(station):\n"
333 "This application should be executed by an SLA station. The argument depends\n"
334 "on how the call was initiated. If the phone was just taken off hook, then\n"
335 "the argument \"station\" should be just the station name. If the call was\n"
336 "initiated by pressing a line key, then the station name should be preceded\n"
337 "by an underscore and the trunk name associated with that line button.\n"
338 "For example: \"station1_line1\"."
339 " On exit, this application will set the variable SLASTATION_STATUS to\n"
340 "one of the following values:\n"
341 " FAILURE | CONGESTION | SUCCESS\n"
344 static const char *slatrunk_desc =
345 " SLATrunk(trunk):\n"
346 "This application should be executed by an SLA trunk on an inbound call.\n"
347 "The channel calling this application should correspond to the SLA trunk\n"
348 "with the name \"trunk\" that is being passed as an argument.\n"
349 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
350 "one of the following values:\n"
351 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\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);
1463 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1465 struct ast_conf_user *user = NULL;
1466 struct ast_conf_user *usr = NULL;
1468 struct zt_confinfo ztc, ztc_empty;
1469 struct ast_frame *f;
1470 struct ast_channel *c;
1471 struct ast_frame fr;
1479 int musiconhold = 0;
1482 int currentmarked = 0;
1485 int menu_active = 0;
1486 int talkreq_manager = 0;
1487 int using_pseudo = 0;
1492 int announcement_played = 0;
1494 struct ast_dsp *dsp=NULL;
1495 struct ast_app *app;
1496 const char *agifile;
1497 const char *agifiledefault = "conf-background.agi";
1498 char meetmesecs[30] = "";
1499 char exitcontext[AST_MAX_CONTEXT] = "";
1500 char recordingtmp[AST_MAX_EXTENSION] = "";
1501 char members[10] = "";
1502 int dtmf, opt_waitmarked_timeout = 0;
1505 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1506 char *buf = __buf + AST_FRIENDLY_OFFSET;
1507 char *exitkeys = NULL;
1509 unsigned int calldurationlimit = 0;
1511 long play_warning = 0;
1512 long warning_freq = 0;
1513 const char *warning_sound = NULL;
1514 const char *end_sound = NULL;
1516 long time_left_ms = 0;
1517 struct timeval nexteventts = { 0, };
1520 if (!(user = ast_calloc(1, sizeof(*user))))
1523 /* Possible timeout waiting for marked user */
1524 if ((confflags & CONFFLAG_WAITMARKED) &&
1525 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1526 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1527 (opt_waitmarked_timeout > 0)) {
1528 timeout = time(NULL) + opt_waitmarked_timeout;
1531 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1532 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1533 if (option_verbose > 2)
1534 ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit);
1537 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1538 char *limit_str, *warning_str, *warnfreq_str;
1541 parse = optargs[OPT_ARG_DURATION_LIMIT];
1542 limit_str = strsep(&parse, ":");
1543 warning_str = strsep(&parse, ":");
1544 warnfreq_str = parse;
1546 timelimit = atol(limit_str);
1548 play_warning = atol(warning_str);
1550 warning_freq = atol(warnfreq_str);
1553 timelimit = play_warning = warning_freq = 0;
1554 warning_sound = NULL;
1555 } else if (play_warning > timelimit) {
1556 if (!warning_freq) {
1559 while (play_warning > timelimit)
1560 play_warning -= warning_freq;
1561 if (play_warning < 1)
1562 play_warning = warning_freq = 0;
1566 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_WARNING_FILE");
1567 warning_sound = var ? var : "timeleft";
1569 var = pbx_builtin_getvar_helper(chan,"CONF_LIMIT_TIMEOUT_FILE");
1570 end_sound = var ? var : NULL;
1572 /* undo effect of S(x) in case they are both used */
1573 calldurationlimit = 0;
1574 /* more efficient do it like S(x) does since no advanced opts */
1575 if (!play_warning && !end_sound && timelimit) {
1576 calldurationlimit = timelimit / 1000;
1577 timelimit = play_warning = warning_freq = 0;
1579 ast_debug(2, "Limit Data for this call:\n");
1580 ast_debug(2, "- timelimit = %ld\n", timelimit);
1581 ast_debug(2, "- play_warning = %ld\n", play_warning);
1582 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1583 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1584 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1589 if ((confflags & CONFFLAG_KEYEXIT)) {
1590 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1591 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1593 exitkeys = ast_strdupa("#"); /* Default */
1596 if (confflags & CONFFLAG_RECORDCONF) {
1597 if (!conf->recordingfilename) {
1598 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1599 if (!conf->recordingfilename) {
1600 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1601 conf->recordingfilename = ast_strdupa(recordingtmp);
1603 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1604 if (!conf->recordingformat) {
1605 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
1606 conf->recordingformat = ast_strdupa(recordingtmp);
1608 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1609 conf->confno, conf->recordingfilename, conf->recordingformat);
1613 ast_mutex_lock(&conf->recordthreadlock);
1614 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1615 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1616 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1618 ztc.confno = conf->zapconf;
1619 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1620 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1621 ast_log(LOG_WARNING, "Error starting listen channel\n");
1622 ast_hangup(conf->lchan);
1625 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1628 ast_mutex_unlock(&conf->recordthreadlock);
1630 time(&user->jointime);
1632 user->timelimit = timelimit;
1633 user->play_warning = play_warning;
1634 user->warning_freq = warning_freq;
1635 user->warning_sound = warning_sound;
1636 user->end_sound = end_sound;
1638 if (calldurationlimit > 0) {
1639 time(&user->kicktime);
1640 user->kicktime = user->kicktime + calldurationlimit;
1643 if (ast_tvzero(user->start_time))
1644 user->start_time = ast_tvnow();
1645 time_left_ms = user->timelimit;
1647 if (user->timelimit) {
1648 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1649 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1652 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1653 /* Sorry, but this conference is locked! */
1654 if (!ast_streamfile(chan, "conf-locked", chan->language))
1655 ast_waitstream(chan, "");
1659 if (confflags & CONFFLAG_MARKEDUSER)
1660 conf->markedusers++;
1662 ast_mutex_lock(&conf->playlock);
1664 if (AST_LIST_EMPTY(&conf->userlist))
1667 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1669 if (rt_schedule && conf->maxusers)
1670 if (user->user_no > conf->maxusers){
1671 /* Sorry, but this confernce has reached the participant limit! */
1672 if (!ast_streamfile(chan, "conf-full", chan->language))
1673 ast_waitstream(chan, "");
1677 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1680 user->userflags = confflags;
1681 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1684 if (rt_log_members) {
1686 snprintf(members, sizeof(members), "%d", conf->users);
1687 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1689 /* This device changed state now - if this is the first user */
1690 if (conf->users == 1)
1691 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1693 ast_mutex_unlock(&conf->playlock);
1695 /* return the unique ID of the conference */
1696 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1698 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1699 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1700 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1701 else if (!ast_strlen_zero(chan->macrocontext))
1702 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1704 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1707 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1708 snprintf(user->namerecloc, sizeof(user->namerecloc),
1709 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1710 conf->confno, user->user_no);
1711 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1712 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1714 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1719 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1720 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1721 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1722 ast_waitstream(chan, "");
1723 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1724 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1725 ast_waitstream(chan, "");
1728 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1729 int keepplaying = 1;
1731 if (conf->users == 2) {
1732 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1733 res = ast_waitstream(chan, AST_DIGIT_ANY);
1734 ast_stopstream(chan);
1741 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1742 res = ast_waitstream(chan, AST_DIGIT_ANY);
1743 ast_stopstream(chan);
1750 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1756 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1757 res = ast_waitstream(chan, AST_DIGIT_ANY);
1758 ast_stopstream(chan);
1767 ast_indicate(chan, -1);
1769 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1770 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1774 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1775 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1779 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->audiohooks ? 1 : 0);
1780 user->zapchannel = !retryzap;
1783 origfd = chan->fds[0];
1785 fd = open("/dev/zap/pseudo", O_RDWR);
1787 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1791 /* Make non-blocking */
1792 flags = fcntl(fd, F_GETFL);
1794 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1798 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1799 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1803 /* Setup buffering information */
1804 memset(&bi, 0, sizeof(bi));
1805 bi.bufsize = CONF_SIZE/2;
1806 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1807 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1808 bi.numbufs = audio_buffers;
1809 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1810 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1815 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1816 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1822 /* XXX Make sure we're not running on a pseudo channel XXX */
1826 memset(&ztc, 0, sizeof(ztc));
1827 memset(&ztc_empty, 0, sizeof(ztc_empty));
1828 /* Check to see if we're in a conference... */
1830 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1831 ast_log(LOG_WARNING, "Error getting conference\n");
1836 /* Whoa, already in a conference... Retry... */
1838 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
1843 memset(&ztc, 0, sizeof(ztc));
1844 /* Add us to the conference */
1846 ztc.confno = conf->zapconf;
1848 ast_mutex_lock(&conf->playlock);
1850 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1851 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1852 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1853 ast_waitstream(conf->chan, "");
1854 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1855 ast_waitstream(conf->chan, "");
1859 if (confflags & CONFFLAG_MONITOR)
1860 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1861 else if (confflags & CONFFLAG_TALKER)
1862 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1864 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1866 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1867 ast_log(LOG_WARNING, "Error setting conference\n");
1869 ast_mutex_unlock(&conf->playlock);
1872 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1875 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1880 chan->name, chan->uniqueid, conf->confno, user->user_no);
1884 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1886 if (!(confflags & CONFFLAG_QUIET))
1887 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1888 conf_play(chan, conf, ENTER);
1891 ast_mutex_unlock(&conf->playlock);
1893 conf_flush(fd, chan);
1895 if (confflags & CONFFLAG_AGI) {
1896 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1897 or use default filename of conf-background.agi */
1899 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1901 agifile = agifiledefault;
1903 if (user->zapchannel) {
1904 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1906 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1908 /* Find a pointer to the agi app and execute the script */
1909 app = pbx_findapp("agi");
1911 char *s = ast_strdupa(agifile);
1912 ret = pbx_exec(chan, app, s);
1914 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1917 if (user->zapchannel) {
1918 /* Remove CONFMUTE mode on Zap channel */
1920 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1923 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1924 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1926 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1928 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1929 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1933 int menu_was_active = 0;
1939 char currenttime[32];
1943 if (now.tv_sec % 60 == 0) {
1945 ast_localtime(&now, &tm, NULL);
1946 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1947 if (strcmp(currenttime, conf->endtime) > 0){
1948 ast_verbose("Quitting time...\n");
1952 if (!announcement_played && conf->endalert) {
1953 now.tv_sec += conf->endalert;
1954 ast_localtime(&now, &tm, NULL);
1955 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1956 if (strcmp(currenttime, conf->endtime) > 0){
1957 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
1958 ast_waitstream(chan, "");
1959 ast_say_digits(chan, conf->endalert / 60, "", chan->language);
1960 if (!ast_streamfile(chan, "minutes", chan->language))
1961 ast_waitstream(chan, "");
1962 announcement_played = 1;
1974 time(&myt); /* get current time */
1975 if (user->kicktime && (user->kicktime < myt))
1979 if (user->timelimit) {
1981 int minutes = 0, seconds = 0, remain = 0;
1984 to = ast_tvdiff_ms(nexteventts, now);
1987 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
1988 if (time_left_ms < to)
1991 if (time_left_ms <= 0) {
1992 if (user->end_sound){
1993 res = ast_streamfile(chan, user->end_sound, chan->language);
1994 res = ast_waitstream(chan, "");
2000 if (time_left_ms >= 5000) {
2002 remain = (time_left_ms + 500) / 1000;
2003 if (remain / 60 >= 1) {
2004 minutes = remain / 60;
2005 seconds = remain % 60;
2010 /* force the time left to round up if appropriate */
2011 if (user->warning_sound && user->play_warning){
2012 if (!strcmp(user->warning_sound,"timeleft")) {
2014 res = ast_streamfile(chan, "vm-youhave", chan->language);
2015 res = ast_waitstream(chan, "");
2017 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2018 res = ast_streamfile(chan, "queue-minutes", chan->language);
2019 res = ast_waitstream(chan, "");
2022 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2023 res = ast_streamfile(chan, "queue-seconds", chan->language);
2024 res = ast_waitstream(chan, "");
2027 res = ast_streamfile(chan, user->warning_sound, chan->language);
2028 res = ast_waitstream(chan, "");
2032 if (user->warning_freq)
2033 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2035 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2039 if (timeout && time(NULL) >= timeout)
2042 /* if we have just exited from the menu, and the user had a channel-driver
2043 volume adjustment, restore it
2045 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2046 set_talk_volume(user, user->listen.desired);
2048 menu_was_active = menu_active;
2050 currentmarked = conf->markedusers;
2051 if (!(confflags & CONFFLAG_QUIET) &&
2052 (confflags & CONFFLAG_MARKEDUSER) &&
2053 (confflags & CONFFLAG_WAITMARKED) &&
2055 if (currentmarked == 1 && conf->users > 1) {
2056 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2057 if (conf->users - 1 == 1) {
2058 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2059 ast_waitstream(chan, "");
2061 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2062 ast_waitstream(chan, "");
2065 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2066 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2067 ast_waitstream(chan, "");
2070 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2073 /* Update the struct with the actual confflags */
2074 user->userflags = confflags;
2076 if (confflags & CONFFLAG_WAITMARKED) {
2077 if(currentmarked == 0) {
2078 if (lastmarked != 0) {
2079 if (!(confflags & CONFFLAG_QUIET))
2080 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2081 ast_waitstream(chan, "");
2082 if (confflags & CONFFLAG_MARKEDEXIT) {
2083 if (confflags & CONFFLAG_KICK_CONTINUE)
2087 ztc.confmode = ZT_CONF_CONF;
2088 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2089 ast_log(LOG_WARNING, "Error setting conference\n");
2095 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
2096 ast_moh_start(chan, NULL, NULL);
2099 } else if(currentmarked >= 1 && lastmarked == 0) {
2100 /* Marked user entered, so cancel timeout */
2102 if (confflags & CONFFLAG_MONITOR)
2103 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
2104 else if (confflags & CONFFLAG_TALKER)
2105 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
2107 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
2108 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2109 ast_log(LOG_WARNING, "Error setting conference\n");
2113 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2117 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2118 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2119 ast_waitstream(chan, "");
2120 conf_play(chan, conf, ENTER);
2125 /* trying to add moh for single person conf */
2126 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2127 if (conf->users == 1) {
2128 if (musiconhold == 0) {
2129 ast_moh_start(chan, NULL, NULL);
2140 /* Leave if the last marked user left */
2141 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2142 if (confflags & CONFFLAG_KICK_CONTINUE)
2149 /* Check if my modes have changed */
2151 /* If I should be muted but am still talker, mute me */
2152 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
2153 ztc.confmode ^= ZT_CONF_TALKER;
2154 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2155 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2160 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2166 chan->name, chan->uniqueid, conf->confno, user->user_no);
2169 /* If I should be un-muted but am not talker, un-mute me */
2170 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
2171 ztc.confmode |= ZT_CONF_TALKER;
2172 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2173 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2178 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2184 chan->name, chan->uniqueid, conf->confno, user->user_no);
2187 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2188 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2189 talkreq_manager = 1;
2191 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2197 chan->name, chan->uniqueid, conf->confno, user->user_no);
2201 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2202 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2203 talkreq_manager = 0;
2204 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2210 chan->name, chan->uniqueid, conf->confno, user->user_no);
2213 /* If I have been kicked, exit the conference */
2214 if (user->adminflags & ADMINFLAG_KICKME) {
2215 //You have been kicked.
2216 if (!(confflags & CONFFLAG_QUIET) &&
2217 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2218 ast_waitstream(chan, "");
2224 /* Perform an extra hangup check just in case */
2225 if (ast_check_hangup(chan))
2229 if (c->fds[0] != origfd || (user->zapchannel && c->audiohooks)) {
2231 /* Kill old pseudo */
2235 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2236 retryzap = (strcasecmp(c->tech->type, "Zap") || c->audiohooks ? 1 : 0);
2237 user->zapchannel = !retryzap;
2240 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2241 f = ast_read_noaudio(c);
2246 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2247 if (user->talk.actual)
2248 ast_frame_adjust_volume(f, user->talk.actual);
2250 if (!(confflags & CONFFLAG_MONITOR)) {
2253 if (user->talking == -1)
2256 res = ast_dsp_silence(dsp, f, &totalsilence);
2257 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2259 if (confflags & CONFFLAG_MONITORTALKER)
2260 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2266 chan->name, chan->uniqueid, conf->confno, user->user_no);
2268 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2270 if (confflags & CONFFLAG_MONITORTALKER)
2271 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2277 chan->name, chan->uniqueid, conf->confno, user->user_no);
2281 /* Absolutely do _not_ use careful_write here...
2282 it is important that we read data from the channel
2283 as fast as it arrives, and feed it into the conference.
2284 The buffering in the pseudo channel will take care of any
2285 timing differences, unless they are so drastic as to lose
2286 audio frames (in which case carefully writing would only
2287 have delayed the audio even further).
2289 /* As it turns out, we do want to use careful write. We just
2290 don't want to block, but we do want to at least *try*
2291 to write out all the samples.
2294 careful_write(fd, f->data, f->datalen, 0);
2296 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
2299 if (confflags & CONFFLAG_PASS_DTMF)
2300 conf_queue_dtmf(conf, user, f);
2302 tmp[0] = f->subclass;
2304 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
2305 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
2310 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
2312 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2315 exitkey[0] = f->subclass;
2318 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
2320 if (confflags & CONFFLAG_PASS_DTMF)
2321 conf_queue_dtmf(conf, user, f);
2325 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2326 if (confflags & CONFFLAG_PASS_DTMF)
2327 conf_queue_dtmf(conf, user, f);
2328 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
2329 ast_log(LOG_WARNING, "Error setting conference\n");
2335 /* if we are entering the menu, and the user has a channel-driver
2336 volume adjustment, clear it
2338 if (!menu_active && user->talk.desired && !user->talk.actual)
2339 set_talk_volume(user, 0);
2344 if ((confflags & CONFFLAG_ADMIN)) {
2348 /* Record this sound! */
2349 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2350 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2351 ast_stopstream(chan);
2358 case '1': /* Un/Mute */
2361 /* for admin, change both admin and use flags */
2362 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2363 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2365 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2367 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2368 if (!ast_streamfile(chan, "conf-muted", chan->language))
2369 ast_waitstream(chan, "");
2371 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2372 ast_waitstream(chan, "");
2375 case '2': /* Un/Lock the Conference */
2379 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2380 ast_waitstream(chan, "");
2383 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2384 ast_waitstream(chan, "");
2387 case '3': /* Eject last user */
2389 usr = AST_LIST_LAST(&conf->userlist);
2390 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2391 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2392 ast_waitstream(chan, "");
2394 usr->adminflags |= ADMINFLAG_KICKME;
2395 ast_stopstream(chan);
2398 tweak_listen_volume(user, VOL_DOWN);
2401 tweak_listen_volume(user, VOL_UP);
2404 tweak_talk_volume(user, VOL_DOWN);
2410 tweak_talk_volume(user, VOL_UP);
2414 /* Play an error message! */
2415 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2416 ast_waitstream(chan, "");
2424 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2425 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2426 ast_stopstream(chan);
2433 case '1': /* Un/Mute */
2436 /* user can only toggle the self-muted state */
2437 user->adminflags ^= ADMINFLAG_SELFMUTED;
2439 /* they can't override the admin mute state */
2440 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2441 if (!ast_streamfile(chan, "conf-muted", chan->language))
2442 ast_waitstream(chan, "");
2444 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2445 ast_waitstream(chan, "");
2450 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2451 user->adminflags |= ADMINFLAG_T_REQUEST;
2453 if (user->adminflags & ADMINFLAG_T_REQUEST)
2454 if (!ast_streamfile(chan, "beep", chan->language))
2455 ast_waitstream(chan, "");
2458 tweak_listen_volume(user, VOL_DOWN);
2461 tweak_listen_volume(user, VOL_UP);
2464 tweak_talk_volume(user, VOL_DOWN);
2470 tweak_talk_volume(user, VOL_UP);
2474 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2475 ast_waitstream(chan, "");
2481 ast_moh_start(chan, NULL, NULL);
2483 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2484 ast_log(LOG_WARNING, "Error setting conference\n");
2490 conf_flush(fd, chan);
2491 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2492 && confflags & CONFFLAG_PASS_DTMF) {
2493 conf_queue_dtmf(conf, user, f);
2494 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2495 switch (f->subclass) {
2496 case AST_CONTROL_HOLD:
2497 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2502 } else if (f->frametype == AST_FRAME_NULL) {
2503 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2506 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2507 chan->name, f->frametype, f->subclass);
2510 } else if (outfd > -1) {
2511 res = read(outfd, buf, CONF_SIZE);
2513 memset(&fr, 0, sizeof(fr));
2514 fr.frametype = AST_FRAME_VOICE;
2515 fr.subclass = AST_FORMAT_SLINEAR;
2519 fr.offset = AST_FRIENDLY_OFFSET;
2520 if ( !user->listen.actual &&
2521 ((confflags & CONFFLAG_MONITOR) ||
2522 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2523 (!user->talking)) ) {
2525 for (index=0;index<AST_FRAME_BITS;index++)
2526 if (chan->rawwriteformat & (1 << index))
2528 if (index >= AST_FRAME_BITS)
2529 goto bailoutandtrynormal;
2530 ast_mutex_lock(&conf->listenlock);
2531 if (!conf->transframe[index]) {
2532 if (conf->origframe) {
2533 if (!conf->transpath[index])
2534 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2535 if (conf->transpath[index]) {
2536 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2537 if (!conf->transframe[index])
2538 conf->transframe[index] = &ast_null_frame;
2542 if (conf->transframe[index]) {
2543 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2544 if (ast_write(chan, conf->transframe[index]))
2545 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2548 ast_mutex_unlock(&conf->listenlock);
2549 goto bailoutandtrynormal;
2551 ast_mutex_unlock(&conf->listenlock);
2553 bailoutandtrynormal:
2554 if (user->listen.actual)
2555 ast_frame_adjust_volume(&fr, user->listen.actual);
2556 if (ast_write(chan, &fr) < 0) {
2557 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2561 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2563 lastmarked = currentmarked;
2573 /* Take out of conference */
2577 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2578 ast_log(LOG_WARNING, "Error setting conference\n");
2582 reset_volumes(user);
2584 AST_LIST_LOCK(&confs);
2585 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2586 conf_play(chan, conf, LEAVE);
2588 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2589 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2590 if ((conf->chan) && (conf->users > 1)) {
2591 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2592 ast_waitstream(conf->chan, "");
2593 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2594 ast_waitstream(conf->chan, "");
2596 ast_filedelete(user->namerecloc, NULL);
2599 AST_LIST_UNLOCK(&confs);
2602 AST_LIST_LOCK(&confs);
2607 if (user->user_no) { /* Only cleanup users who really joined! */
2609 hr = (now.tv_sec - user->jointime) / 3600;
2610 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2611 sec = (now.tv_sec - user->jointime) % 60;
2614 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2619 "CallerIDNum: %s\r\n"
2620 "CallerIDName: %s\r\n"
2621 "Duration: %ld\r\n",
2622 chan->name, chan->uniqueid, conf->confno,
2624 S_OR(user->chan->cid.cid_num, "<unknown>"),
2625 S_OR(user->chan->cid.cid_name, "<unknown>"),
2626 (long)(now.tv_sec - user->jointime));
2630 if (rt_log_members){
2632 snprintf(members, sizeof(members), "%d", conf->users);
2633 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2635 if (confflags & CONFFLAG_MARKEDUSER)
2636 conf->markedusers--;
2637 /* Remove ourselves from the list */
2638 AST_LIST_REMOVE(&conf->userlist, user, list);
2640 /* Change any states */
2642 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2644 /* Return the number of seconds the user was in the conf */
2645 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2646 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2649 AST_LIST_UNLOCK(&confs);
2654 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2655 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2656 char *optargs[], int *too_early)
2658 struct ast_variable *var;
2659 struct ast_conference *cnf;
2663 /* Check first in the conference list */
2664 AST_LIST_LOCK(&confs);
2665 AST_LIST_TRAVERSE(&confs, cnf, list) {
2666 if (!strcmp(confno, cnf->confno))
2670 cnf->refcount += refcount;
2672 AST_LIST_UNLOCK(&confs);
2675 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2678 char currenttime[19] = "";
2679 char startTime[19] = "";
2680 char endtime[19] = "";
2681 char eatime[19] = "";
2682 char userOpts[32] = "";
2683 char adminOpts[32] = "";
2684 struct ast_tm tm, etm;
2690 now.tv_sec += fuzzystart;
2692 ast_localtime(&now, &tm, NULL);
2693 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2696 now.tv_sec += earlyalert;
2697 ast_localtime(&now, &etm, NULL);
2698 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2700 ast_copy_string(eatime, currenttime, sizeof(eatime));
2703 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2705 var = ast_load_realtime("meetme", "confno",
2706 confno, "startTime<= ", eatime, "endtime>= ",
2709 var = ast_load_realtime("meetme", "confno", confno, NULL);
2715 if (!strcasecmp(var->name, "pin")) {
2716 pin = ast_strdupa(var->value);
2717 } else if (!strcasecmp(var->name, "adminpin")) {
2718 pinadmin = ast_strdupa(var->value);
2719 } else if (!strcasecmp(var->name, "opts")) {
2720 ast_copy_string(userOpts, var->value, sizeof(userOpts));
2721 } else if (!strcasecmp(var->name, "maxusers")) {
2722 maxusers = atoi(var->value);
2723 } else if (!strcasecmp(var->name, "adminopts")) {
2724 ast_copy_string(adminOpts, var->value, sizeof(adminOpts));
2725 } else if (!strcasecmp(var->name, "endtime")) {
2726 ast_copy_string(endtime, var->value, sizeof(endtime));
2727 } else if (!strcasecmp(var->name, "starttime")) {
2728 ast_copy_string(startTime, var->value, sizeof(startTime));
2733 ast_variables_destroy(var);
2736 if (strcmp(startTime, currenttime) > 0) {
2737 /* Announce that the caller is early and exit */
2738 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2739 ast_waitstream(chan, "");
2745 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2748 cnf->maxusers = maxusers;
2749 cnf->endalert = endalert;
2750 ast_copy_string(cnf->endtime, endtime, sizeof(cnf->endtime));
2755 if (confflags && !cnf->chan &&
2756 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2757 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2758 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2759 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2762 if (confflags && !cnf->chan &&
2763 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2764 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2765 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2773 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2774 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2776 struct ast_config *cfg;
2777 struct ast_variable *var;
2778 struct ast_flags config_flags = { 0 };
2779 struct ast_conference *cnf;
2781 AST_DECLARE_APP_ARGS(args,
2782 AST_APP_ARG(confno);
2784 AST_APP_ARG(pinadmin);
2787 /* Check first in the conference list */
2788 ast_debug(1,"The requested confno is '%s'?\n", confno);
2789 AST_LIST_LOCK(&confs);
2790 AST_LIST_TRAVERSE(&confs, cnf, list) {
2791 ast_debug(3,"Does conf %s match %s?\n", confno, cnf->confno);
2792 if (!strcmp(confno, cnf->confno))
2796 cnf->refcount += refcount;
2798 AST_LIST_UNLOCK(&confs);
2802 /* No need to parse meetme.conf */
2803 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2805 if (dynamic_pin[0] == 'q') {
2806 /* Query the user to enter a PIN */
2807 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2810 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2812 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2815 /* Check the config */
2816 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2818 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2821 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2822 if (strcasecmp(var->name, "conf"))
2825 if (!(parse = ast_strdupa(var->value)))
2828 AST_STANDARD_APP_ARGS(args, parse);
2829 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2830 if (!strcasecmp(args.confno, confno)) {
2831 /* Bingo it's a valid conference */
2832 cnf = build_conf(args.confno,
2834 S_OR(args.pinadmin, ""),
2835 make, dynamic, refcount, chan);
2840 ast_debug(1, "%s isn't a valid conference\n", confno);
2842 ast_config_destroy(cfg);
2844 } else if (dynamic_pin) {
2845 /* Correct for the user selecting 'D' instead of 'd' to have
2846 someone join into a conference that has already been created
2848 if (dynamic_pin[0] == 'q')
2849 dynamic_pin[0] = '\0';
2853 if (confflags && !cnf->chan &&
2854 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2855 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2856 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2857 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2860 if (confflags && !cnf->chan &&
2861 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2862 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2863 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2870 /*! \brief The MeetmeCount application */
2871 static int count_exec(struct ast_channel *chan, void *data)
2874 struct ast_conference *conf;
2878 AST_DECLARE_APP_ARGS(args,
2879 AST_APP_ARG(confno);
2880 AST_APP_ARG(varname);
2883 if (ast_strlen_zero(data)) {
2884 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2888 if (!(localdata = ast_strdupa(data)))
2891 AST_STANDARD_APP_ARGS(args, localdata);
2893 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2896 count = conf->users;
2902 if (!ast_strlen_zero(args.varname)){
2903 /* have var so load it and exit */
2904 snprintf(val, sizeof(val), "%d",count);
2905 pbx_builtin_setvar_helper(chan, args.varname, val);
2907 if (chan->_state != AST_STATE_UP)
2909 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2915 /*! \brief The meetme() application */
2916 static int conf_exec(struct ast_channel *chan, void *data)
2919 char confno[MAX_CONFNUM] = "";
2922 struct ast_conference *cnf = NULL;
2923 struct ast_flags confflags = {0}, config_flags = { 0 };
2925 int empty = 0, empty_no_pin = 0;
2926 int always_prompt = 0;
2927 char *notdata, *info, the_pin[MAX_PIN] = "";
2928 AST_DECLARE_APP_ARGS(args,
2929 AST_APP_ARG(confno);
2930 AST_APP_ARG(options);
2933 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2935 if (ast_strlen_zero(data)) {
2942 if (chan->_state != AST_STATE_UP)
2945 info = ast_strdupa(notdata);
2947 AST_STANDARD_APP_ARGS(args, info);
2950 ast_copy_string(confno, args.confno, sizeof(confno));
2951 if (ast_strlen_zero(confno)) {
2957 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2960 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2961 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2962 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2963 strcpy(the_pin, "q");
2965 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2966 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2967 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2975 struct ast_config *cfg;
2976 struct ast_variable *var;
2979 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2980 if ((empty_no_pin) || (!dynamic)) {
2981 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2983 var = ast_variable_browse(cfg, "rooms");
2985 if (!strcasecmp(var->name, "conf")) {
2986 char *stringp = ast_strdupa(var->value);
2988 char *confno_tmp = strsep(&stringp, "|,");
2991 /* For static: run through the list and see if this conference is empty */
2992 AST_LIST_LOCK(&confs);
2993 AST_LIST_TRAVERSE(&confs, cnf, list) {
2994 if (!strcmp(confno_tmp, cnf->confno)) {
2995 /* The conference exists, therefore it's not empty */
3000 AST_LIST_UNLOCK(&confs);
3002 /* At this point, we have a confno_tmp (static conference) that is empty */
3003 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
3004 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
3005 * Case 2: empty_no_pin and pin is blank (but not NULL)
3006 * Case 3: not empty_no_pin
3008 ast_copy_string(confno, confno_tmp, sizeof(confno));
3010 /* XXX the map is not complete (but we do have a confno) */
3018 ast_config_destroy(cfg);
3022 /* Select first conference number not in use */
3023 if (ast_strlen_zero(confno) && dynamic) {
3024 AST_LIST_LOCK(&confs);
3025 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
3027 snprintf(confno, sizeof(confno), "%d", i);
3032 AST_LIST_UNLOCK(&confs);
3036 if (ast_strlen_zero(confno)) {
3037 res = ast_streamfile(chan, "conf-noempty", chan->language);
3039 ast_waitstream(chan, "");
3041 if (sscanf(confno, "%d", &confno_int) == 1) {