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 (sla.thread == AST_PTHREADT_NULL) {
1507 if (!(event = ast_calloc(1, sizeof(*event))))
1511 event->trunk_ref = trunk_ref;
1512 event->station = station;
1515 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1519 ast_mutex_lock(&sla.lock);
1520 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1521 ast_cond_signal(&sla.cond);
1522 ast_mutex_unlock(&sla.lock);
1525 static void sla_queue_event_nolock(enum sla_event_type type)
1527 sla_queue_event_full(type, NULL, NULL, 0);
1530 static void sla_queue_event(enum sla_event_type type)
1532 sla_queue_event_full(type, NULL, NULL, 1);
1535 /*! \brief Queue a SLA event from the conference */
1536 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1537 struct ast_conference *conf)
1539 struct sla_station *station;
1540 struct sla_trunk_ref *trunk_ref = NULL;
1543 trunk_name = ast_strdupa(conf->confno);
1544 strsep(&trunk_name, "_");
1545 if (ast_strlen_zero(trunk_name)) {
1546 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1550 AST_RWLIST_RDLOCK(&sla_stations);
1551 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1552 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1553 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1559 AST_RWLIST_UNLOCK(&sla_stations);
1562 ast_debug(1, "Trunk not found for event!\n");
1566 sla_queue_event_full(type, trunk_ref, station, 1);
1569 /* Decrement reference counts, as incremented by find_conf() */
1570 static int dispose_conf(struct ast_conference *conf)
1575 AST_LIST_LOCK(&confs);
1576 if (ast_atomic_dec_and_test(&conf->refcount)) {
1577 /* Take the conference room number out of an inuse state */
1578 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1579 conf_map[confno_int] = 0;
1584 AST_LIST_UNLOCK(&confs);
1589 static int rt_extend_conf(char *confno)
1591 char currenttime[32];
1595 struct ast_variable *var;
1604 ast_localtime(&now, &tm, NULL);
1605 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1607 var = ast_load_realtime("meetme", "confno",
1608 confno, "startTime<= ", currenttime,
1609 "endtime>= ", currenttime, NULL);
1611 /* Identify the specific RealTime conference */
1613 if (!strcasecmp(var->name, "bookid")) {
1614 ast_copy_string(bookid, var->value, sizeof(bookid));
1616 if (!strcasecmp(var->name, "endtime")) {
1617 ast_copy_string(endtime, var->value, sizeof(endtime));
1622 ast_variables_destroy(var);
1624 ast_strptime(endtime, DATE_FORMAT, &tm);
1625 now = ast_mktime(&tm, NULL);
1627 now.tv_sec += extendby;
1629 ast_localtime(&now, &tm, NULL);
1630 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1631 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1633 var = ast_load_realtime("meetme", "confno",
1634 confno, "startTime<= ", currenttime,
1635 "endtime>= ", currenttime, NULL);
1637 /* If there is no conflict with extending the conference, update the DB */
1639 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1640 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1645 ast_variables_destroy(var);
1649 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1653 ast_channel_lock(chan);
1654 original_moh = ast_strdupa(chan->musicclass);
1655 ast_string_field_set(chan, musicclass, musicclass);
1656 ast_channel_unlock(chan);
1658 ast_moh_start(chan, original_moh, NULL);
1660 ast_channel_lock(chan);
1661 ast_string_field_set(chan, musicclass, original_moh);
1662 ast_channel_unlock(chan);
1665 static const char *get_announce_filename(enum announcetypes type)
1669 return "conf-hasleft";
1672 return "conf-hasjoin";
1679 static void *announce_thread(void *data)
1681 struct announce_listitem *current;
1682 struct ast_conference *conf = data;
1684 char filename[PATH_MAX] = "";
1685 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
1686 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
1688 while (!conf->announcethread_stop) {
1689 ast_mutex_lock(&conf->announcelistlock);
1690 if (conf->announcethread_stop) {
1691 ast_mutex_unlock(&conf->announcelistlock);
1694 if (AST_LIST_EMPTY(&conf->announcelist))
1695 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
1697 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
1698 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1700 ast_mutex_unlock(&conf->announcelistlock);
1701 if (conf->announcethread_stop) {
1705 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
1706 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
1707 if (!ast_fileexists(current->namerecloc, NULL, NULL))
1709 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
1710 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
1711 res = ast_waitstream(current->confchan, "");
1713 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
1714 if (!ast_streamfile(current->confchan, filename, current->language))
1715 ast_waitstream(current->confchan, "");
1718 if (current->announcetype == CONF_HASLEFT) {
1719 ast_filedelete(current->namerecloc, NULL);
1724 /* thread marked to stop, clean up */
1725 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
1726 ast_filedelete(current->namerecloc, NULL);
1727 ao2_ref(current, -1);
1732 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1734 struct ast_conf_user *user = NULL;
1735 struct ast_conf_user *usr = NULL;
1737 struct dahdi_confinfo dahdic, dahdic_empty;
1738 struct ast_frame *f;
1739 struct ast_channel *c;
1740 struct ast_frame fr;
1748 int musiconhold = 0;
1751 int currentmarked = 0;
1754 int menu_active = 0;
1755 int talkreq_manager = 0;
1756 int using_pseudo = 0;
1761 int announcement_played = 0;
1763 struct ast_dsp *dsp = NULL;
1764 struct ast_app *agi_app;
1766 const char *agifiledefault = "conf-background.agi", *tmpvar;
1767 char meetmesecs[30] = "";
1768 char exitcontext[AST_MAX_CONTEXT] = "";
1769 char recordingtmp[AST_MAX_EXTENSION] = "";
1770 char members[10] = "";
1771 int dtmf, opt_waitmarked_timeout = 0;
1773 struct dahdi_bufferinfo bi;
1774 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1775 char *buf = __buf + AST_FRIENDLY_OFFSET;
1776 char *exitkeys = NULL;
1777 unsigned int calldurationlimit = 0;
1779 long play_warning = 0;
1780 long warning_freq = 0;
1781 const char *warning_sound = NULL;
1782 const char *end_sound = NULL;
1784 long time_left_ms = 0;
1785 struct timeval nexteventts = { 0, };
1787 int setusercount = 0;
1789 if (!(user = ast_calloc(1, sizeof(*user))))
1792 /* Possible timeout waiting for marked user */
1793 if ((confflags & CONFFLAG_WAITMARKED) &&
1794 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1795 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1796 (opt_waitmarked_timeout > 0)) {
1797 timeout = time(NULL) + opt_waitmarked_timeout;
1800 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1801 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1802 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1805 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1806 char *limit_str, *warning_str, *warnfreq_str;
1809 parse = optargs[OPT_ARG_DURATION_LIMIT];
1810 limit_str = strsep(&parse, ":");
1811 warning_str = strsep(&parse, ":");
1812 warnfreq_str = parse;
1814 timelimit = atol(limit_str);
1816 play_warning = atol(warning_str);
1818 warning_freq = atol(warnfreq_str);
1821 timelimit = play_warning = warning_freq = 0;
1822 warning_sound = NULL;
1823 } else if (play_warning > timelimit) {
1824 if (!warning_freq) {
1827 while (play_warning > timelimit)
1828 play_warning -= warning_freq;
1829 if (play_warning < 1)
1830 play_warning = warning_freq = 0;
1834 ast_channel_lock(chan);
1835 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1836 var = ast_strdupa(var);
1838 ast_channel_unlock(chan);
1840 warning_sound = var ? var : "timeleft";
1842 ast_channel_lock(chan);
1843 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1844 var = ast_strdupa(var);
1846 ast_channel_unlock(chan);
1848 end_sound = var ? var : NULL;
1850 /* undo effect of S(x) in case they are both used */
1851 calldurationlimit = 0;
1852 /* more efficient do it like S(x) does since no advanced opts */
1853 if (!play_warning && !end_sound && timelimit) {
1854 calldurationlimit = timelimit / 1000;
1855 timelimit = play_warning = warning_freq = 0;
1857 ast_debug(2, "Limit Data for this call:\n");
1858 ast_debug(2, "- timelimit = %ld\n", timelimit);
1859 ast_debug(2, "- play_warning = %ld\n", play_warning);
1860 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1861 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1862 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1867 if ((confflags & CONFFLAG_KEYEXIT)) {
1868 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1869 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1871 exitkeys = ast_strdupa("#"); /* Default */
1874 if (confflags & CONFFLAG_RECORDCONF) {
1875 if (!conf->recordingfilename) {
1877 ast_channel_lock(chan);
1878 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1879 conf->recordingfilename = ast_strdup(var);
1881 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1882 conf->recordingformat = ast_strdup(var);
1884 ast_channel_unlock(chan);
1885 if (!conf->recordingfilename) {
1886 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1887 conf->recordingfilename = ast_strdup(recordingtmp);
1889 if (!conf->recordingformat) {
1890 conf->recordingformat = ast_strdup("wav");
1892 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1893 conf->confno, conf->recordingfilename, conf->recordingformat);
1897 ast_mutex_lock(&conf->recordthreadlock);
1898 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1899 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1900 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1902 dahdic.confno = conf->dahdiconf;
1903 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1904 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1905 ast_log(LOG_WARNING, "Error starting listen channel\n");
1906 ast_hangup(conf->lchan);
1909 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1912 ast_mutex_unlock(&conf->recordthreadlock);
1914 ast_mutex_lock(&conf->announcethreadlock);
1915 if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1916 ast_mutex_init(&conf->announcelistlock);
1917 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1918 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
1920 ast_mutex_unlock(&conf->announcethreadlock);
1922 time(&user->jointime);
1924 user->timelimit = timelimit;
1925 user->play_warning = play_warning;
1926 user->warning_freq = warning_freq;
1927 user->warning_sound = warning_sound;
1928 user->end_sound = end_sound;
1930 if (calldurationlimit > 0) {
1931 time(&user->kicktime);
1932 user->kicktime = user->kicktime + calldurationlimit;
1935 if (ast_tvzero(user->start_time))
1936 user->start_time = ast_tvnow();
1937 time_left_ms = user->timelimit;
1939 if (user->timelimit) {
1940 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1941 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1944 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1945 /* Sorry, but this conference is locked! */
1946 if (!ast_streamfile(chan, "conf-locked", chan->language))
1947 ast_waitstream(chan, "");
1951 ast_mutex_lock(&conf->playlock);
1953 if (AST_LIST_EMPTY(&conf->userlist))
1956 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1958 if (rt_schedule && conf->maxusers)
1959 if (user->user_no > conf->maxusers) {
1960 /* Sorry, but this confernce has reached the participant limit! */
1961 if (!ast_streamfile(chan, "conf-full", chan->language))
1962 ast_waitstream(chan, "");
1966 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1969 user->userflags = confflags;
1970 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1973 ast_mutex_unlock(&conf->playlock);
1975 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1976 char destdir[PATH_MAX];
1978 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1980 if (ast_mkdir(destdir, 0777) != 0) {
1981 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1985 snprintf(user->namerecloc, sizeof(user->namerecloc),
1986 "%s/meetme-username-%s-%d", destdir,
1987 conf->confno, user->user_no);
1988 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1989 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1991 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1996 ast_mutex_lock(&conf->playlock);
1998 if (confflags & CONFFLAG_MARKEDUSER)
1999 conf->markedusers++;
2001 if (rt_log_members) {
2003 snprintf(members, sizeof(members), "%d", conf->users);
2004 ast_realtime_require_field("meetme",
2005 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2006 "members", RQ_UINTEGER1, strlen(members),
2008 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2012 /* This device changed state now - if this is the first user */
2013 if (conf->users == 1)
2014 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2016 ast_mutex_unlock(&conf->playlock);
2018 /* return the unique ID of the conference */
2019 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2021 if (confflags & CONFFLAG_EXIT_CONTEXT) {
2022 ast_channel_lock(chan);
2023 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2024 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2025 } else if (!ast_strlen_zero(chan->macrocontext)) {
2026 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2028 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2030 ast_channel_unlock(chan);
2033 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2034 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
2035 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2036 ast_waitstream(chan, "");
2037 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2038 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2039 ast_waitstream(chan, "");
2042 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
2043 int keepplaying = 1;
2045 if (conf->users == 2) {
2046 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2047 res = ast_waitstream(chan, AST_DIGIT_ANY);
2048 ast_stopstream(chan);
2055 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2056 res = ast_waitstream(chan, AST_DIGIT_ANY);
2057 ast_stopstream(chan);
2064 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2070 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2071 res = ast_waitstream(chan, AST_DIGIT_ANY);
2072 ast_stopstream(chan);
2081 ast_indicate(chan, -1);
2083 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2084 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2088 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2089 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2093 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2094 user->dahdichannel = !retrydahdi;
2097 origfd = chan->fds[0];
2099 fd = open("/dev/dahdi/pseudo", O_RDWR);
2101 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2105 /* Make non-blocking */
2106 flags = fcntl(fd, F_GETFL);
2108 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
2112 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
2113 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
2117 /* Setup buffering information */
2118 memset(&bi, 0, sizeof(bi));
2119 bi.bufsize = CONF_SIZE / 2;
2120 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2121 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2122 bi.numbufs = audio_buffers;
2123 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2124 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2129 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2130 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2136 /* XXX Make sure we're not running on a pseudo channel XXX */
2140 memset(&dahdic, 0, sizeof(dahdic));
2141 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2142 /* Check to see if we're in a conference... */
2144 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2145 ast_log(LOG_WARNING, "Error getting conference\n");
2149 if (dahdic.confmode) {
2150 /* Whoa, already in a conference... Retry... */
2152 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2157 memset(&dahdic, 0, sizeof(dahdic));
2158 /* Add us to the conference */
2160 dahdic.confno = conf->dahdiconf;
2162 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2163 struct announce_listitem *item;
2164 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2166 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2167 ast_copy_string(item->language, chan->language, sizeof(item->language));
2168 item->confchan = conf->chan;
2169 item->confusers = conf->users;
2170 item->announcetype = CONF_HASJOIN;
2171 ast_mutex_lock(&conf->announcelistlock);
2172 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2173 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2174 ast_cond_signal(&conf->announcelist_addition);
2175 ast_mutex_unlock(&conf->announcelistlock);
2177 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2183 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2184 dahdic.confmode = DAHDI_CONF_CONF;
2185 else if (confflags & CONFFLAG_MONITOR)
2186 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2187 else if (confflags & CONFFLAG_TALKER)
2188 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2190 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2192 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2193 ast_log(LOG_WARNING, "Error setting conference\n");
2197 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2200 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
2205 "CallerIDnum: %s\r\n"
2206 "CallerIDname: %s\r\n",
2207 chan->name, chan->uniqueid, conf->confno,
2209 S_OR(user->chan->cid.cid_num, "<unknown>"),
2210 S_OR(user->chan->cid.cid_name, "<unknown>")
2215 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2217 if (!(confflags & CONFFLAG_QUIET))
2218 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2219 conf_play(chan, conf, ENTER);
2222 conf_flush(fd, chan);
2224 if (confflags & CONFFLAG_AGI) {
2225 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2226 or use default filename of conf-background.agi */
2228 ast_channel_lock(chan);
2229 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2230 agifile = ast_strdupa(tmpvar);
2232 agifile = ast_strdupa(agifiledefault);
2234 ast_channel_unlock(chan);
2236 if (user->dahdichannel) {
2237 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2239 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2241 /* Find a pointer to the agi app and execute the script */
2242 agi_app = pbx_findapp("agi");
2244 ret = pbx_exec(chan, agi_app, agifile);
2246 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2249 if (user->dahdichannel) {
2250 /* Remove CONFMUTE mode on DAHDI channel */
2252 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2255 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2256 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2258 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2260 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2261 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2265 int menu_was_active = 0;
2271 if (rt_schedule && conf->endtime) {
2272 char currenttime[32];
2273 long localendtime = 0;
2276 struct ast_variable *var, *origvar;
2279 if (now.tv_sec % 60 == 0) {
2281 ast_localtime(&now, &tm, NULL);
2282 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2283 var = origvar = ast_load_realtime("meetme", "confno",
2284 conf->confno, "starttime <=", currenttime,
2285 "endtime >=", currenttime, NULL);
2287 for ( ; var; var = var->next) {
2288 if (!strcasecmp(var->name, "endtime")) {
2289 struct ast_tm endtime_tm;
2290 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2291 tmp = ast_mktime(&endtime_tm, NULL);
2292 localendtime = tmp.tv_sec;
2295 ast_variables_destroy(origvar);
2297 /* A conference can be extended from the
2298 Admin/User menu or by an external source */
2299 if (localendtime > conf->endtime){
2300 conf->endtime = localendtime;
2304 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2305 ast_verbose("Quitting time...\n");
2309 if (!announcement_played && conf->endalert) {
2310 if (now.tv_sec + conf->endalert >= conf->endtime) {
2311 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2312 ast_waitstream(chan, "");
2313 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2314 if (!ast_streamfile(chan, "minutes", chan->language))
2315 ast_waitstream(chan, "");
2316 announcement_played = 1;
2321 announcement_played = 0;
2331 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2336 if (user->timelimit) {
2337 int minutes = 0, seconds = 0, remain = 0;
2339 to = ast_tvdiff_ms(nexteventts, now);
2343 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2344 if (time_left_ms < to) {
2348 if (time_left_ms <= 0) {
2349 if (user->end_sound) {
2350 res = ast_streamfile(chan, user->end_sound, chan->language);
2351 res = ast_waitstream(chan, "");
2357 if (time_left_ms >= 5000) {
2359 remain = (time_left_ms + 500) / 1000;
2360 if (remain / 60 >= 1) {
2361 minutes = remain / 60;
2362 seconds = remain % 60;
2367 /* force the time left to round up if appropriate */
2368 if (user->warning_sound && user->play_warning) {
2369 if (!strcmp(user->warning_sound, "timeleft")) {
2371 res = ast_streamfile(chan, "vm-youhave", chan->language);
2372 res = ast_waitstream(chan, "");
2374 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2375 res = ast_streamfile(chan, "queue-minutes", chan->language);
2376 res = ast_waitstream(chan, "");
2379 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2380 res = ast_streamfile(chan, "queue-seconds", chan->language);
2381 res = ast_waitstream(chan, "");
2384 res = ast_streamfile(chan, user->warning_sound, chan->language);
2385 res = ast_waitstream(chan, "");
2389 if (user->warning_freq) {
2390 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2392 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2398 if (timeout && now.tv_sec >= timeout) {
2402 /* if we have just exited from the menu, and the user had a channel-driver
2403 volume adjustment, restore it
2405 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2406 set_talk_volume(user, user->listen.desired);
2409 menu_was_active = menu_active;
2411 currentmarked = conf->markedusers;
2412 if (!(confflags & CONFFLAG_QUIET) &&
2413 (confflags & CONFFLAG_MARKEDUSER) &&
2414 (confflags & CONFFLAG_WAITMARKED) &&
2416 if (currentmarked == 1 && conf->users > 1) {
2417 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2418 if (conf->users - 1 == 1) {
2419 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2420 ast_waitstream(chan, "");
2423 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2424 ast_waitstream(chan, "");
2428 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2429 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2430 ast_waitstream(chan, "");
2435 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2437 /* Update the struct with the actual confflags */
2438 user->userflags = confflags;
2440 if (confflags & CONFFLAG_WAITMARKED) {
2441 if (currentmarked == 0) {
2442 if (lastmarked != 0) {
2443 if (!(confflags & CONFFLAG_QUIET)) {
2444 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2445 ast_waitstream(chan, "");
2448 if (confflags & CONFFLAG_MARKEDEXIT) {
2449 if (confflags & CONFFLAG_KICK_CONTINUE) {
2454 dahdic.confmode = DAHDI_CONF_CONF;
2455 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2456 ast_log(LOG_WARNING, "Error setting conference\n");
2462 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2463 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2466 } else if (currentmarked >= 1 && lastmarked == 0) {
2467 /* Marked user entered, so cancel timeout */
2469 if (confflags & CONFFLAG_MONITOR) {
2470 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2471 } else if (confflags & CONFFLAG_TALKER) {
2472 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2474 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2476 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2477 ast_log(LOG_WARNING, "Error setting conference\n");
2481 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2485 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2486 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2487 ast_waitstream(chan, "");
2489 conf_play(chan, conf, ENTER);
2494 /* trying to add moh for single person conf */
2495 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2496 if (conf->users == 1) {
2498 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2509 /* Leave if the last marked user left */
2510 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2511 if (confflags & CONFFLAG_KICK_CONTINUE) {
2519 /* Check if my modes have changed */
2521 /* If I should be muted but am still talker, mute me */
2522 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2523 dahdic.confmode ^= DAHDI_CONF_TALKER;
2524 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2525 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2530 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2536 chan->name, chan->uniqueid, conf->confno, user->user_no);
2539 /* If I should be un-muted but am not talker, un-mute me */
2540 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2541 dahdic.confmode |= DAHDI_CONF_TALKER;
2542 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2543 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2548 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2554 chan->name, chan->uniqueid, conf->confno, user->user_no);
2557 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2558 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2559 talkreq_manager = 1;
2561 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2567 chan->name, chan->uniqueid, conf->confno, user->user_no);
2571 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2572 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2573 talkreq_manager = 0;
2574 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2580 chan->name, chan->uniqueid, conf->confno, user->user_no);
2583 /* If I have been kicked, exit the conference */
2584 if (user->adminflags & ADMINFLAG_KICKME) {
2585 /* You have been kicked. */
2586 if (!(confflags & CONFFLAG_QUIET) &&
2587 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2588 ast_waitstream(chan, "");
2594 /* Perform an extra hangup check just in case */
2595 if (ast_check_hangup(chan)) {
2600 char dtmfstr[2] = "";
2602 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2604 /* Kill old pseudo */
2608 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2609 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2610 user->dahdichannel = !retrydahdi;
2613 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2614 f = ast_read_noaudio(c);
2621 if (f->frametype == AST_FRAME_DTMF) {
2622 dtmfstr[0] = f->subclass;
2626 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2627 if (user->talk.actual) {
2628 ast_frame_adjust_volume(f, user->talk.actual);
2631 if (!(confflags & CONFFLAG_MONITOR)) {
2634 if (user->talking == -1) {
2638 res = ast_dsp_silence(dsp, f, &totalsilence);
2639 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2641 if (confflags & CONFFLAG_MONITORTALKER)
2642 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2648 chan->name, chan->uniqueid, conf->confno, user->user_no);
2650 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2652 if (confflags & CONFFLAG_MONITORTALKER) {
2653 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2659 chan->name, chan->uniqueid, conf->confno, user->user_no);
2664 /* Absolutely do _not_ use careful_write here...
2665 it is important that we read data from the channel
2666 as fast as it arrives, and feed it into the conference.
2667 The buffering in the pseudo channel will take care of any
2668 timing differences, unless they are so drastic as to lose
2669 audio frames (in which case carefully writing would only
2670 have delayed the audio even further).
2672 /* As it turns out, we do want to use careful write. We just
2673 don't want to block, but we do want to at least *try*
2674 to write out all the samples.
2676 if (user->talking) {
2677 careful_write(fd, f->data.ptr, f->datalen, 0);
2680 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2681 if (confflags & CONFFLAG_PASS_DTMF) {
2682 conf_queue_dtmf(conf, user, f);
2684 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2685 ast_log(LOG_WARNING, "Error setting conference\n");
2691 /* if we are entering the menu, and the user has a channel-driver
2692 volume adjustment, clear it
2694 if (!menu_active && user->talk.desired && !user->talk.actual) {
2695 set_talk_volume(user, 0);
2701 if ((confflags & CONFFLAG_ADMIN)) {
2705 /* Record this sound! */
2706 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2707 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2708 ast_stopstream(chan);
2717 case '1': /* Un/Mute */
2720 /* for admin, change both admin and use flags */
2721 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2722 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2724 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2727 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2728 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2729 ast_waitstream(chan, "");
2732 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2733 ast_waitstream(chan, "");
2737 case '2': /* Un/Lock the Conference */
2741 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2742 ast_waitstream(chan, "");
2746 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2747 ast_waitstream(chan, "");
2751 case '3': /* Eject last user */
2753 usr = AST_LIST_LAST(&conf->userlist);
2754 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2755 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2756 ast_waitstream(chan, "");
2759 usr->adminflags |= ADMINFLAG_KICKME;
2761 ast_stopstream(chan);
2764 tweak_listen_volume(user, VOL_DOWN);
2767 /* Extend RT conference */
2769 if (!rt_extend_conf(conf->confno)) {
2770 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2771 ast_waitstream(chan, "");
2774 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2775 ast_waitstream(chan, "");
2778 ast_stopstream(chan);
2783 tweak_listen_volume(user, VOL_UP);
2786 tweak_talk_volume(user, VOL_DOWN);
2792 tweak_talk_volume(user, VOL_UP);
2796 /* Play an error message! */
2797 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2798 ast_waitstream(chan, "");
2807 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2808 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2809 ast_stopstream(chan);
2818 case '1': /* Un/Mute */
2821 /* user can only toggle the self-muted state */
2822 user->adminflags ^= ADMINFLAG_SELFMUTED;
2824 /* they can't override the admin mute state */
2825 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2826 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2827 ast_waitstream(chan, "");
2830 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2831 ast_waitstream(chan, "");
2837 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2838 user->adminflags |= ADMINFLAG_T_REQUEST;
2841 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2842 if (!ast_streamfile(chan, "beep", chan->language)) {
2843 ast_waitstream(chan, "");
2848 tweak_listen_volume(user, VOL_DOWN);
2851 /* Extend RT conference */
2853 rt_extend_conf(conf->confno);
2858 tweak_listen_volume(user, VOL_UP);
2861 tweak_talk_volume(user, VOL_DOWN);
2867 tweak_talk_volume(user, VOL_UP);
2871 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2872 ast_waitstream(chan, "");
2879 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2882 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2883 ast_log(LOG_WARNING, "Error setting conference\n");
2889 conf_flush(fd, chan);
2890 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2891 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2892 if (confflags & CONFFLAG_PASS_DTMF) {
2893 conf_queue_dtmf(conf, user, f);
2896 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2897 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2902 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2904 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2905 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2907 if (confflags & CONFFLAG_PASS_DTMF) {
2908 conf_queue_dtmf(conf, user, f);
2913 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2914 && confflags & CONFFLAG_PASS_DTMF) {
2915 conf_queue_dtmf(conf, user, f);
2916 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2917 switch (f->subclass) {
2918 case AST_CONTROL_HOLD:
2919 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2924 } else if (f->frametype == AST_FRAME_NULL) {
2925 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2928 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2929 chan->name, f->frametype, f->subclass);
2932 } else if (outfd > -1) {
2933 res = read(outfd, buf, CONF_SIZE);
2935 memset(&fr, 0, sizeof(fr));
2936 fr.frametype = AST_FRAME_VOICE;
2937 fr.subclass = AST_FORMAT_SLINEAR;
2939 fr.samples = res / 2;
2941 fr.offset = AST_FRIENDLY_OFFSET;
2942 if (!user->listen.actual &&
2943 ((confflags & CONFFLAG_MONITOR) ||
2944 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2945 (!user->talking)) ) {
2947 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2948 if (chan->rawwriteformat & (1 << idx)) {
2952 if (idx >= AST_FRAME_BITS) {
2953 goto bailoutandtrynormal;
2955 ast_mutex_lock(&conf->listenlock);
2956 if (!conf->transframe[idx]) {
2957 if (conf->origframe) {
2958 if (!conf->transpath[idx]) {
2959 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2961 if (conf->transpath[idx]) {
2962 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2963 if (!conf->transframe[idx]) {
2964 conf->transframe[idx] = &ast_null_frame;
2969 if (conf->transframe[idx]) {
2970 if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2971 if (ast_write(chan, conf->transframe[idx])) {
2972 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2976 ast_mutex_unlock(&conf->listenlock);
2977 goto bailoutandtrynormal;
2979 ast_mutex_unlock(&conf->listenlock);
2981 bailoutandtrynormal:
2982 if (user->listen.actual) {
2983 ast_frame_adjust_volume(&fr, user->listen.actual);
2985 if (ast_write(chan, &fr) < 0) {
2986 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2990 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2993 lastmarked = currentmarked;
3004 /* Take out of conference */
3007 dahdic.confmode = 0;
3008 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3009 ast_log(LOG_WARNING, "Error setting conference\n");
3013 reset_volumes(user);
3015 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
3016 conf_play(chan, conf, LEAVE);
3019 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
3020 struct announce_listitem *item;
3021 if (!(item = ao2_alloc(sizeof(*item), NULL)))
3023 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3024 ast_copy_string(item->language, chan->language, sizeof(item->language));
3025 item->confchan = conf->chan;
3026 item->confusers = conf->users;
3027 item->announcetype = CONF_HASLEFT;
3028 ast_mutex_lock(&conf->announcelistlock);
3029 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3030 ast_cond_signal(&conf->announcelist_addition);
3031 ast_mutex_unlock(&conf->announcelistlock);
3032 } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
3033 /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
3034 ast_filedelete(user->namerecloc, NULL);
3038 AST_LIST_LOCK(&confs);
3044 if (user->user_no) { /* Only cleanup users who really joined! */
3046 hr = (now.tv_sec - user->jointime) / 3600;
3047 min = ((now.tv_sec - user->jointime) % 3600) / 60;
3048 sec = (now.tv_sec - user->jointime) % 60;
3051 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",