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>dahdi</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <dahdi/user.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 DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
232 " must be present for conferencing to operate properly. In addition, the chan_dahdi\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-DAHDI 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"
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 dahdiconf; /*!< DAHDI 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 char *recordingfilename; /*!< Filename to record the Conference into */
378 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 dahdichannel; /*!< Is a DAHDI 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 = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
643 res = ioctl(fd, DAHDI_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 dahdi_confinfo dahdic = { 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 dahdi conference */
832 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
833 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
834 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
835 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
843 cnf->dahdiconf = dahdic.confno;
845 /* Setup a new channel for playback of audio files */
846 cnf->chan = ast_request("DAHDI", 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 dahdic.confno = cnf->dahdiconf;
852 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
853 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
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->dahdiconf, 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 */
1312 x = DAHDI_FLUSH_ALL;
1313 if (ioctl(fd, DAHDI_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);
1353 if (conf->recordingfilename) {
1354 ast_free(conf->recordingfilename);
1356 if (conf->recordingformat) {
1357 ast_free(conf->recordingformat);
1359 ast_mutex_destroy(&conf->playlock);
1360 ast_mutex_destroy(&conf->listenlock);
1361 ast_mutex_destroy(&conf->recordthreadlock);
1367 static void conf_queue_dtmf(const struct ast_conference *conf,
1368 const struct ast_conf_user *sender, struct ast_frame *f)
1370 struct ast_conf_user *user;
1372 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1375 if (ast_write(user->chan, f) < 0)
1376 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1380 static void sla_queue_event_full(enum sla_event_type type,
1381 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1383 struct sla_event *event;
1385 if (!(event = ast_calloc(1, sizeof(*event))))
1389 event->trunk_ref = trunk_ref;
1390 event->station = station;
1393 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1397 ast_mutex_lock(&sla.lock);
1398 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1399 ast_cond_signal(&sla.cond);
1400 ast_mutex_unlock(&sla.lock);
1403 static void sla_queue_event_nolock(enum sla_event_type type)
1405 sla_queue_event_full(type, NULL, NULL, 0);
1408 static void sla_queue_event(enum sla_event_type type)
1410 sla_queue_event_full(type, NULL, NULL, 1);
1413 /*! \brief Queue a SLA event from the conference */
1414 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1415 struct ast_conference *conf)
1417 struct sla_station *station;
1418 struct sla_trunk_ref *trunk_ref = NULL;
1421 trunk_name = ast_strdupa(conf->confno);
1422 strsep(&trunk_name, "_");
1423 if (ast_strlen_zero(trunk_name)) {
1424 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1428 AST_RWLIST_RDLOCK(&sla_stations);
1429 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1430 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1431 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1437 AST_RWLIST_UNLOCK(&sla_stations);
1440 ast_debug(1, "Trunk not found for event!\n");
1444 sla_queue_event_full(type, trunk_ref, station, 1);
1447 /* Decrement reference counts, as incremented by find_conf() */
1448 static int dispose_conf(struct ast_conference *conf)
1453 AST_LIST_LOCK(&confs);
1454 if (ast_atomic_dec_and_test(&conf->refcount)) {
1455 /* Take the conference room number out of an inuse state */
1456 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1457 conf_map[confno_int] = 0;
1461 AST_LIST_UNLOCK(&confs);
1466 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1470 ast_channel_lock(chan);
1471 original_moh = ast_strdupa(chan->musicclass);
1472 ast_string_field_set(chan, musicclass, musicclass);
1473 ast_channel_unlock(chan);
1475 ast_moh_start(chan, original_moh, NULL);
1477 ast_channel_lock(chan);
1478 ast_string_field_set(chan, musicclass, original_moh);
1479 ast_channel_unlock(chan);
1482 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1484 struct ast_conf_user *user = NULL;
1485 struct ast_conf_user *usr = NULL;
1487 struct dahdi_confinfo dahdic, dahdic_empty;
1488 struct ast_frame *f;
1489 struct ast_channel *c;
1490 struct ast_frame fr;
1498 int musiconhold = 0;
1501 int currentmarked = 0;
1504 int menu_active = 0;
1505 int talkreq_manager = 0;
1506 int using_pseudo = 0;
1511 int announcement_played = 0;
1513 struct ast_dsp *dsp = NULL;
1514 struct ast_app *app;
1516 const char *agifiledefault = "conf-background.agi", *tmp;
1517 char meetmesecs[30] = "";
1518 char exitcontext[AST_MAX_CONTEXT] = "";
1519 char recordingtmp[AST_MAX_EXTENSION] = "";
1520 char members[10] = "";
1521 int dtmf, opt_waitmarked_timeout = 0;
1523 DAHDI_BUFFERINFO bi;
1524 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1525 char *buf = __buf + AST_FRIENDLY_OFFSET;
1526 char *exitkeys = NULL;
1527 unsigned int calldurationlimit = 0;
1529 long play_warning = 0;
1530 long warning_freq = 0;
1531 const char *warning_sound = NULL;
1532 const char *end_sound = NULL;
1534 long time_left_ms = 0;
1535 struct timeval nexteventts = { 0, };
1537 int setusercount = 0;
1539 if (!(user = ast_calloc(1, sizeof(*user))))
1542 /* Possible timeout waiting for marked user */
1543 if ((confflags & CONFFLAG_WAITMARKED) &&
1544 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1545 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1546 (opt_waitmarked_timeout > 0)) {
1547 timeout = time(NULL) + opt_waitmarked_timeout;
1550 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1551 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1552 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1555 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1556 char *limit_str, *warning_str, *warnfreq_str;
1559 parse = optargs[OPT_ARG_DURATION_LIMIT];
1560 limit_str = strsep(&parse, ":");
1561 warning_str = strsep(&parse, ":");
1562 warnfreq_str = parse;
1564 timelimit = atol(limit_str);
1566 play_warning = atol(warning_str);
1568 warning_freq = atol(warnfreq_str);
1571 timelimit = play_warning = warning_freq = 0;
1572 warning_sound = NULL;
1573 } else if (play_warning > timelimit) {
1574 if (!warning_freq) {
1577 while (play_warning > timelimit)
1578 play_warning -= warning_freq;
1579 if (play_warning < 1)
1580 play_warning = warning_freq = 0;
1584 ast_channel_lock(chan);
1585 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1586 var = ast_strdupa(var);
1588 ast_channel_unlock(chan);
1590 warning_sound = var ? var : "timeleft";
1592 ast_channel_lock(chan);
1593 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1594 var = ast_strdupa(var);
1596 ast_channel_unlock(chan);
1598 end_sound = var ? var : NULL;
1600 /* undo effect of S(x) in case they are both used */
1601 calldurationlimit = 0;
1602 /* more efficient do it like S(x) does since no advanced opts */
1603 if (!play_warning && !end_sound && timelimit) {
1604 calldurationlimit = timelimit / 1000;
1605 timelimit = play_warning = warning_freq = 0;
1607 ast_debug(2, "Limit Data for this call:\n");
1608 ast_debug(2, "- timelimit = %ld\n", timelimit);
1609 ast_debug(2, "- play_warning = %ld\n", play_warning);
1610 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1611 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1612 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1617 if ((confflags & CONFFLAG_KEYEXIT)) {
1618 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1619 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1621 exitkeys = ast_strdupa("#"); /* Default */
1624 if (confflags & CONFFLAG_RECORDCONF) {
1625 if (!conf->recordingfilename) {
1627 ast_channel_lock(chan);
1628 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1629 conf->recordingfilename = ast_strdup(var);
1631 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1632 conf->recordingformat = ast_strdup(var);
1634 ast_channel_unlock(chan);
1635 if (!conf->recordingfilename) {
1636 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1637 conf->recordingfilename = ast_strdup(recordingtmp);
1639 if (!conf->recordingformat) {
1640 conf->recordingformat = ast_strdup("wav");
1642 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1643 conf->confno, conf->recordingfilename, conf->recordingformat);
1647 ast_mutex_lock(&conf->recordthreadlock);
1648 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1649 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1650 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1652 dahdic.confno = conf->dahdiconf;
1653 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1654 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1655 ast_log(LOG_WARNING, "Error starting listen channel\n");
1656 ast_hangup(conf->lchan);
1659 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1662 ast_mutex_unlock(&conf->recordthreadlock);
1664 time(&user->jointime);
1666 user->timelimit = timelimit;
1667 user->play_warning = play_warning;
1668 user->warning_freq = warning_freq;
1669 user->warning_sound = warning_sound;
1670 user->end_sound = end_sound;
1672 if (calldurationlimit > 0) {
1673 time(&user->kicktime);
1674 user->kicktime = user->kicktime + calldurationlimit;
1677 if (ast_tvzero(user->start_time))
1678 user->start_time = ast_tvnow();
1679 time_left_ms = user->timelimit;
1681 if (user->timelimit) {
1682 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1683 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1686 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1687 /* Sorry, but this conference is locked! */
1688 if (!ast_streamfile(chan, "conf-locked", chan->language))
1689 ast_waitstream(chan, "");
1693 ast_mutex_lock(&conf->playlock);
1695 if (AST_LIST_EMPTY(&conf->userlist))
1698 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1700 if (rt_schedule && conf->maxusers)
1701 if (user->user_no > conf->maxusers) {
1702 /* Sorry, but this confernce has reached the participant limit! */
1703 if (!ast_streamfile(chan, "conf-full", chan->language))
1704 ast_waitstream(chan, "");
1708 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1711 user->userflags = confflags;
1712 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1715 ast_mutex_unlock(&conf->playlock);
1717 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1718 char destdir[PATH_MAX];
1720 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1722 if (ast_mkdir(destdir, 0777) != 0) {
1723 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1727 snprintf(user->namerecloc, sizeof(user->namerecloc),
1728 "%s/meetme-username-%s-%d", destdir,
1729 conf->confno, user->user_no);
1730 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1731 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1733 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1738 ast_mutex_lock(&conf->playlock);
1740 if (confflags & CONFFLAG_MARKEDUSER)
1741 conf->markedusers++;
1743 if (rt_log_members) {
1745 snprintf(members, sizeof(members), "%d", conf->users);
1746 ast_realtime_require_field("meetme",
1747 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
1748 "members", RQ_UINTEGER1, strlen(members),
1750 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1754 /* This device changed state now - if this is the first user */
1755 if (conf->users == 1)
1756 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1758 ast_mutex_unlock(&conf->playlock);
1760 /* return the unique ID of the conference */
1761 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1763 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1764 ast_channel_lock(chan);
1765 if ((tmp = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
1766 ast_copy_string(exitcontext, tmp, sizeof(exitcontext));
1767 } else if (!ast_strlen_zero(chan->macrocontext)) {
1768 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1770 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1772 ast_channel_unlock(chan);
1775 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1776 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1777 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1778 ast_waitstream(chan, "");
1779 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1780 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1781 ast_waitstream(chan, "");
1784 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1785 int keepplaying = 1;
1787 if (conf->users == 2) {
1788 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1789 res = ast_waitstream(chan, AST_DIGIT_ANY);
1790 ast_stopstream(chan);
1797 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1798 res = ast_waitstream(chan, AST_DIGIT_ANY);
1799 ast_stopstream(chan);
1806 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1812 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1813 res = ast_waitstream(chan, AST_DIGIT_ANY);
1814 ast_stopstream(chan);
1823 ast_indicate(chan, -1);
1825 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1826 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1830 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1831 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1835 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1836 user->dahdichannel = !retrydahdi;
1839 origfd = chan->fds[0];
1841 fd = open("/dev/dahdi/pseudo", O_RDWR);
1843 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1847 /* Make non-blocking */
1848 flags = fcntl(fd, F_GETFL);
1850 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1854 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1855 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1859 /* Setup buffering information */
1860 memset(&bi, 0, sizeof(bi));
1861 bi.bufsize = CONF_SIZE / 2;
1862 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
1863 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
1864 bi.numbufs = audio_buffers;
1865 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
1866 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1871 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
1872 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1878 /* XXX Make sure we're not running on a pseudo channel XXX */
1882 memset(&dahdic, 0, sizeof(dahdic));
1883 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
1884 /* Check to see if we're in a conference... */
1886 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
1887 ast_log(LOG_WARNING, "Error getting conference\n");
1891 if (dahdic.confmode) {
1892 /* Whoa, already in a conference... Retry... */
1894 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
1899 memset(&dahdic, 0, sizeof(dahdic));
1900 /* Add us to the conference */
1902 dahdic.confno = conf->dahdiconf;
1904 ast_mutex_lock(&conf->playlock);
1906 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1907 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1908 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1909 ast_waitstream(conf->chan, "");
1910 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1911 ast_waitstream(conf->chan, "");
1915 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
1916 dahdic.confmode = DAHDI_CONF_CONF;
1917 else if (confflags & CONFFLAG_MONITOR)
1918 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
1919 else if (confflags & CONFFLAG_TALKER)
1920 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
1922 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
1924 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
1925 ast_log(LOG_WARNING, "Error setting conference\n");
1927 ast_mutex_unlock(&conf->playlock);
1930 ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->dahdiconf);
1933 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1938 "CallerIDnum: %s\r\n"
1939 "CallerIDname: %s\r\n",
1940 chan->name, chan->uniqueid, conf->confno,
1942 S_OR(user->chan->cid.cid_num, "<unknown>"),
1943 S_OR(user->chan->cid.cid_name, "<unknown>")
1948 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1950 if (!(confflags & CONFFLAG_QUIET))
1951 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1952 conf_play(chan, conf, ENTER);
1955 ast_mutex_unlock(&conf->playlock);
1957 conf_flush(fd, chan);
1959 if (confflags & CONFFLAG_AGI) {
1960 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1961 or use default filename of conf-background.agi */
1963 ast_channel_lock(chan);
1964 if ((tmp = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
1965 agifile = ast_strdupa(tmp);
1967 agifile = ast_strdupa(agifiledefault);
1969 ast_channel_unlock(chan);
1971 if (user->dahdichannel) {
1972 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
1974 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1976 /* Find a pointer to the agi app and execute the script */
1977 app = pbx_findapp("agi");
1979 ret = pbx_exec(chan, app, agifile);
1981 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1984 if (user->dahdichannel) {
1985 /* Remove CONFMUTE mode on DAHDI channel */
1987 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1990 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
1991 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
1993 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1995 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
1996 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2000 int menu_was_active = 0;
2007 if (now.tv_sec % 60 == 0) {
2009 if (now.tv_sec > conf->endtime) {
2010 ast_verbose("Quitting time...\n");
2014 if (!announcement_played && conf->endalert) {
2015 if (now.tv_sec + conf->endalert > conf->endtime) {
2016 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2017 ast_waitstream(chan, "");
2018 ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
2019 if (!ast_streamfile(chan, "minutes", chan->language))
2020 ast_waitstream(chan, "");
2021 announcement_played = 1;
2032 if (user->kicktime && (user->kicktime <= now.tv_sec))
2036 if (user->timelimit) {
2037 int minutes = 0, seconds = 0, remain = 0;
2039 to = ast_tvdiff_ms(nexteventts, now);
2042 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2043 if (time_left_ms < to)
2046 if (time_left_ms <= 0) {
2047 if (user->end_sound) {
2048 res = ast_streamfile(chan, user->end_sound, chan->language);
2049 res = ast_waitstream(chan, "");
2055 if (time_left_ms >= 5000) {
2057 remain = (time_left_ms + 500) / 1000;
2058 if (remain / 60 >= 1) {
2059 minutes = remain / 60;
2060 seconds = remain % 60;
2065 /* force the time left to round up if appropriate */
2066 if (user->warning_sound && user->play_warning) {
2067 if (!strcmp(user->warning_sound, "timeleft")) {
2069 res = ast_streamfile(chan, "vm-youhave", chan->language);
2070 res = ast_waitstream(chan, "");
2072 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2073 res = ast_streamfile(chan, "queue-minutes", chan->language);
2074 res = ast_waitstream(chan, "");
2077 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2078 res = ast_streamfile(chan, "queue-seconds", chan->language);
2079 res = ast_waitstream(chan, "");
2082 res = ast_streamfile(chan, user->warning_sound, chan->language);
2083 res = ast_waitstream(chan, "");
2087 if (user->warning_freq)
2088 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2090 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2095 if (timeout && now.tv_sec >= timeout)
2098 /* if we have just exited from the menu, and the user had a channel-driver
2099 volume adjustment, restore it
2101 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
2102 set_talk_volume(user, user->listen.desired);
2104 menu_was_active = menu_active;
2106 currentmarked = conf->markedusers;
2107 if (!(confflags & CONFFLAG_QUIET) &&
2108 (confflags & CONFFLAG_MARKEDUSER) &&
2109 (confflags & CONFFLAG_WAITMARKED) &&
2111 if (currentmarked == 1 && conf->users > 1) {
2112 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2113 if (conf->users - 1 == 1) {
2114 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
2115 ast_waitstream(chan, "");
2117 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
2118 ast_waitstream(chan, "");
2121 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
2122 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2123 ast_waitstream(chan, "");
2126 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2128 /* Update the struct with the actual confflags */
2129 user->userflags = confflags;
2131 if (confflags & CONFFLAG_WAITMARKED) {
2132 if (currentmarked == 0) {
2133 if (lastmarked != 0) {
2134 if (!(confflags & CONFFLAG_QUIET))
2135 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
2136 ast_waitstream(chan, "");
2137 if (confflags & CONFFLAG_MARKEDEXIT) {
2138 if (confflags & CONFFLAG_KICK_CONTINUE)
2142 dahdic.confmode = DAHDI_CONF_CONF;
2143 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2144 ast_log(LOG_WARNING, "Error setting conference\n");
2150 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2151 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2154 } else if (currentmarked >= 1 && lastmarked == 0) {
2155 /* Marked user entered, so cancel timeout */
2157 if (confflags & CONFFLAG_MONITOR)
2158 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2159 else if (confflags & CONFFLAG_TALKER)
2160 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2162 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2163 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2164 ast_log(LOG_WARNING, "Error setting conference\n");
2168 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2172 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2173 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
2174 ast_waitstream(chan, "");
2175 conf_play(chan, conf, ENTER);
2180 /* trying to add moh for single person conf */
2181 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2182 if (conf->users == 1) {
2184 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2195 /* Leave if the last marked user left */
2196 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2197 if (confflags & CONFFLAG_KICK_CONTINUE)
2204 /* Check if my modes have changed */
2206 /* If I should be muted but am still talker, mute me */
2207 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2208 dahdic.confmode ^= DAHDI_CONF_TALKER;
2209 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2210 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2215 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2221 chan->name, chan->uniqueid, conf->confno, user->user_no);
2224 /* If I should be un-muted but am not talker, un-mute me */
2225 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2226 dahdic.confmode |= DAHDI_CONF_TALKER;
2227 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2228 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2233 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2239 chan->name, chan->uniqueid, conf->confno, user->user_no);
2242 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2243 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2244 talkreq_manager = 1;
2246 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2252 chan->name, chan->uniqueid, conf->confno, user->user_no);
2256 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2257 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2258 talkreq_manager = 0;
2259 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2265 chan->name, chan->uniqueid, conf->confno, user->user_no);
2268 /* If I have been kicked, exit the conference */
2269 if (user->adminflags & ADMINFLAG_KICKME) {
2270 /* You have been kicked. */
2271 if (!(confflags & CONFFLAG_QUIET) &&
2272 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2273 ast_waitstream(chan, "");
2279 /* Perform an extra hangup check just in case */
2280 if (ast_check_hangup(chan))
2284 char dtmfstr[2] = "";
2286 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2288 /* Kill old pseudo */
2292 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2293 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2294 user->dahdichannel = !retrydahdi;
2297 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
2298 f = ast_read_noaudio(c);
2303 if (f->frametype == AST_FRAME_DTMF) {
2304 dtmfstr[0] = f->subclass;
2308 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2309 if (user->talk.actual)
2310 ast_frame_adjust_volume(f, user->talk.actual);
2312 if (!(confflags & CONFFLAG_MONITOR)) {
2315 if (user->talking == -1)
2318 res = ast_dsp_silence(dsp, f, &totalsilence);
2319 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2321 if (confflags & CONFFLAG_MONITORTALKER)
2322 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2328 chan->name, chan->uniqueid, conf->confno, user->user_no);
2330 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2332 if (confflags & CONFFLAG_MONITORTALKER)
2333 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2339 chan->name, chan->uniqueid, conf->confno, user->user_no);
2343 /* Absolutely do _not_ use careful_write here...
2344 it is important that we read data from the channel
2345 as fast as it arrives, and feed it into the conference.
2346 The buffering in the pseudo channel will take care of any
2347 timing differences, unless they are so drastic as to lose
2348 audio frames (in which case carefully writing would only
2349 have delayed the audio even further).
2351 /* As it turns out, we do want to use careful write. We just
2352 don't want to block, but we do want to at least *try*
2353 to write out all the samples.
2356 careful_write(fd, f->data.ptr, f->datalen, 0);
2358 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2359 if (confflags & CONFFLAG_PASS_DTMF)
2360 conf_queue_dtmf(conf, user, f);
2361 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2362 ast_log(LOG_WARNING, "Error setting conference\n");
2368 /* if we are entering the menu, and the user has a channel-driver
2369 volume adjustment, clear it
2371 if (!menu_active && user->talk.desired && !user->talk.actual)
2372 set_talk_volume(user, 0);
2377 if ((confflags & CONFFLAG_ADMIN)) {
2381 /* Record this sound! */
2382 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2383 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2384 ast_stopstream(chan);
2391 case '1': /* Un/Mute */
2394 /* for admin, change both admin and use flags */
2395 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2396 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2398 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2400 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2401 if (!ast_streamfile(chan, "conf-muted", chan->language))
2402 ast_waitstream(chan, "");
2404 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2405 ast_waitstream(chan, "");
2408 case '2': /* Un/Lock the Conference */
2412 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2413 ast_waitstream(chan, "");
2416 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2417 ast_waitstream(chan, "");
2420 case '3': /* Eject last user */
2422 usr = AST_LIST_LAST(&conf->userlist);
2423 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2424 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2425 ast_waitstream(chan, "");
2427 usr->adminflags |= ADMINFLAG_KICKME;
2428 ast_stopstream(chan);
2431 tweak_listen_volume(user, VOL_DOWN);
2434 tweak_listen_volume(user, VOL_UP);
2437 tweak_talk_volume(user, VOL_DOWN);
2443 tweak_talk_volume(user, VOL_UP);
2447 /* Play an error message! */
2448 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2449 ast_waitstream(chan, "");
2457 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2458 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2459 ast_stopstream(chan);
2466 case '1': /* Un/Mute */
2469 /* user can only toggle the self-muted state */
2470 user->adminflags ^= ADMINFLAG_SELFMUTED;
2472 /* they can't override the admin mute state */
2473 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2474 if (!ast_streamfile(chan, "conf-muted", chan->language))
2475 ast_waitstream(chan, "");
2477 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2478 ast_waitstream(chan, "");
2483 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2484 user->adminflags |= ADMINFLAG_T_REQUEST;
2486 if (user->adminflags & ADMINFLAG_T_REQUEST)
2487 if (!ast_streamfile(chan, "beep", chan->language))
2488 ast_waitstream(chan, "");
2491 tweak_listen_volume(user, VOL_DOWN);
2494 tweak_listen_volume(user, VOL_UP);
2497 tweak_talk_volume(user, VOL_DOWN);
2503 tweak_talk_volume(user, VOL_UP);
2507 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2508 ast_waitstream(chan, "");
2514 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2516 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2517 ast_log(LOG_WARNING, "Error setting conference\n");
2523 conf_flush(fd, chan);
2524 /* Since this option could absorb dtmf meant for the previous (menu), we have to check this one last */
2525 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2526 if (confflags & CONFFLAG_PASS_DTMF)
2527 conf_queue_dtmf(conf, user, f);
2529 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2530 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2535 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2537 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2538 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2540 if (confflags & CONFFLAG_PASS_DTMF)
2541 conf_queue_dtmf(conf, user, f);
2545 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2546 && confflags & CONFFLAG_PASS_DTMF) {
2547 conf_queue_dtmf(conf, user, f);
2548 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2549 switch (f->subclass) {
2550 case AST_CONTROL_HOLD:
2551 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2556 } else if (f->frametype == AST_FRAME_NULL) {
2557 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2560 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2561 chan->name, f->frametype, f->subclass);
2564 } else if (outfd > -1) {
2565 res = read(outfd, buf, CONF_SIZE);
2567 memset(&fr, 0, sizeof(fr));
2568 fr.frametype = AST_FRAME_VOICE;
2569 fr.subclass = AST_FORMAT_SLINEAR;
2571 fr.samples = res / 2;
2573 fr.offset = AST_FRIENDLY_OFFSET;
2574 if (!user->listen.actual &&
2575 ((confflags & CONFFLAG_MONITOR) ||
2576 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2577 (!user->talking)) ) {
2579 for (index = 0; index < AST_FRAME_BITS; index++)
2580 if (chan->rawwriteformat & (1 << index))
2582 if (index >= AST_FRAME_BITS)
2583 goto bailoutandtrynormal;
2584 ast_mutex_lock(&conf->listenlock);
2585 if (!conf->transframe[index]) {
2586 if (conf->origframe) {
2587 if (!conf->transpath[index])
2588 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2589 if (conf->transpath[index]) {
2590 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2591 if (!conf->transframe[index])
2592 conf->transframe[index] = &ast_null_frame;
2596 if (conf->transframe[index]) {
2597 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2598 if (ast_write(chan, conf->transframe[index]))
2599 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2602 ast_mutex_unlock(&conf->listenlock);
2603 goto bailoutandtrynormal;
2605 ast_mutex_unlock(&conf->listenlock);
2607 bailoutandtrynormal:
2608 if (user->listen.actual)
2609 ast_frame_adjust_volume(&fr, user->listen.actual);
2610 if (ast_write(chan, &fr) < 0) {
2611 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2615 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2617 lastmarked = currentmarked;
2627 /* Take out of conference */
2630 dahdic.confmode = 0;
2631 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2632 ast_log(LOG_WARNING, "Error setting conference\n");
2636 reset_volumes(user);
2638 AST_LIST_LOCK(&confs);
2639 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2640 conf_play(chan, conf, LEAVE);
2642 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2643 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2644 if ((conf->chan) && (conf->users > 1)) {
2645 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2646 ast_waitstream(conf->chan, "");
2647 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2648 ast_waitstream(conf->chan, "");
2650 ast_filedelete(user->namerecloc, NULL);
2653 AST_LIST_UNLOCK(&confs);
2656 AST_LIST_LOCK(&confs);
2661 if (user->user_no) { /* Only cleanup users who really joined! */
2663 hr = (now.tv_sec - user->jointime) / 3600;
2664 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2665 sec = (now.tv_sec - user->jointime) % 60;
2668 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2673 "CallerIDNum: %s\r\n"
2674 "CallerIDName: %s\r\n"
2675 "Duration: %ld\r\n",
2676 chan->name, chan->uniqueid, conf->confno,
2678 S_OR(user->chan->cid.cid_num, "<unknown>"),
2679 S_OR(user->chan->cid.cid_name, "<unknown>"),
2680 (long)(now.tv_sec - user->jointime));
2685 if (rt_log_members) {
2687 snprintf(members, sizeof(members), "%d", conf->users);
2688 ast_realtime_require_field("meetme",
2689 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2690 "members", RQ_UINTEGER1, strlen(members),
2692 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2694 if (confflags & CONFFLAG_MARKEDUSER)
2695 conf->markedusers--;
2697 /* Remove ourselves from the list */
2698 AST_LIST_REMOVE(&conf->userlist, user, list);
2700 /* Change any states */
2702 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2704 /* Return the number of seconds the user was in the conf */
2705 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2706 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2709 AST_LIST_UNLOCK(&confs);
2714 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2715 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2716 char *optargs[], int *too_early)
2718 struct ast_variable *var;
2719 struct ast_conference *cnf;
2723 /* Check first in the conference list */
2724 AST_LIST_LOCK(&confs);
2725 AST_LIST_TRAVERSE(&confs, cnf, list) {
2726 if (!strcmp(confno, cnf->confno))
2730 cnf->refcount += refcount;
2732 AST_LIST_UNLOCK(&confs);
2735 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2738 char currenttime[19] = "";
2739 char eatime[19] = "";
2740 char useropts[32] = "";
2741 char adminopts[32] = "";
2742 struct ast_tm tm, etm;
2743 struct timeval endtime = { .tv_sec = 0 };
2748 ast_localtime(&now, &tm, NULL);
2749 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2751 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
2753 var = ast_load_realtime("meetme", "confno",
2754 confno, "starttime <= ", currenttime, "endtime >= ",
2757 if (!var && fuzzystart) {
2759 now.tv_sec += fuzzystart;
2761 ast_localtime(&now, &tm, NULL);
2762 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2763 var = ast_load_realtime("meetme", "confno",
2764 confno, "starttime <= ", currenttime, "endtime >= ",
2768 if (!var && earlyalert) {
2770 now.tv_sec += earlyalert;
2771 ast_localtime(&now, &etm, NULL);
2772 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
2773 var = ast_load_realtime("meetme", "confno",
2774 confno, "starttime <= ", eatime, "endtime >= ",
2781 var = ast_load_realtime("meetme", "confno", confno, NULL);
2786 if (rt_schedule && *too_early) {
2787 /* Announce that the caller is early and exit */
2788 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
2789 ast_waitstream(chan, "");
2790 ast_variables_destroy(var);
2795 if (!strcasecmp(var->name, "pin")) {
2796 pin = ast_strdupa(var->value);
2797 } else if (!strcasecmp(var->name, "adminpin")) {
2798 pinadmin = ast_strdupa(var->value);
2799 } else if (!strcasecmp(var->name, "opts")) {
2800 ast_copy_string(useropts, var->value, sizeof(useropts));
2801 } else if (!strcasecmp(var->name, "maxusers")) {
2802 maxusers = atoi(var->value);
2803 } else if (!strcasecmp(var->name, "adminopts")) {
2804 ast_copy_string(adminopts, var->value, sizeof(adminopts));
2805 } else if (!strcasecmp(var->name, "endtime")) {
2810 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
2811 /* strptime does not determine if a time is
2812 * in DST or not. Set tm_isdst to -1 to
2813 * allow ast_mktime to adjust for DST
2816 endtime = ast_mktime(&t.atm, NULL);
2822 ast_variables_destroy(var);
2824 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
2827 cnf->maxusers = maxusers;
2828 cnf->endalert = endalert;
2829 cnf->endtime = endtime.tv_sec;
2834 if (confflags && !cnf->chan &&
2835 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2836 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2837 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
2838 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2841 if (confflags && !cnf->chan &&
2842 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2843 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
2844 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2852 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2853 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2855 struct ast_config *cfg;
2856 struct ast_variable *var;
2857 struct ast_flags config_flags = { 0 };
2858 struct ast_conference *cnf;
2860 AST_DECLARE_APP_ARGS(args,
2861 AST_APP_ARG(confno);
2863 AST_APP_ARG(pinadmin);
2866 /* Check first in the conference list */
2867 ast_debug(1, "The requested confno is '%s'?\n", confno);
2868 AST_LIST_LOCK(&confs);
2869 AST_LIST_TRAVERSE(&confs, cnf, list) {
2870 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
2871 if (!strcmp(confno, cnf->confno))
2875 cnf->refcount += refcount;
2877 AST_LIST_UNLOCK(&confs);
2881 /* No need to parse meetme.conf */
2882 ast_debug(1, "Building dynamic conference '%s'\n", confno);
2884 if (dynamic_pin[0] == 'q') {
2885 /* Query the user to enter a PIN */
2886 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2889 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
2891 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
2894 /* Check the config */
2895 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
2897 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2900 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2901 if (strcasecmp(var->name, "conf"))
2904 if (!(parse = ast_strdupa(var->value)))
2907 AST_STANDARD_APP_ARGS(args, parse);
2908 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
2909 if (!strcasecmp(args.confno, confno)) {
2910 /* Bingo it's a valid conference */
2911 cnf = build_conf(args.confno,
2913 S_OR(args.pinadmin, ""),
2914 make, dynamic, refcount, chan);
2919 ast_debug(1, "%s isn't a valid conference\n", confno);
2921 ast_config_destroy(cfg);
2923 } else if (dynamic_pin) {
2924 /* Correct for the user selecting 'D' instead of 'd' to have
2925 someone join into a conference that has already been created
2927 if (dynamic_pin[0] == 'q')
2928 dynamic_pin[0] = '\0';
2932 if (confflags && !cnf->chan &&
2933 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2934 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2935 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
2936 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2939 if (confflags && !cnf->chan &&
2940 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2941 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
2942 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2949 /*! \brief The MeetmeCount application */
2950 static int count_exec(struct ast_channel *chan, void *data)
2953 struct ast_conference *conf;
2957 AST_DECLARE_APP_ARGS(args,
2958 AST_APP_ARG(confno);
2959 AST_APP_ARG(varname);
2962 if (ast_strlen_zero(data)) {
2963 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2967 if (!(localdata = ast_strdupa(data)))
2970 AST_STANDARD_APP_ARGS(args, localdata);
2972 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2975 count = conf->users;
2981 if (!ast_strlen_zero(args.varname)) {
2982 /* have var so load it and exit */
2983 snprintf(val, sizeof(val), "%d", count);
2984 pbx_builtin_setvar_helper(chan, args.varname, val);
2986 if (chan->_state != AST_STATE_UP)
2988 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2994 /*! \brief The meetme() application */
2995 static int conf_exec(struct ast_channel *chan, void *data)
2998 char confno[MAX_CONFNUM] = "";
3001 struct ast_conference *cnf = NULL;
3002 struct ast_flags confflags = {0}, config_flags = { 0 };
3004 int empty = 0, empty_no_pin = 0;
3005 int always_prompt = 0;
3006 char *notdata, *info, the_pin[MAX_PIN] = "";
3007 AST_DECLARE_APP_ARGS(args,
3008 AST_APP_ARG(confno);
3009 AST_APP_ARG(options);
3012 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
3014 if (ast_strlen_zero(data)) {
3021 if (chan->_state != AST_STATE_UP)
3024 info = ast_strdupa(notdata);
3026 AST_STANDARD_APP_ARGS(args, info);
3029 ast_copy_string(confno, args.confno, sizeof(confno));
3030 if (ast_strlen_zero(confno)) {
3036 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
3039 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
3040 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
3041 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
3042 strcpy(the_pin, "q");
3044 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
3045 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
3046 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
3054 struct ast_config *cfg;
3055 struct ast_variable *var;
3058 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
3059 if ((empty_no_pin) || (!dynamic)) {
3060 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
3062 var = ast_variable_browse(cfg, "rooms");
3064 if (!strcasecmp(var->name, "conf")) {
3065 char *stringp = ast_strdupa(var->value);
3067 char *confno_tmp = strsep(&stringp, "|,");
3070 /* For static: run through the list and see if this conference is empty */
3071 AST_LIST_LOCK(&confs);