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/astobj2.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.h"
66 #define CONFIG_FILE_NAME "meetme.conf"
67 #define SLA_CONFIG_FILE "sla.conf"
69 /*! each buffer is 20ms, so this is 640ms total */
70 #define DEFAULT_AUDIO_BUFFERS 32
72 /*! String format for scheduled conferences */
73 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
76 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
77 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
78 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
79 /*! User has requested to speak */
80 ADMINFLAG_T_REQUEST = (1 << 4),
83 #define MEETME_DELAYDETECTTALK 300
84 #define MEETME_DELAYDETECTENDTALK 1000
86 #define AST_FRAME_BITS 32
98 enum recording_state {
100 MEETME_RECORD_STARTED,
101 MEETME_RECORD_ACTIVE,
102 MEETME_RECORD_TERMINATE
105 #define CONF_SIZE 320
108 /*! user has admin access on the conference */
109 CONFFLAG_ADMIN = (1 << 0),
110 /*! If set the user can only receive audio from the conference */
111 CONFFLAG_MONITOR = (1 << 1),
112 /*! If set asterisk will exit conference when key defined in p() option is pressed */
113 CONFFLAG_KEYEXIT = (1 << 2),
114 /*! If set asterisk will provide a menu to the user when '*' is pressed */
115 CONFFLAG_STARMENU = (1 << 3),
116 /*! If set the use can only send audio to the conference */
117 CONFFLAG_TALKER = (1 << 4),
118 /*! If set there will be no enter or leave sounds */
119 CONFFLAG_QUIET = (1 << 5),
120 /*! If set, when user joins the conference, they will be told the number
121 * of users that are already in */
122 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
123 /*! Set to run AGI Script in Background */
124 CONFFLAG_AGI = (1 << 7),
125 /*! Set to have music on hold when user is alone in conference */
126 CONFFLAG_MOH = (1 << 8),
127 /*! If set the MeetMe will return if all marked with this flag left */
128 CONFFLAG_MARKEDEXIT = (1 << 9),
129 /*! If set, the MeetMe will wait until a marked user enters */
130 CONFFLAG_WAITMARKED = (1 << 10),
131 /*! If set, the MeetMe will exit to the specified context */
132 CONFFLAG_EXIT_CONTEXT = (1 << 11),
133 /*! If set, the user will be marked */
134 CONFFLAG_MARKEDUSER = (1 << 12),
135 /*! If set, user will be ask record name on entry of conference */
136 CONFFLAG_INTROUSER = (1 << 13),
137 /*! If set, the MeetMe will be recorded */
138 CONFFLAG_RECORDCONF = (1<< 14),
139 /*! If set, the user will be monitored if the user is talking or not */
140 CONFFLAG_MONITORTALKER = (1 << 15),
141 CONFFLAG_DYNAMIC = (1 << 16),
142 CONFFLAG_DYNAMICPIN = (1 << 17),
143 CONFFLAG_EMPTY = (1 << 18),
144 CONFFLAG_EMPTYNOPIN = (1 << 19),
145 CONFFLAG_ALWAYSPROMPT = (1 << 20),
146 /*! If set, won't speak the extra prompt when the first person
147 * enters the conference */
148 CONFFLAG_NOONLYPERSON = (1 << 22),
149 /*! If set, user will be asked to record name on entry of conference
151 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
152 /*! If set, the user will be initially self-muted */
153 CONFFLAG_STARTMUTED = (1 << 24),
154 /*! Pass DTMF through the conference */
155 CONFFLAG_PASS_DTMF = (1 << 25),
156 CONFFLAG_SLA_STATION = (1 << 26),
157 CONFFLAG_SLA_TRUNK = (1 << 27),
158 /*! If set, the user should continue in the dialplan if kicked out */
159 CONFFLAG_KICK_CONTINUE = (1 << 28),
160 CONFFLAG_DURATION_STOP = (1 << 29),
161 CONFFLAG_DURATION_LIMIT = (1 << 30),
165 OPT_ARG_WAITMARKED = 0,
166 OPT_ARG_EXITKEYS = 1,
167 OPT_ARG_DURATION_STOP = 2,
168 OPT_ARG_DURATION_LIMIT = 3,
169 OPT_ARG_MOH_CLASS = 4,
170 OPT_ARG_ARRAY_SIZE = 5,
173 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
174 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
175 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
176 AST_APP_OPTION('b', CONFFLAG_AGI ),
177 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
178 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
179 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
180 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
181 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
182 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
183 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
184 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
185 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
186 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
187 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
188 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
189 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
190 AST_APP_OPTION('q', CONFFLAG_QUIET ),
191 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
192 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
193 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
194 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
195 AST_APP_OPTION('t', CONFFLAG_TALKER ),
196 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
197 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
198 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
199 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
200 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
201 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
204 static const char *app = "MeetMe";
205 static const char *app2 = "MeetMeCount";
206 static const char *app3 = "MeetMeAdmin";
207 static const char *app4 = "MeetMeChannelAdmin";
208 static const char *slastation_app = "SLAStation";
209 static const char *slatrunk_app = "SLATrunk";
211 static const char *synopsis = "MeetMe conference bridge";
212 static const char *synopsis2 = "MeetMe participant count";
213 static const char *synopsis3 = "MeetMe conference Administration";
214 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
215 static const char *slastation_synopsis = "Shared Line Appearance Station";
216 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
218 /* Lookup RealTime conferences based on confno and current time */
219 static int rt_schedule;
220 static int fuzzystart;
221 static int earlyalert;
225 /* Log participant count to the RealTime backend */
226 static int rt_log_members;
228 static const char *descrip =
229 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
230 "conference. If the conference number is omitted, the user will be prompted\n"
231 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
232 "is specified, by pressing '#'.\n"
233 "Please note: The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
234 " must be present for conferencing to operate properly. In addition, the chan_dahdi\n"
235 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
236 "The option string may contain zero or more of the following characters:\n"
237 " 'a' -- set admin mode\n"
238 " 'A' -- set marked mode\n"
239 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
240 " Default: conf-background.agi (Note: This does not work with\n"
241 " non-DAHDI channels in the same conference)\n"
242 " 'c' -- announce user(s) count on joining a conference\n"
243 " 'C' -- continue in dialplan when kicked out of conference\n"
244 " 'd' -- dynamically add conference\n"
245 " 'D' -- dynamically add conference, prompting for a PIN\n"
246 " 'e' -- select an empty conference\n"
247 " 'E' -- select an empty pinless conference\n"
248 " 'F' -- Pass DTMF through the conference.\n"
249 " 'i' -- announce user join/leave with review\n"
250 " 'I' -- announce user join/leave without review\n"
251 " 'l' -- set listen only mode (Listen only, no talking)\n"
252 " 'm' -- set initially muted\n"
254 " -- enable music on hold when the conference has a single caller.\n"
255 " Optionally, specify a musiconhold class to use. If one is not\n"
256 " provided, it will use the channel's currently set music class,\n"
258 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
259 " being muted, meaning (a) No encode is done on transmission and\n"
260 " (b) Received audio that is not registered as talking is omitted\n"
261 " causing no buildup in background noise\n"
263 " -- allow user to exit the conference by pressing '#' (default)\n"
264 " or any of the defined keys. If keys contain '*' this will override\n"
265 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
266 " 'P' -- always prompt for the pin even if it is specified\n"
267 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
268 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
269 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
270 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
272 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
273 " 't' -- set talk only mode. (Talk only, no listening)\n"
274 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
276 " -- wait until the marked user enters the conference\n"
277 " 'x' -- close the conference when last marked user exits\n"
278 " 'X' -- allow user to exit the conference by entering a valid single\n"
279 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
280 " if that variable is not defined.\n"
281 " '1' -- do not play message when first person enters\n"
282 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
283 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
284 " left. Repeat the warning every 'z' ms. The following special\n"
285 " variables can be used with this option:\n"
286 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
287 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
288 " The default is to say the time remaining.\n"
291 static const char *descrip2 =
292 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
293 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
294 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
295 "the channel, unless priority n+1 exists, in which case priority progress will\n"
299 static const char *descrip3 =
300 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
301 " 'e' -- Eject last user that joined\n"
302 " 'E' -- Extend conference end time, if scheduled\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"
321 " MeetMeAdmin will additionally set the variable MEETMEADMINSTATUS with one\n"
322 "of the following values:\n"
323 " 'NOPARSE' -- Invalid arguments\n"
324 " 'NOTFOUND' -- User specified was not found\n"
325 " 'FAILED' -- Another failure occurred\n"
326 " 'OK' -- The operation was completed successfully\n"
329 static const char *descrip4 =
330 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
331 "channel in any coference.\n"
332 " 'k' -- Kick the specified user out of the conference he is in\n"
333 " 'm' -- Unmute the specified user\n"
334 " 'M' -- Mute the specified user\n"
337 static const char *slastation_desc =
338 " SLAStation(<station name>):\n"
339 "This application should be executed by an SLA station. The argument depends\n"
340 "on how the call was initiated. If the phone was just taken off hook, then\n"
341 "the argument \"station\" should be just the station name. If the call was\n"
342 "initiated by pressing a line key, then the station name should be preceded\n"
343 "by an underscore and the trunk name associated with that line button.\n"
344 "For example: \"station1_line1\"."
345 " On exit, this application will set the variable SLASTATION_STATUS to\n"
346 "one of the following values:\n"
347 " FAILURE | CONGESTION | SUCCESS\n"
350 static const char *slatrunk_desc =
351 " SLATrunk(<trunk name>[,options]):\n"
352 "This application should be executed by an SLA trunk on an inbound call.\n"
353 "The channel calling this application should correspond to the SLA trunk\n"
354 "with the name \"trunk\" that is being passed as an argument.\n"
355 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
356 "one of the following values:\n"
357 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
358 " The available options are:\n"
359 " M[(<class>)] - Play back the specified MOH class instead of ringing\n"
362 #define MAX_CONFNUM 80
364 #define OPTIONS_LEN 32
371 struct announce_listitem {
372 AST_LIST_ENTRY(announce_listitem) entry;
373 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
374 char language[MAX_LANGUAGE];
375 struct ast_channel *confchan;
377 enum announcetypes announcetype;
380 /*! \brief The MeetMe Conference object */
381 struct ast_conference {
382 ast_mutex_t playlock; /*!< Conference specific lock (players) */
383 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
384 char confno[MAX_CONFNUM]; /*!< Conference */
385 struct ast_channel *chan; /*!< Announcements channel */
386 struct ast_channel *lchan; /*!< Listen/Record channel */
387 int fd; /*!< Announcements fd */
388 int dahdiconf; /*!< DAHDI Conf # */
389 int users; /*!< Number of active users */
390 int markedusers; /*!< Number of marked users */
391 int maxusers; /*!< Participant limit if scheduled */
392 int endalert; /*!< When to play conf ending message */
393 time_t start; /*!< Start time (s) */
394 int refcount; /*!< reference count of usage */
395 enum recording_state recording:2; /*!< recording status */
396 unsigned int isdynamic:1; /*!< Created on the fly? */
397 unsigned int locked:1; /*!< Is the conference locked? */
398 pthread_t recordthread; /*!< thread for recording */
399 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
400 pthread_attr_t attr; /*!< thread attribute */
401 char *recordingfilename; /*!< Filename to record the Conference into */
402 char *recordingformat; /*!< Format to record the Conference in */
403 char pin[MAX_PIN]; /*!< If protected by a PIN */
404 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
406 long endtime; /*!< When to end the conf if scheduled */
407 const char *useropts; /*!< RealTime user flags */
408 const char *adminopts; /*!< RealTime moderator flags */
409 const char *bookid; /*!< RealTime conference id */
410 struct ast_frame *transframe[32];
411 struct ast_frame *origframe;
412 struct ast_trans_pvt *transpath[32];
413 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
414 AST_LIST_ENTRY(ast_conference) list;
415 /* announce_thread related data */
416 pthread_t announcethread;
417 ast_mutex_t announcethreadlock;
418 unsigned int announcethread_stop:1;
419 ast_cond_t announcelist_addition;
420 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
421 ast_mutex_t announcelistlock;
424 static AST_LIST_HEAD_STATIC(confs, ast_conference);
426 static unsigned int conf_map[1024] = {0, };
429 int desired; /*!< Desired volume adjustment */
430 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
433 /*! \brief The MeetMe User object */
434 struct ast_conf_user {
435 int user_no; /*!< User Number */
436 int userflags; /*!< Flags as set in the conference */
437 int adminflags; /*!< Flags set by the Admin */
438 struct ast_channel *chan; /*!< Connected channel */
439 int talking; /*!< Is user talking */
440 int dahdichannel; /*!< Is a DAHDI channel */
441 char usrvalue[50]; /*!< Custom User Value */
442 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
443 time_t jointime; /*!< Time the user joined the conference */
444 time_t kicktime; /*!< Time the user will be kicked from the conference */
445 struct timeval start_time; /*!< Time the user entered into the conference */
446 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
447 long play_warning; /*!< Play a warning when 'y' ms are left */
448 long warning_freq; /*!< Repeat the warning every 'z' ms */
449 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
450 const char *end_sound; /*!< File to play when time is up. */
452 struct volume listen;
453 AST_LIST_ENTRY(ast_conf_user) list;
456 enum sla_which_trunk_refs {
461 enum sla_trunk_state {
462 SLA_TRUNK_STATE_IDLE,
463 SLA_TRUNK_STATE_RINGING,
465 SLA_TRUNK_STATE_ONHOLD,
466 SLA_TRUNK_STATE_ONHOLD_BYME,
469 enum sla_hold_access {
470 /*! This means that any station can put it on hold, and any station
471 * can retrieve the call from hold. */
473 /*! This means that only the station that put the call on hold may
474 * retrieve it from hold. */
478 struct sla_trunk_ref;
481 AST_RWLIST_ENTRY(sla_station) entry;
482 AST_DECLARE_STRING_FIELDS(
483 AST_STRING_FIELD(name);
484 AST_STRING_FIELD(device);
485 AST_STRING_FIELD(autocontext);
487 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
488 struct ast_dial *dial;
489 /*! Ring timeout for this station, for any trunk. If a ring timeout
490 * is set for a specific trunk on this station, that will take
491 * priority over this value. */
492 unsigned int ring_timeout;
493 /*! Ring delay for this station, for any trunk. If a ring delay
494 * is set for a specific trunk on this station, that will take
495 * priority over this value. */
496 unsigned int ring_delay;
497 /*! This option uses the values in the sla_hold_access enum and sets the
498 * access control type for hold on this station. */
499 unsigned int hold_access:1;
500 /*! Use count for inside sla_station_exec */
501 unsigned int ref_count;
504 struct sla_station_ref {
505 AST_LIST_ENTRY(sla_station_ref) entry;
506 struct sla_station *station;
510 AST_RWLIST_ENTRY(sla_trunk) entry;
511 AST_DECLARE_STRING_FIELDS(
512 AST_STRING_FIELD(name);
513 AST_STRING_FIELD(device);
514 AST_STRING_FIELD(autocontext);
516 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
517 /*! Number of stations that use this trunk */
518 unsigned int num_stations;
519 /*! Number of stations currently on a call with this trunk */
520 unsigned int active_stations;
521 /*! Number of stations that have this trunk on hold. */
522 unsigned int hold_stations;
523 struct ast_channel *chan;
524 unsigned int ring_timeout;
525 /*! If set to 1, no station will be able to join an active call with
527 unsigned int barge_disabled:1;
528 /*! This option uses the values in the sla_hold_access enum and sets the
529 * access control type for hold on this trunk. */
530 unsigned int hold_access:1;
531 /*! Whether this trunk is currently on hold, meaning that once a station
532 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
533 unsigned int on_hold:1;
534 /*! Use count for inside sla_trunk_exec */
535 unsigned int ref_count;
538 struct sla_trunk_ref {
539 AST_LIST_ENTRY(sla_trunk_ref) entry;
540 struct sla_trunk *trunk;
541 enum sla_trunk_state state;
542 struct ast_channel *chan;
543 /*! Ring timeout to use when this trunk is ringing on this specific
544 * station. This takes higher priority than a ring timeout set at
545 * the station level. */
546 unsigned int ring_timeout;
547 /*! Ring delay to use when this trunk is ringing on this specific
548 * station. This takes higher priority than a ring delay set at
549 * the station level. */
550 unsigned int ring_delay;
553 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
554 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
556 static const char sla_registrar[] = "SLA";
558 /*! \brief Event types that can be queued up for the SLA thread */
559 enum sla_event_type {
560 /*! A station has put the call on hold */
562 /*! The state of a dial has changed */
563 SLA_EVENT_DIAL_STATE,
564 /*! The state of a ringing trunk has changed */
565 SLA_EVENT_RINGING_TRUNK,
566 /*! A reload of configuration has been requested */
568 /*! Poke the SLA thread so it can check if it can perform a reload */
569 SLA_EVENT_CHECK_RELOAD,
573 enum sla_event_type type;
574 struct sla_station *station;
575 struct sla_trunk_ref *trunk_ref;
576 AST_LIST_ENTRY(sla_event) entry;
579 /*! \brief A station that failed to be dialed
580 * \note Only used by the SLA thread. */
581 struct sla_failed_station {
582 struct sla_station *station;
583 struct timeval last_try;
584 AST_LIST_ENTRY(sla_failed_station) entry;
587 /*! \brief A trunk that is ringing */
588 struct sla_ringing_trunk {
589 struct sla_trunk *trunk;
590 /*! The time that this trunk started ringing */
591 struct timeval ring_begin;
592 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
593 AST_LIST_ENTRY(sla_ringing_trunk) entry;
596 enum sla_station_hangup {
597 SLA_STATION_HANGUP_NORMAL,
598 SLA_STATION_HANGUP_TIMEOUT,
601 /*! \brief A station that is ringing */
602 struct sla_ringing_station {
603 struct sla_station *station;
604 /*! The time that this station started ringing */
605 struct timeval ring_begin;
606 AST_LIST_ENTRY(sla_ringing_station) entry;
610 * \brief A structure for data used by the sla thread
613 /*! The SLA thread ID */
617 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
618 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
619 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
620 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
622 /*! Attempt to handle CallerID, even though it is known not to work
623 * properly in some situations. */
624 unsigned int attempt_callerid:1;
625 /*! A reload has been requested */
626 unsigned int reload:1;
628 .thread = AST_PTHREADT_NULL,
631 /*! The number of audio buffers to be allocated on pseudo channels
632 * when in a conference */
633 static int audio_buffers;
635 /*! Map 'volume' levels from -5 through +5 into
636 * decibel (dB) settings for channel drivers
637 * Note: these are not a straight linear-to-dB
638 * conversion... the numbers have been modified
639 * to give the user a better level of adjustability
641 static char const gain_map[] = {
656 static int admin_exec(struct ast_channel *chan, void *data);
657 static void *recordthread(void *args);
659 static char *istalking(int x)
664 return "(unmonitored)";
666 return "(not talking)";
669 static int careful_write(int fd, unsigned char *data, int len, int block)
676 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
677 res = ioctl(fd, DAHDI_IOMUX, &x);
681 res = write(fd, data, len);
683 if (errno != EAGAIN) {
684 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
696 static int set_talk_volume(struct ast_conf_user *user, int volume)
700 /* attempt to make the adjustment in the channel driver;
701 if successful, don't adjust in the frame reading routine
703 gain_adjust = gain_map[volume + 5];
705 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
708 static int set_listen_volume(struct ast_conf_user *user, int volume)
712 /* attempt to make the adjustment in the channel driver;
713 if successful, don't adjust in the frame reading routine
715 gain_adjust = gain_map[volume + 5];
717 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
720 static void tweak_volume(struct volume *vol, enum volume_action action)
724 switch (vol->desired) {
739 switch (vol->desired) {
755 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
757 tweak_volume(&user->talk, action);
758 /* attempt to make the adjustment in the channel driver;
759 if successful, don't adjust in the frame reading routine
761 if (!set_talk_volume(user, user->talk.desired))
762 user->talk.actual = 0;
764 user->talk.actual = user->talk.desired;
767 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
769 tweak_volume(&user->listen, action);
770 /* attempt to make the adjustment in the channel driver;
771 if successful, don't adjust in the frame reading routine
773 if (!set_listen_volume(user, user->listen.desired))
774 user->listen.actual = 0;
776 user->listen.actual = user->listen.desired;
779 static void reset_volumes(struct ast_conf_user *user)
781 signed char zero_volume = 0;
783 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
784 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
787 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
793 if (!ast_check_hangup(chan))
794 res = ast_autoservice_start(chan);
796 AST_LIST_LOCK(&confs);
812 careful_write(conf->fd, data, len, 1);
815 AST_LIST_UNLOCK(&confs);
818 ast_autoservice_stop(chan);
822 * \brief Find or create a conference
824 * \param confno The conference name/number
825 * \param pin The regular user pin
826 * \param pinadmin The admin pin
827 * \param make Make the conf if it doesn't exist
828 * \param dynamic Mark the newly created conference as dynamic
829 * \param refcount How many references to mark on the conference
830 * \param chan The asterisk channel
832 * \return A pointer to the conference struct, or NULL if it wasn't found and
833 * make or dynamic were not set.
835 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
837 struct ast_conference *cnf;
838 struct dahdi_confinfo dahdic = { 0, };
841 AST_LIST_LOCK(&confs);
843 AST_LIST_TRAVERSE(&confs, cnf, list) {
844 if (!strcmp(confno, cnf->confno))
848 if (cnf || (!make && !dynamic))
852 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
855 ast_mutex_init(&cnf->playlock);
856 ast_mutex_init(&cnf->listenlock);
857 cnf->recordthread = AST_PTHREADT_NULL;
858 ast_mutex_init(&cnf->recordthreadlock);
859 cnf->announcethread = AST_PTHREADT_NULL;
860 ast_mutex_init(&cnf->announcethreadlock);
861 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
862 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
863 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
864 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
866 /* Setup a new dahdi conference */
868 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
869 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
870 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
871 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
879 cnf->dahdiconf = dahdic.confno;
881 /* Setup a new channel for playback of audio files */
882 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
884 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
885 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
887 dahdic.confno = cnf->dahdiconf;
888 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
889 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
890 ast_log(LOG_WARNING, "Error setting conference\n");
892 ast_hangup(cnf->chan);
902 /* Fill the conference struct */
903 cnf->start = time(NULL);
904 cnf->maxusers = 0x7fffffff;
905 cnf->isdynamic = dynamic ? 1 : 0;
906 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
907 AST_LIST_INSERT_HEAD(&confs, cnf, list);
909 /* Reserve conference number in map */
910 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
911 conf_map[confno_int] = 1;
915 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
917 AST_LIST_UNLOCK(&confs);
922 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
924 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
926 int len = strlen(word);
928 struct ast_conference *cnf = NULL;
929 struct ast_conf_user *usr = NULL;
932 char *myline, *ret = NULL;
934 if (pos == 1) { /* Command */
935 return ast_cli_complete(word, cmds, state);
936 } else if (pos == 2) { /* Conference Number */
937 AST_LIST_LOCK(&confs);
938 AST_LIST_TRAVERSE(&confs, cnf, list) {
939 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
944 ret = ast_strdup(ret); /* dup before releasing the lock */
945 AST_LIST_UNLOCK(&confs);
947 } else if (pos == 3) {
948 /* User Number || Conf Command option*/
949 if (strstr(line, "mute") || strstr(line, "kick")) {
950 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
951 return ast_strdup("all");
953 AST_LIST_LOCK(&confs);
955 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
956 myline = ast_strdupa(line);
957 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
958 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
962 AST_LIST_TRAVERSE(&confs, cnf, list) {
963 if (!strcmp(confno, cnf->confno))
968 /* Search for the user */
969 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
970 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
971 if (!strncasecmp(word, usrno, len) && ++which > state)
975 AST_LIST_UNLOCK(&confs);
976 return usr ? ast_strdup(usrno) : NULL;
983 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
985 /* Process the command */
986 struct ast_conf_user *user;
987 struct ast_conference *cnf;
989 int i = 0, total = 0;
991 struct ast_str *cmdline = NULL;
992 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
993 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
997 e->command = "meetme list [concise]";
999 "Usage: meetme list [concise] <confno> \n"
1000 " List all or a specific conference.\n";
1003 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1006 /* Check for length so no buffer will overflow... */
1007 for (i = 0; i < a->argc; i++) {
1008 if (strlen(a->argv[i]) > 100)
1009 ast_cli(a->fd, "Invalid Arguments.\n");
1012 /* Max confno length */
1013 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1017 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1018 /* List all the conferences */
1019 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1021 AST_LIST_LOCK(&confs);
1022 if (AST_LIST_EMPTY(&confs)) {
1024 ast_cli(a->fd, "No active MeetMe conferences.\n");
1026 AST_LIST_UNLOCK(&confs);
1031 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1033 AST_LIST_TRAVERSE(&confs, cnf, list) {
1034 if (cnf->markedusers == 0) {
1035 ast_str_set(&cmdline, 0, "N/A ");
1037 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1039 hr = (now - cnf->start) / 3600;
1040 min = ((now - cnf->start) % 3600) / 60;
1041 sec = (now - cnf->start) % 60;
1043 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1045 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1054 total += cnf->users;
1056 AST_LIST_UNLOCK(&confs);
1058 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1062 } else if (strcmp(a->argv[1], "list") == 0) {
1063 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1064 /* List all the users in a conference */
1065 if (AST_LIST_EMPTY(&confs)) {
1067 ast_cli(a->fd, "No active MeetMe conferences.\n");
1072 /* Find the right conference */
1073 AST_LIST_LOCK(&confs);
1074 AST_LIST_TRAVERSE(&confs, cnf, list) {
1075 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1081 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1082 AST_LIST_UNLOCK(&confs);
1086 /* Show all the users */
1088 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1089 hr = (now - user->jointime) / 3600;
1090 min = ((now - user->jointime) % 3600) / 60;
1091 sec = (now - user->jointime) % 60;
1093 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1095 S_OR(user->chan->cid.cid_num, "<unknown>"),
1096 S_OR(user->chan->cid.cid_name, "<no name>"),
1098 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1099 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1100 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1101 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1102 istalking(user->talking), hr, min, sec);
1104 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1106 S_OR(user->chan->cid.cid_num, ""),
1107 S_OR(user->chan->cid.cid_name, ""),
1109 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1110 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1111 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1112 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1113 user->talking, hr, min, sec);
1117 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1119 AST_LIST_UNLOCK(&confs);
1125 return CLI_SHOWUSAGE;
1128 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1130 admin_exec(NULL, cmdline->str);
1137 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1139 /* Process the command */
1140 struct ast_str *cmdline = NULL;
1145 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1147 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1148 " Executes a command for the conference or on a conferee\n";
1151 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1155 ast_cli(a->fd, "Invalid Arguments.\n");
1156 /* Check for length so no buffer will overflow... */
1157 for (i = 0; i < a->argc; i++) {
1158 if (strlen(a->argv[i]) > 100)
1159 ast_cli(a->fd, "Invalid Arguments.\n");
1162 /* Max confno length */
1163 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1169 return CLI_SHOWUSAGE;
1172 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1173 if (strstr(a->argv[1], "lock")) {
1174 if (strcmp(a->argv[1], "lock") == 0) {
1176 ast_str_append(&cmdline, 0, ",L");
1179 ast_str_append(&cmdline, 0, ",l");
1181 } else if (strstr(a->argv[1], "mute")) {
1184 return CLI_SHOWUSAGE;
1186 if (strcmp(a->argv[1], "mute") == 0) {
1188 if (strcmp(a->argv[3], "all") == 0) {
1189 ast_str_append(&cmdline, 0, ",N");
1191 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1195 if (strcmp(a->argv[3], "all") == 0) {
1196 ast_str_append(&cmdline, 0, ",n");
1198 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1201 } else if (strcmp(a->argv[1], "kick") == 0) {
1204 return CLI_SHOWUSAGE;
1206 if (strcmp(a->argv[3], "all") == 0) {
1208 ast_str_append(&cmdline, 0, ",K");
1210 /* Kick a single user */
1211 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1215 return CLI_SHOWUSAGE;
1218 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1220 admin_exec(NULL, cmdline->str);
1226 static const char *sla_hold_str(unsigned int hold_access)
1228 const char *hold = "Unknown";
1230 switch (hold_access) {
1234 case SLA_HOLD_PRIVATE:
1243 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1245 const struct sla_trunk *trunk;
1249 e->command = "sla show trunks";
1251 "Usage: sla show trunks\n"
1252 " This will list all trunks defined in sla.conf\n";
1259 "=============================================================\n"
1260 "=== Configured SLA Trunks ===================================\n"
1261 "=============================================================\n"
1263 AST_RWLIST_RDLOCK(&sla_trunks);
1264 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1265 struct sla_station_ref *station_ref;
1266 char ring_timeout[16] = "(none)";
1267 if (trunk->ring_timeout)
1268 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1269 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1270 "=== Trunk Name: %s\n"
1271 "=== ==> Device: %s\n"
1272 "=== ==> AutoContext: %s\n"
1273 "=== ==> RingTimeout: %s\n"
1274 "=== ==> BargeAllowed: %s\n"
1275 "=== ==> HoldAccess: %s\n"
1276 "=== ==> Stations ...\n",
1277 trunk->name, trunk->device,
1278 S_OR(trunk->autocontext, "(none)"),
1280 trunk->barge_disabled ? "No" : "Yes",
1281 sla_hold_str(trunk->hold_access));
1282 AST_RWLIST_RDLOCK(&sla_stations);
1283 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1284 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1285 AST_RWLIST_UNLOCK(&sla_stations);
1286 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1288 AST_RWLIST_UNLOCK(&sla_trunks);
1289 ast_cli(a->fd, "=============================================================\n\n");
1294 static const char *trunkstate2str(enum sla_trunk_state state)
1296 #define S(e) case e: return # e;
1298 S(SLA_TRUNK_STATE_IDLE)
1299 S(SLA_TRUNK_STATE_RINGING)
1300 S(SLA_TRUNK_STATE_UP)
1301 S(SLA_TRUNK_STATE_ONHOLD)
1302 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1304 return "Uknown State";
1308 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1310 const struct sla_station *station;
1314 e->command = "sla show stations";
1316 "Usage: sla show stations\n"
1317 " This will list all stations defined in sla.conf\n";
1324 "=============================================================\n"
1325 "=== Configured SLA Stations =================================\n"
1326 "=============================================================\n"
1328 AST_RWLIST_RDLOCK(&sla_stations);
1329 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1330 struct sla_trunk_ref *trunk_ref;
1331 char ring_timeout[16] = "(none)";
1332 char ring_delay[16] = "(none)";
1333 if (station->ring_timeout) {
1334 snprintf(ring_timeout, sizeof(ring_timeout),
1335 "%u", station->ring_timeout);
1337 if (station->ring_delay) {
1338 snprintf(ring_delay, sizeof(ring_delay),
1339 "%u", station->ring_delay);
1341 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1342 "=== Station Name: %s\n"
1343 "=== ==> Device: %s\n"
1344 "=== ==> AutoContext: %s\n"
1345 "=== ==> RingTimeout: %s\n"
1346 "=== ==> RingDelay: %s\n"
1347 "=== ==> HoldAccess: %s\n"
1348 "=== ==> Trunks ...\n",
1349 station->name, station->device,
1350 S_OR(station->autocontext, "(none)"),
1351 ring_timeout, ring_delay,
1352 sla_hold_str(station->hold_access));
1353 AST_RWLIST_RDLOCK(&sla_trunks);
1354 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1355 if (trunk_ref->ring_timeout) {
1356 snprintf(ring_timeout, sizeof(ring_timeout),
1357 "%u", trunk_ref->ring_timeout);
1359 strcpy(ring_timeout, "(none)");
1360 if (trunk_ref->ring_delay) {
1361 snprintf(ring_delay, sizeof(ring_delay),
1362 "%u", trunk_ref->ring_delay);
1364 strcpy(ring_delay, "(none)");
1365 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1366 "=== ==> State: %s\n"
1367 "=== ==> RingTimeout: %s\n"
1368 "=== ==> RingDelay: %s\n",
1369 trunk_ref->trunk->name,
1370 trunkstate2str(trunk_ref->state),
1371 ring_timeout, ring_delay);
1373 AST_RWLIST_UNLOCK(&sla_trunks);
1374 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1377 AST_RWLIST_UNLOCK(&sla_stations);
1378 ast_cli(a->fd, "============================================================\n"
1384 static struct ast_cli_entry cli_meetme[] = {
1385 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1386 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1387 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1388 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1391 static void conf_flush(int fd, struct ast_channel *chan)
1395 /* read any frames that may be waiting on the channel
1399 struct ast_frame *f;
1401 /* when no frames are available, this will wait
1402 for 1 millisecond maximum
1404 while (ast_waitfor(chan, 1)) {
1408 else /* channel was hung up or something else happened */
1413 /* flush any data sitting in the pseudo channel */
1414 x = DAHDI_FLUSH_ALL;
1415 if (ioctl(fd, DAHDI_FLUSH, &x))
1416 ast_log(LOG_WARNING, "Error flushing channel\n");
1420 /* Remove the conference from the list and free it.
1421 We assume that this was called while holding conflock. */
1422 static int conf_free(struct ast_conference *conf)
1425 struct announce_listitem *item;
1427 AST_LIST_REMOVE(&confs, conf, list);
1428 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1430 if (conf->recording == MEETME_RECORD_ACTIVE) {
1431 conf->recording = MEETME_RECORD_TERMINATE;
1432 AST_LIST_UNLOCK(&confs);
1435 AST_LIST_LOCK(&confs);
1436 if (conf->recording == MEETME_RECORD_OFF)
1438 AST_LIST_UNLOCK(&confs);
1442 for (x = 0; x < AST_FRAME_BITS; x++) {
1443 if (conf->transframe[x])
1444 ast_frfree(conf->transframe[x]);
1445 if (conf->transpath[x])
1446 ast_translator_free_path(conf->transpath[x]);
1448 if (conf->announcethread != AST_PTHREADT_NULL) {
1449 ast_mutex_lock(&conf->announcelistlock);
1450 conf->announcethread_stop = 1;
1451 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1452 ast_cond_signal(&conf->announcelist_addition);
1453 ast_mutex_unlock(&conf->announcelistlock);
1454 pthread_join(conf->announcethread, NULL);
1456 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1457 ast_filedelete(item->namerecloc, NULL);
1460 ast_mutex_destroy(&conf->announcelistlock);
1462 if (conf->origframe)
1463 ast_frfree(conf->origframe);
1465 ast_hangup(conf->lchan);
1467 ast_hangup(conf->chan);
1470 if (conf->recordingfilename) {
1471 ast_free(conf->recordingfilename);
1473 if (conf->recordingformat) {
1474 ast_free(conf->recordingformat);
1476 ast_mutex_destroy(&conf->playlock);
1477 ast_mutex_destroy(&conf->listenlock);
1478 ast_mutex_destroy(&conf->recordthreadlock);
1479 ast_mutex_destroy(&conf->announcethreadlock);
1485 static void conf_queue_dtmf(const struct ast_conference *conf,
1486 const struct ast_conf_user *sender, struct ast_frame *f)
1488 struct ast_conf_user *user;
1490 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1493 if (ast_write(user->chan, f) < 0)
1494 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1498 static void sla_queue_event_full(enum sla_event_type type,
1499 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1501 struct sla_event *event;
1503 if (!(event = ast_calloc(1, sizeof(*event))))
1507 event->trunk_ref = trunk_ref;
1508 event->station = station;
1511 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1515 ast_mutex_lock(&sla.lock);
1516 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1517 ast_cond_signal(&sla.cond);
1518 ast_mutex_unlock(&sla.lock);
1521 static void sla_queue_event_nolock(enum sla_event_type type)
1523 sla_queue_event_full(type, NULL, NULL, 0);
1526 static void sla_queue_event(enum sla_event_type type)
1528 sla_queue_event_full(type, NULL, NULL, 1);
1531 /*! \brief Queue a SLA event from the conference */
1532 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1533 struct ast_conference *conf)
1535 struct sla_station *station;
1536 struct sla_trunk_ref *trunk_ref = NULL;
1539 trunk_name = ast_strdupa(conf->confno);
1540 strsep(&trunk_name, "_");
1541 if (ast_strlen_zero(trunk_name)) {
1542 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1546 AST_RWLIST_RDLOCK(&sla_stations);
1547 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1548 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1549 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1555 AST_RWLIST_UNLOCK(&sla_stations);
1558 ast_debug(1, "Trunk not found for event!\n");
1562 sla_queue_event_full(type, trunk_ref, station, 1);
1565 /* Decrement reference counts, as incremented by find_conf() */
1566 static int dispose_conf(struct ast_conference *conf)
1571 AST_LIST_LOCK(&confs);
1572 if (ast_atomic_dec_and_test(&conf->refcount)) {
1573 /* Take the conference room number out of an inuse state */
1574 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1575 conf_map[confno_int] = 0;
1580 AST_LIST_UNLOCK(&confs);
1585 static int rt_extend_conf(char *confno)
1587 char currenttime[32];
1591 struct ast_variable *var;
1600 ast_localtime(&now, &tm, NULL);
1601 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1603 var = ast_load_realtime("meetme", "confno",
1604 confno, "startTime<= ", currenttime,
1605 "endtime>= ", currenttime, NULL);
1607 /* Identify the specific RealTime conference */
1609 if (!strcasecmp(var->name, "bookid")) {
1610 ast_copy_string(bookid, var->value, sizeof(bookid));
1612 if (!strcasecmp(var->name, "endtime")) {
1613 ast_copy_string(endtime, var->value, sizeof(endtime));
1618 ast_variables_destroy(var);
1620 ast_strptime(endtime, DATE_FORMAT, &tm);
1621 now = ast_mktime(&tm, NULL);
1623 now.tv_sec += extendby;
1625 ast_localtime(&now, &tm, NULL);
1626 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1627 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1629 var = ast_load_realtime("meetme", "confno",
1630 confno, "startTime<= ", currenttime,
1631 "endtime>= ", currenttime, NULL);
1633 /* If there is no conflict with extending the conference, update the DB */
1635 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1636 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1641 ast_variables_destroy(var);
1645 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1649 ast_channel_lock(chan);
1650 original_moh = ast_strdupa(chan->musicclass);
1651 ast_string_field_set(chan, musicclass, musicclass);
1652 ast_channel_unlock(chan);
1654 ast_moh_start(chan, original_moh, NULL);
1656 ast_channel_lock(chan);
1657 ast_string_field_set(chan, musicclass, original_moh);
1658 ast_channel_unlock(chan);
1661 static const char *get_announce_filename(enum announcetypes type)
1665 return "conf-hasleft";
1668 return "conf-hasjoin";
1675 static void *announce_thread(void *data)
1677 struct announce_listitem *current;
1678 struct ast_conference *conf = data;
1680 char filename[PATH_MAX] = "";
1681 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
1682 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
1684 while (!conf->announcethread_stop) {
1685 ast_mutex_lock(&conf->announcelistlock);
1686 if (conf->announcethread_stop) {
1687 ast_mutex_unlock(&conf->announcelistlock);
1690 if (AST_LIST_EMPTY(&conf->announcelist))
1691 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
1693 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
1694 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1696 ast_mutex_unlock(&conf->announcelistlock);
1697 if (conf->announcethread_stop) {
1701 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
1702 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
1703 if (!ast_fileexists(current->namerecloc, NULL, NULL))
1705 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
1706 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
1707 res = ast_waitstream(current->confchan, "");
1709 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
1710 if (!ast_streamfile(current->confchan, filename, current->language))
1711 ast_waitstream(current->confchan, "");
1714 if (current->announcetype == CONF_HASLEFT) {
1715 ast_filedelete(current->namerecloc, NULL);
1720 /* thread marked to stop, clean up */
1721 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
1722 ast_filedelete(current->namerecloc, NULL);
1723 ao2_ref(current, -1);
1728 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1730 struct ast_conf_user *user = NULL;
1731 struct ast_conf_user *usr = NULL;
1733 struct dahdi_confinfo dahdic, dahdic_empty;
1734 struct ast_frame *f;
1735 struct ast_channel *c;
1736 struct ast_frame fr;
1744 int musiconhold = 0;
1747 int currentmarked = 0;
1750 int menu_active = 0;
1751 int talkreq_manager = 0;
1752 int using_pseudo = 0;
1757 int announcement_played = 0;
1759 struct ast_dsp *dsp = NULL;
1760 struct ast_app *agi_app;
1762 const char *agifiledefault = "conf-background.agi", *tmpvar;
1763 char meetmesecs[30] = "";
1764 char exitcontext[AST_MAX_CONTEXT] = "";
1765 char recordingtmp[AST_MAX_EXTENSION] = "";
1766 char members[10] = "";
1767 int dtmf, opt_waitmarked_timeout = 0;
1769 struct dahdi_bufferinfo bi;
1770 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1771 char *buf = __buf + AST_FRIENDLY_OFFSET;
1772 char *exitkeys = NULL;
1773 unsigned int calldurationlimit = 0;
1775 long play_warning = 0;
1776 long warning_freq = 0;
1777 const char *warning_sound = NULL;
1778 const char *end_sound = NULL;
1780 long time_left_ms = 0;
1781 struct timeval nexteventts = { 0, };
1783 int setusercount = 0;
1785 if (!(user = ast_calloc(1, sizeof(*user))))
1788 /* Possible timeout waiting for marked user */
1789 if ((confflags & CONFFLAG_WAITMARKED) &&
1790 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1791 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1792 (opt_waitmarked_timeout > 0)) {
1793 timeout = time(NULL) + opt_waitmarked_timeout;
1796 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1797 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1798 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1801 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1802 char *limit_str, *warning_str, *warnfreq_str;
1805 parse = optargs[OPT_ARG_DURATION_LIMIT];
1806 limit_str = strsep(&parse, ":");
1807 warning_str = strsep(&parse, ":");
1808 warnfreq_str = parse;
1810 timelimit = atol(limit_str);
1812 play_warning = atol(warning_str);
1814 warning_freq = atol(warnfreq_str);
1817 timelimit = play_warning = warning_freq = 0;
1818 warning_sound = NULL;
1819 } else if (play_warning > timelimit) {
1820 if (!warning_freq) {
1823 while (play_warning > timelimit)
1824 play_warning -= warning_freq;
1825 if (play_warning < 1)
1826 play_warning = warning_freq = 0;
1830 ast_channel_lock(chan);
1831 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1832 var = ast_strdupa(var);
1834 ast_channel_unlock(chan);
1836 warning_sound = var ? var : "timeleft";
1838 ast_channel_lock(chan);
1839 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1840 var = ast_strdupa(var);
1842 ast_channel_unlock(chan);
1844 end_sound = var ? var : NULL;
1846 /* undo effect of S(x) in case they are both used */
1847 calldurationlimit = 0;
1848 /* more efficient do it like S(x) does since no advanced opts */
1849 if (!play_warning && !end_sound && timelimit) {
1850 calldurationlimit = timelimit / 1000;
1851 timelimit = play_warning = warning_freq = 0;
1853 ast_debug(2, "Limit Data for this call:\n");
1854 ast_debug(2, "- timelimit = %ld\n", timelimit);
1855 ast_debug(2, "- play_warning = %ld\n", play_warning);
1856 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1857 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1858 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1863 if ((confflags & CONFFLAG_KEYEXIT)) {
1864 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1865 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1867 exitkeys = ast_strdupa("#"); /* Default */
1870 if (confflags & CONFFLAG_RECORDCONF) {
1871 if (!conf->recordingfilename) {
1873 ast_channel_lock(chan);
1874 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1875 conf->recordingfilename = ast_strdup(var);
1877 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1878 conf->recordingformat = ast_strdup(var);
1880 ast_channel_unlock(chan);
1881 if (!conf->recordingfilename) {
1882 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1883 conf->recordingfilename = ast_strdup(recordingtmp);
1885 if (!conf->recordingformat) {
1886 conf->recordingformat = ast_strdup("wav");
1888 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1889 conf->confno, conf->recordingfilename, conf->recordingformat);
1893 ast_mutex_lock(&conf->recordthreadlock);
1894 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1895 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1896 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1898 dahdic.confno = conf->dahdiconf;
1899 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1900 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1901 ast_log(LOG_WARNING, "Error starting listen channel\n");
1902 ast_hangup(conf->lchan);
1905 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1908 ast_mutex_unlock(&conf->recordthreadlock);
1910 ast_mutex_lock(&conf->announcethreadlock);
1911 if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1912 ast_mutex_init(&conf->announcelistlock);
1913 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1914 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
1916 ast_mutex_unlock(&conf->announcethreadlock);
1918 time(&user->jointime);
1920 user->timelimit = timelimit;
1921 user->play_warning = play_warning;
1922 user->warning_freq = warning_freq;
1923 user->warning_sound = warning_sound;
1924 user->end_sound = end_sound;
1926 if (calldurationlimit > 0) {
1927 time(&user->kicktime);
1928 user->kicktime = user->kicktime + calldurationlimit;
1931 if (ast_tvzero(user->start_time))
1932 user->start_time = ast_tvnow();
1933 time_left_ms = user->timelimit;
1935 if (user->timelimit) {
1936 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1937 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1940 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1941 /* Sorry, but this conference is locked! */
1942 if (!ast_streamfile(chan, "conf-locked", chan->language))
1943 ast_waitstream(chan, "");
1947 ast_mutex_lock(&conf->playlock);
1949 if (AST_LIST_EMPTY(&conf->userlist))
1952 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1954 if (rt_schedule && conf->maxusers)
1955 if (user->user_no > conf->maxusers) {
1956 /* Sorry, but this confernce has reached the participant limit! */
1957 if (!ast_streamfile(chan, "conf-full", chan->language))
1958 ast_waitstream(chan, "");
1962 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1965 user->userflags = confflags;
1966 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1969 ast_mutex_unlock(&conf->playlock);
1971 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1972 char destdir[PATH_MAX];
1974 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1976 if (ast_mkdir(destdir, 0777) != 0) {
1977 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1981 snprintf(user->namerecloc, sizeof(user->namerecloc),
1982 "%s/meetme-username-%s-%d", destdir,
1983 conf->confno, user->user_no);
1984 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1985 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1987 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1992 ast_mutex_lock(&conf->playlock);
1994 if (confflags & CONFFLAG_MARKEDUSER)
1995 conf->markedusers++;
1997 if (rt_log_members) {
1999 snprintf(members, sizeof(members), "%d", conf->users);
2000 ast_realtime_require_field("meetme",
2001 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2002 "members", RQ_UINTEGER1, strlen(members),
2004 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2008 /* This device changed state now - if this is the first user */
2009 if (conf->users == 1)
2010 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2012 ast_mutex_unlock(&conf->playlock);
2014 /* return the unique ID of the conference */
2015 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2017 if (confflags & CONFFLAG_EXIT_CONTEXT) {
2018 ast_channel_lock(chan);
2019 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2020 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2021 } else if (!ast_strlen_zero(chan->macrocontext)) {
2022 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2024 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2026 ast_channel_unlock(chan);
2029 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2030 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
2031 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2032 ast_waitstream(chan, "");
2033 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2034 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2035 ast_waitstream(chan, "");
2038 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
2039 int keepplaying = 1;
2041 if (conf->users == 2) {
2042 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2043 res = ast_waitstream(chan, AST_DIGIT_ANY);
2044 ast_stopstream(chan);
2051 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2052 res = ast_waitstream(chan, AST_DIGIT_ANY);
2053 ast_stopstream(chan);
2060 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2066 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2067 res = ast_waitstream(chan, AST_DIGIT_ANY);
2068 ast_stopstream(chan);
2077 ast_indicate(chan, -1);
2079 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2080 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2084 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2085 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2089 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2090 user->dahdichannel = !retrydahdi;
2093 origfd = chan->fds[0];
2095 fd = open("/dev/dahdi/pseudo", O_RDWR);
2097 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2101 /* Make non-blocking */
2102 flags = fcntl(fd, F_GETFL);
2104 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
2108 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
2109 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
2113 /* Setup buffering information */
2114 memset(&bi, 0, sizeof(bi));
2115 bi.bufsize = CONF_SIZE / 2;
2116 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2117 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2118 bi.numbufs = audio_buffers;
2119 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2120 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2125 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2126 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2132 /* XXX Make sure we're not running on a pseudo channel XXX */
2136 memset(&dahdic, 0, sizeof(dahdic));
2137 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2138 /* Check to see if we're in a conference... */
2140 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2141 ast_log(LOG_WARNING, "Error getting conference\n");
2145 if (dahdic.confmode) {
2146 /* Whoa, already in a conference... Retry... */
2148 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2153 memset(&dahdic, 0, sizeof(dahdic));
2154 /* Add us to the conference */
2156 dahdic.confno = conf->dahdiconf;
2158 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2159 struct announce_listitem *item;
2160 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2162 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2163 ast_copy_string(item->language, chan->language, sizeof(item->language));
2164 item->confchan = conf->chan;
2165 item->confusers = conf->users;
2166 item->announcetype = CONF_HASJOIN;
2167 ast_mutex_lock(&conf->announcelistlock);
2168 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2169 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2170 ast_cond_signal(&conf->announcelist_addition);
2171 ast_mutex_unlock(&conf->announcelistlock);
2173 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2179 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2180 dahdic.confmode = DAHDI_CONF_CONF;
2181 else if (confflags & CONFFLAG_MONITOR)
2182 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2183 else if (confflags & CONFFLAG_TALKER)
2184 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2186 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2188 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2189 ast_log(LOG_WARNING, "Error setting conference\n");
2193 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2196 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
2201 "CallerIDnum: %s\r\n"
2202 "CallerIDname: %s\r\n",
2203 chan->name, chan->uniqueid, conf->confno,
2205 S_OR(user->chan->cid.cid_num, "<unknown>"),
2206 S_OR(user->chan->cid.cid_name, "<unknown>")
2211 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2213 if (!(confflags & CONFFLAG_QUIET))
2214 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2215 conf_play(chan, conf, ENTER);
2218 conf_flush(fd, chan);
2220 if (confflags & CONFFLAG_AGI) {
2221 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2222 or use default filename of conf-background.agi */
2224 ast_channel_lock(chan);
2225 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2226 agifile = ast_strdupa(tmpvar);
2228 agifile = ast_strdupa(agifiledefault);
2230 ast_channel_unlock(chan);
2232 if (user->dahdichannel) {
2233 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2235 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2237 /* Find a pointer to the agi app and execute the script */
2238 agi_app = pbx_findapp("agi");
2240 ret = pbx_exec(chan, agi_app, agifile);
2242 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2245 if (user->dahdichannel) {
2246 /* Remove CONFMUTE mode on DAHDI channel */
2248 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2251 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2252 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2254 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2256 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2257 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2261 int menu_was_active = 0;
2267 if (rt_schedule && conf->endtime) {
2268 char currenttime[32];
2269 long localendtime = 0;
2272 struct ast_variable *var, *origvar;
2275 if (now.tv_sec % 60 == 0) {
2277 ast_localtime(&now, &tm, NULL);
2278 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2279 var = origvar = ast_load_realtime("meetme", "confno",
2280 conf->confno, "starttime <=", currenttime,
2281 "endtime >=", currenttime, NULL);
2283 for ( ; var; var = var->next) {
2284 if (!strcasecmp(var->name, "endtime")) {
2285 struct ast_tm endtime_tm;
2286 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2287 tmp = ast_mktime(&endtime_tm, NULL);
2288 localendtime = tmp.tv_sec;
2291 ast_variables_destroy(origvar);
2293 /* A conference can be extended from the
2294 Admin/User menu or by an external source */
2295 if (localendtime > conf->endtime){
2296 conf->endtime = localendtime;
2300 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2301 ast_verbose("Quitting time...\n");
2305 if (!announcement_played && conf->endalert) {
2306 if (now.tv_sec + conf->endalert >= conf->endtime) {
2307 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2308 ast_waitstream(chan, "");
2309 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2310 if (!ast_streamfile(chan, "minutes", chan->language))
2311 ast_waitstream(chan, "");
2312 announcement_played = 1;
2317 announcement_played = 0;
2327 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2332 if (user->timelimit) {
2333 int minutes = 0, seconds = 0, remain = 0;
2335 to = ast_tvdiff_ms(nexteventts, now);
2339 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2340 if (time_left_ms < to) {
2344 if (time_left_ms <= 0) {
2345 if (user->end_sound) {
2346 res = ast_streamfile(chan, user->end_sound, chan->language);
2347 res = ast_waitstream(chan, "");
2353 if (time_left_ms >= 5000) {
2355 remain = (time_left_ms + 500) / 1000;
2356 if (remain / 60 >= 1) {
2357 minutes = remain / 60;
2358 seconds = remain % 60;
2363 /* force the time left to round up if appropriate */
2364 if (user->warning_sound && user->play_warning) {
2365 if (!strcmp(user->warning_sound, "timeleft")) {
2367 res = ast_streamfile(chan, "vm-youhave", chan->language);
2368 res = ast_waitstream(chan, "");
2370 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2371 res = ast_streamfile(chan, "queue-minutes", chan->language);
2372 res = ast_waitstream(chan, "");
2375 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2376 res = ast_streamfile(chan, "queue-seconds", chan->language);
2377 res = ast_waitstream(chan, "");
2380 res = ast_streamfile(chan, user->warning_sound, chan->language);
2381 res = ast_waitstream(chan, "");
2385 if (user->warning_freq) {
2386 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2388 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2394 if (timeout && now.tv_sec >= timeout) {
2398 /* if we have just exited from the menu, and the user had a channel-driver
2399 volume adjustment, restore it
2401 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2402 set_talk_volume(user, user->listen.desired);
2405 menu_was_active = menu_active;
2407 currentmarked = conf->markedusers;
2408 if (!(confflags & CONFFLAG_QUIET) &&
2409 (confflags & CONFFLAG_MARKEDUSER) &&
2410 (confflags & CONFFLAG_WAITMARKED) &&
2412 if (currentmarked == 1 && conf->users > 1) {
2413 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2414 if (conf->users - 1 == 1) {
2415 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2416 ast_waitstream(chan, "");
2419 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2420 ast_waitstream(chan, "");
2424 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2425 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2426 ast_waitstream(chan, "");
2431 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2433 /* Update the struct with the actual confflags */
2434 user->userflags = confflags;
2436 if (confflags & CONFFLAG_WAITMARKED) {
2437 if (currentmarked == 0) {
2438 if (lastmarked != 0) {
2439 if (!(confflags & CONFFLAG_QUIET)) {
2440 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2441 ast_waitstream(chan, "");
2444 if (confflags & CONFFLAG_MARKEDEXIT) {
2445 if (confflags & CONFFLAG_KICK_CONTINUE) {
2450 dahdic.confmode = DAHDI_CONF_CONF;
2451 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2452 ast_log(LOG_WARNING, "Error setting conference\n");
2458 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2459 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2462 } else if (currentmarked >= 1 && lastmarked == 0) {
2463 /* Marked user entered, so cancel timeout */
2465 if (confflags & CONFFLAG_MONITOR) {
2466 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2467 } else if (confflags & CONFFLAG_TALKER) {
2468 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2470 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2472 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2473 ast_log(LOG_WARNING, "Error setting conference\n");
2477 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2481 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2482 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2483 ast_waitstream(chan, "");
2485 conf_play(chan, conf, ENTER);
2490 /* trying to add moh for single person conf */
2491 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2492 if (conf->users == 1) {
2494 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2505 /* Leave if the last marked user left */
2506 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2507 if (confflags & CONFFLAG_KICK_CONTINUE) {
2515 /* Check if my modes have changed */
2517 /* If I should be muted but am still talker, mute me */
2518 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2519 dahdic.confmode ^= DAHDI_CONF_TALKER;
2520 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2521 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2526 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2532 chan->name, chan->uniqueid, conf->confno, user->user_no);
2535 /* If I should be un-muted but am not talker, un-mute me */
2536 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2537 dahdic.confmode |= DAHDI_CONF_TALKER;
2538 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2539 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2544 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2550 chan->name, chan->uniqueid, conf->confno, user->user_no);
2553 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2554 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2555 talkreq_manager = 1;
2557 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2563 chan->name, chan->uniqueid, conf->confno, user->user_no);
2567 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2568 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2569 talkreq_manager = 0;
2570 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2576 chan->name, chan->uniqueid, conf->confno, user->user_no);
2579 /* If I have been kicked, exit the conference */
2580 if (user->adminflags & ADMINFLAG_KICKME) {
2581 /* You have been kicked. */
2582 if (!(confflags & CONFFLAG_QUIET) &&
2583 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2584 ast_waitstream(chan, "");
2590 /* Perform an extra hangup check just in case */
2591 if (ast_check_hangup(chan)) {
2596 char dtmfstr[2] = "";
2598 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2600 /* Kill old pseudo */
2604 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2605 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2606 user->dahdichannel = !retrydahdi;
2609 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2610 f = ast_read_noaudio(c);
2617 if (f->frametype == AST_FRAME_DTMF) {
2618 dtmfstr[0] = f->subclass;
2622 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2623 if (user->talk.actual) {
2624 ast_frame_adjust_volume(f, user->talk.actual);
2627 if (!(confflags & CONFFLAG_MONITOR)) {
2630 if (user->talking == -1) {
2634 res = ast_dsp_silence(dsp, f, &totalsilence);
2635 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2637 if (confflags & CONFFLAG_MONITORTALKER)
2638 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2644 chan->name, chan->uniqueid, conf->confno, user->user_no);
2646 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2648 if (confflags & CONFFLAG_MONITORTALKER) {
2649 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2655 chan->name, chan->uniqueid, conf->confno, user->user_no);
2660 /* Absolutely do _not_ use careful_write here...
2661 it is important that we read data from the channel
2662 as fast as it arrives, and feed it into the conference.
2663 The buffering in the pseudo channel will take care of any
2664 timing differences, unless they are so drastic as to lose
2665 audio frames (in which case carefully writing would only
2666 have delayed the audio even further).
2668 /* As it turns out, we do want to use careful write. We just
2669 don't want to block, but we do want to at least *try*
2670 to write out all the samples.
2672 if (user->talking) {
2673 careful_write(fd, f->data.ptr, f->datalen, 0);
2676 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2677 if (confflags & CONFFLAG_PASS_DTMF) {
2678 conf_queue_dtmf(conf, user, f);
2680 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2681 ast_log(LOG_WARNING, "Error setting conference\n");
2687 /* if we are entering the menu, and the user has a channel-driver
2688 volume adjustment, clear it
2690 if (!menu_active && user->talk.desired && !user->talk.actual) {
2691 set_talk_volume(user, 0);
2697 if ((confflags & CONFFLAG_ADMIN)) {
2701 /* Record this sound! */
2702 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2703 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2704 ast_stopstream(chan);
2713 case '1': /* Un/Mute */
2716 /* for admin, change both admin and use flags */
2717 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2718 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2720 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2723 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2724 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2725 ast_waitstream(chan, "");
2728 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2729 ast_waitstream(chan, "");
2733 case '2': /* Un/Lock the Conference */
2737 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2738 ast_waitstream(chan, "");
2742 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2743 ast_waitstream(chan, "");
2747 case '3': /* Eject last user */
2749 usr = AST_LIST_LAST(&conf->userlist);
2750 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2751 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2752 ast_waitstream(chan, "");
2755 usr->adminflags |= ADMINFLAG_KICKME;
2757 ast_stopstream(chan);
2760 tweak_listen_volume(user, VOL_DOWN);
2763 /* Extend RT conference */
2765 if (!rt_extend_conf(conf->confno)) {
2766 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2767 ast_waitstream(chan, "");
2770 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2771 ast_waitstream(chan, "");
2774 ast_stopstream(chan);
2779 tweak_listen_volume(user, VOL_UP);
2782 tweak_talk_volume(user, VOL_DOWN);
2788 tweak_talk_volume(user, VOL_UP);
2792 /* Play an error message! */
2793 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2794 ast_waitstream(chan, "");
2803 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2804 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2805 ast_stopstream(chan);
2814 case '1': /* Un/Mute */
2817 /* user can only toggle the self-muted state */
2818 user->adminflags ^= ADMINFLAG_SELFMUTED;
2820 /* they can't override the admin mute state */
2821 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2822 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2823 ast_waitstream(chan, "");
2826 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2827 ast_waitstream(chan, "");
2833 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2834 user->adminflags |= ADMINFLAG_T_REQUEST;
2837 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2838 if (!ast_streamfile(chan, "beep", chan->language)) {
2839 ast_waitstream(chan, "");
2844 tweak_listen_volume(user, VOL_DOWN);
2847 /* Extend RT conference */
2849 rt_extend_conf(conf->confno);
2854 tweak_listen_volume(user, VOL_UP);
2857 tweak_talk_volume(user, VOL_DOWN);
2863 tweak_talk_volume(user, VOL_UP);
2867 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2868 ast_waitstream(chan, "");
2875 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2878 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2879 ast_log(LOG_WARNING, "Error setting conference\n");
2885 conf_flush(fd, chan);
2886 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2887 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2888 if (confflags & CONFFLAG_PASS_DTMF) {
2889 conf_queue_dtmf(conf, user, f);
2892 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2893 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2898 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2900 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2901 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2903 if (confflags & CONFFLAG_PASS_DTMF) {
2904 conf_queue_dtmf(conf, user, f);
2909 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2910 && confflags & CONFFLAG_PASS_DTMF) {
2911 conf_queue_dtmf(conf, user, f);
2912 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2913 switch (f->subclass) {
2914 case AST_CONTROL_HOLD:
2915 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2920 } else if (f->frametype == AST_FRAME_NULL) {
2921 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2924 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2925 chan->name, f->frametype, f->subclass);
2928 } else if (outfd > -1) {
2929 res = read(outfd, buf, CONF_SIZE);
2931 memset(&fr, 0, sizeof(fr));
2932 fr.frametype = AST_FRAME_VOICE;
2933 fr.subclass = AST_FORMAT_SLINEAR;
2935 fr.samples = res / 2;
2937 fr.offset = AST_FRIENDLY_OFFSET;
2938 if (!user->listen.actual &&
2939 ((confflags & CONFFLAG_MONITOR) ||
2940 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2941 (!user->talking)) ) {
2943 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2944 if (chan->rawwriteformat & (1 << idx)) {
2948 if (idx >= AST_FRAME_BITS) {
2949 goto bailoutandtrynormal;
2951 ast_mutex_lock(&conf->listenlock);
2952 if (!conf->transframe[idx]) {
2953 if (conf->origframe) {
2954 if (!conf->transpath[idx]) {
2955 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2957 if (conf->transpath[idx]) {
2958 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2959 if (!conf->transframe[idx]) {
2960 conf->transframe[idx] = &ast_null_frame;
2965 if (conf->transframe[idx]) {
2966 if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2967 if (ast_write(chan, conf->transframe[idx])) {
2968 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2972 ast_mutex_unlock(&conf->listenlock);
2973 goto bailoutandtrynormal;
2975 ast_mutex_unlock(&conf->listenlock);
2977 bailoutandtrynormal:
2978 if (user->listen.actual) {
2979 ast_frame_adjust_volume(&fr, user->listen.actual);
2981 if (ast_write(chan, &fr) < 0) {
2982 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2986 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2989 lastmarked = currentmarked;
3000 /* Take out of conference */
3003 dahdic.confmode = 0;
3004 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3005 ast_log(LOG_WARNING, "Error setting conference\n");
3009 reset_volumes(user);
3011 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
3012 conf_play(chan, conf, LEAVE);
3015 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
3016 struct announce_listitem *item;
3017 if (!(item = ao2_alloc(sizeof(*item), NULL)))
3019 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3020 ast_copy_string(item->language, chan->language, sizeof(item->language));
3021 item->confchan = conf->chan;
3022 item->confusers = conf->users;
3023 item->announcetype = CONF_HASLEFT;
3024 ast_mutex_lock(&conf->announcelistlock);
3025 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3026 ast_cond_signal(&conf->announcelist_addition);
3027 ast_mutex_unlock(&conf->announcelistlock);
3028 } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
3029 /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
3030 ast_filedelete(user->namerecloc, NULL);
3034 AST_LIST_LOCK(&confs);
3040 if (user->user_no) { /* Only cleanup users who really joined! */
3042 hr = (now.tv_sec - user->jointime) / 3600;
3043 min = ((now.tv_sec - user->jointime) % 3600) / 60;
3044 sec = (now.tv_sec - user->jointime) % 60;
3047 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",