2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
33 <depend>dahdi</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <dahdi/user.h>
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/devicestate.h"
58 #include "asterisk/dial.h"
59 #include "asterisk/causes.h"
60 #include "asterisk/paths.h"
65 #define CONFIG_FILE_NAME "meetme.conf"
66 #define SLA_CONFIG_FILE "sla.conf"
68 /*! each buffer is 20ms, so this is 640ms total */
69 #define DEFAULT_AUDIO_BUFFERS 32
71 /*! String format for scheduled conferences */
72 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
75 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
76 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
77 ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
78 /*! User has requested to speak */
79 ADMINFLAG_T_REQUEST = (1 << 4),
82 #define MEETME_DELAYDETECTTALK 300
83 #define MEETME_DELAYDETECTENDTALK 1000
85 #define AST_FRAME_BITS 32
97 enum recording_state {
99 MEETME_RECORD_STARTED,
100 MEETME_RECORD_ACTIVE,
101 MEETME_RECORD_TERMINATE
104 #define CONF_SIZE 320
107 /*! user has admin access on the conference */
108 CONFFLAG_ADMIN = (1 << 0),
109 /*! If set the user can only receive audio from the conference */
110 CONFFLAG_MONITOR = (1 << 1),
111 /*! If set asterisk will exit conference when key defined in p() option is pressed */
112 CONFFLAG_KEYEXIT = (1 << 2),
113 /*! If set asterisk will provide a menu to the user when '*' is pressed */
114 CONFFLAG_STARMENU = (1 << 3),
115 /*! If set the use can only send audio to the conference */
116 CONFFLAG_TALKER = (1 << 4),
117 /*! If set there will be no enter or leave sounds */
118 CONFFLAG_QUIET = (1 << 5),
119 /*! If set, when user joins the conference, they will be told the number
120 * of users that are already in */
121 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
122 /*! Set to run AGI Script in Background */
123 CONFFLAG_AGI = (1 << 7),
124 /*! Set to have music on hold when user is alone in conference */
125 CONFFLAG_MOH = (1 << 8),
126 /*! If set the MeetMe will return if all marked with this flag left */
127 CONFFLAG_MARKEDEXIT = (1 << 9),
128 /*! If set, the MeetMe will wait until a marked user enters */
129 CONFFLAG_WAITMARKED = (1 << 10),
130 /*! If set, the MeetMe will exit to the specified context */
131 CONFFLAG_EXIT_CONTEXT = (1 << 11),
132 /*! If set, the user will be marked */
133 CONFFLAG_MARKEDUSER = (1 << 12),
134 /*! If set, user will be ask record name on entry of conference */
135 CONFFLAG_INTROUSER = (1 << 13),
136 /*! If set, the MeetMe will be recorded */
137 CONFFLAG_RECORDCONF = (1<< 14),
138 /*! If set, the user will be monitored if the user is talking or not */
139 CONFFLAG_MONITORTALKER = (1 << 15),
140 CONFFLAG_DYNAMIC = (1 << 16),
141 CONFFLAG_DYNAMICPIN = (1 << 17),
142 CONFFLAG_EMPTY = (1 << 18),
143 CONFFLAG_EMPTYNOPIN = (1 << 19),
144 CONFFLAG_ALWAYSPROMPT = (1 << 20),
145 /*! If set, won't speak the extra prompt when the first person
146 * enters the conference */
147 CONFFLAG_NOONLYPERSON = (1 << 22),
148 /*! If set, user will be asked to record name on entry of conference
150 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
151 /*! If set, the user will be initially self-muted */
152 CONFFLAG_STARTMUTED = (1 << 24),
153 /*! Pass DTMF through the conference */
154 CONFFLAG_PASS_DTMF = (1 << 25),
155 CONFFLAG_SLA_STATION = (1 << 26),
156 CONFFLAG_SLA_TRUNK = (1 << 27),
157 /*! If set, the user should continue in the dialplan if kicked out */
158 CONFFLAG_KICK_CONTINUE = (1 << 28),
159 CONFFLAG_DURATION_STOP = (1 << 29),
160 CONFFLAG_DURATION_LIMIT = (1 << 30),
164 OPT_ARG_WAITMARKED = 0,
165 OPT_ARG_EXITKEYS = 1,
166 OPT_ARG_DURATION_STOP = 2,
167 OPT_ARG_DURATION_LIMIT = 3,
168 OPT_ARG_MOH_CLASS = 4,
169 OPT_ARG_ARRAY_SIZE = 5,
172 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
173 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
174 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
175 AST_APP_OPTION('b', CONFFLAG_AGI ),
176 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
177 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
178 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
179 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
180 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
181 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
182 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
183 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
184 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
185 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
186 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
187 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
188 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
189 AST_APP_OPTION('q', CONFFLAG_QUIET ),
190 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
191 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
192 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
193 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
194 AST_APP_OPTION('t', CONFFLAG_TALKER ),
195 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
196 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
197 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
198 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
199 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
200 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
203 static const char *app = "MeetMe";
204 static const char *app2 = "MeetMeCount";
205 static const char *app3 = "MeetMeAdmin";
206 static const char *app4 = "MeetMeChannelAdmin";
207 static const char *slastation_app = "SLAStation";
208 static const char *slatrunk_app = "SLATrunk";
210 static const char *synopsis = "MeetMe conference bridge";
211 static const char *synopsis2 = "MeetMe participant count";
212 static const char *synopsis3 = "MeetMe conference Administration";
213 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
214 static const char *slastation_synopsis = "Shared Line Appearance Station";
215 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
217 /* Lookup RealTime conferences based on confno and current time */
218 static int rt_schedule;
219 static int fuzzystart;
220 static int earlyalert;
224 /* Log participant count to the RealTime backend */
225 static int rt_log_members;
227 static const char *descrip =
228 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
229 "conference. If the conference number is omitted, the user will be prompted\n"
230 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
231 "is specified, by pressing '#'.\n"
232 "Please note: The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
233 " must be present for conferencing to operate properly. In addition, the chan_dahdi\n"
234 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
235 "The option string may contain zero or more of the following characters:\n"
236 " 'a' -- set admin mode\n"
237 " 'A' -- set marked mode\n"
238 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
239 " Default: conf-background.agi (Note: This does not work with\n"
240 " non-DAHDI channels in the same conference)\n"
241 " 'c' -- announce user(s) count on joining a conference\n"
242 " 'C' -- continue in dialplan when kicked out of conference\n"
243 " 'd' -- dynamically add conference\n"
244 " 'D' -- dynamically add conference, prompting for a PIN\n"
245 " 'e' -- select an empty conference\n"
246 " 'E' -- select an empty pinless conference\n"
247 " 'F' -- Pass DTMF through the conference.\n"
248 " 'i' -- announce user join/leave with review\n"
249 " 'I' -- announce user join/leave without review\n"
250 " 'l' -- set listen only mode (Listen only, no talking)\n"
251 " 'm' -- set initially muted\n"
253 " -- enable music on hold when the conference has a single caller.\n"
254 " Optionally, specify a musiconhold class to use. If one is not\n"
255 " provided, it will use the channel's currently set music class,\n"
257 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
258 " being muted, meaning (a) No encode is done on transmission and\n"
259 " (b) Received audio that is not registered as talking is omitted\n"
260 " causing no buildup in background noise\n"
262 " -- allow user to exit the conference by pressing '#' (default)\n"
263 " or any of the defined keys. If keys contain '*' this will override\n"
264 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
265 " 'P' -- always prompt for the pin even if it is specified\n"
266 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
267 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
268 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
269 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
271 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
272 " 't' -- set talk only mode. (Talk only, no listening)\n"
273 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
275 " -- wait until the marked user enters the conference\n"
276 " 'x' -- close the conference when last marked user exits\n"
277 " 'X' -- allow user to exit the conference by entering a valid single\n"
278 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
279 " if that variable is not defined.\n"
280 " '1' -- do not play message when first person enters\n"
281 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
282 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
283 " left. Repeat the warning every 'z' ms. The following special\n"
284 " variables can be used with this option:\n"
285 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
286 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
287 " The default is to say the time remaining.\n"
290 static const char *descrip2 =
291 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
292 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
293 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
294 "the channel, unless priority n+1 exists, in which case priority progress will\n"
298 static const char *descrip3 =
299 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
300 " 'e' -- Eject last user that joined\n"
301 " 'E' -- Extend conference end time, if scheduled\n"
302 " 'k' -- Kick one user out of conference\n"
303 " 'K' -- Kick all users out of conference\n"
304 " 'l' -- Unlock conference\n"
305 " 'L' -- Lock conference\n"
306 " 'm' -- Unmute one user\n"
307 " 'M' -- Mute one user\n"
308 " 'n' -- Unmute all users in the conference\n"
309 " 'N' -- Mute all non-admin users in the conference\n"
310 " 'r' -- Reset one user's volume settings\n"
311 " 'R' -- Reset all users volume settings\n"
312 " 's' -- Lower entire conference speaking volume\n"
313 " 'S' -- Raise entire conference speaking volume\n"
314 " 't' -- Lower one user's talk volume\n"
315 " 'T' -- Raise one user's talk volume\n"
316 " 'u' -- Lower one user's listen volume\n"
317 " 'U' -- Raise one user's listen volume\n"
318 " 'v' -- Lower entire conference listening volume\n"
319 " 'V' -- Raise entire conference listening volume\n"
320 " MeetMeAdmin will additionally set the variable MEETMEADMINSTATUS with one\n"
321 "of the following values:\n"
322 " 'NOPARSE' -- Invalid arguments\n"
323 " 'NOTFOUND' -- User specified was not found\n"
324 " 'FAILED' -- Another failure occurred\n"
325 " 'OK' -- The operation was completed successfully\n"
328 static const char *descrip4 =
329 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
330 "channel in any coference.\n"
331 " 'k' -- Kick the specified user out of the conference he is in\n"
332 " 'm' -- Unmute the specified user\n"
333 " 'M' -- Mute the specified user\n"
336 static const char *slastation_desc =
337 " SLAStation(<station name>):\n"
338 "This application should be executed by an SLA station. The argument depends\n"
339 "on how the call was initiated. If the phone was just taken off hook, then\n"
340 "the argument \"station\" should be just the station name. If the call was\n"
341 "initiated by pressing a line key, then the station name should be preceded\n"
342 "by an underscore and the trunk name associated with that line button.\n"
343 "For example: \"station1_line1\"."
344 " On exit, this application will set the variable SLASTATION_STATUS to\n"
345 "one of the following values:\n"
346 " FAILURE | CONGESTION | SUCCESS\n"
349 static const char *slatrunk_desc =
350 " SLATrunk(<trunk name>[,options]):\n"
351 "This application should be executed by an SLA trunk on an inbound call.\n"
352 "The channel calling this application should correspond to the SLA trunk\n"
353 "with the name \"trunk\" that is being passed as an argument.\n"
354 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
355 "one of the following values:\n"
356 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
357 " The available options are:\n"
358 " M[(<class>)] - Play back the specified MOH class instead of ringing\n"
361 #define MAX_CONFNUM 80
363 #define OPTIONS_LEN 32
365 /*! \brief The MeetMe Conference object */
366 struct ast_conference {
367 ast_mutex_t playlock; /*!< Conference specific lock (players) */
368 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
369 char confno[MAX_CONFNUM]; /*!< Conference */
370 struct ast_channel *chan; /*!< Announcements channel */
371 struct ast_channel *lchan; /*!< Listen/Record channel */
372 int fd; /*!< Announcements fd */
373 int dahdiconf; /*!< DAHDI Conf # */
374 int users; /*!< Number of active users */
375 int markedusers; /*!< Number of marked users */
376 int maxusers; /*!< Participant limit if scheduled */
377 int endalert; /*!< When to play conf ending message */
378 time_t start; /*!< Start time (s) */
379 int refcount; /*!< reference count of usage */
380 enum recording_state recording:2; /*!< recording status */
381 unsigned int isdynamic:1; /*!< Created on the fly? */
382 unsigned int locked:1; /*!< Is the conference locked? */
383 pthread_t recordthread; /*!< thread for recording */
384 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
385 pthread_attr_t attr; /*!< thread attribute */
386 char *recordingfilename; /*!< Filename to record the Conference into */
387 char *recordingformat; /*!< Format to record the Conference in */
388 char pin[MAX_PIN]; /*!< If protected by a PIN */
389 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
391 long endtime; /*!< When to end the conf if scheduled */
392 const char *useropts; /*!< RealTime user flags */
393 const char *adminopts; /*!< RealTime moderator flags */
394 const char *bookid; /*!< RealTime conference id */
395 struct ast_frame *transframe[32];
396 struct ast_frame *origframe;
397 struct ast_trans_pvt *transpath[32];
398 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
399 AST_LIST_ENTRY(ast_conference) list;
402 static AST_LIST_HEAD_STATIC(confs, ast_conference);
404 static unsigned int conf_map[1024] = {0, };
407 int desired; /*!< Desired volume adjustment */
408 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
411 /*! \brief The MeetMe User object */
412 struct ast_conf_user {
413 int user_no; /*!< User Number */
414 int userflags; /*!< Flags as set in the conference */
415 int adminflags; /*!< Flags set by the Admin */
416 struct ast_channel *chan; /*!< Connected channel */
417 int talking; /*!< Is user talking */
418 int dahdichannel; /*!< Is a DAHDI channel */
419 char usrvalue[50]; /*!< Custom User Value */
420 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
421 time_t jointime; /*!< Time the user joined the conference */
422 time_t kicktime; /*!< Time the user will be kicked from the conference */
423 struct timeval start_time; /*!< Time the user entered into the conference */
424 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
425 long play_warning; /*!< Play a warning when 'y' ms are left */
426 long warning_freq; /*!< Repeat the warning every 'z' ms */
427 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
428 const char *end_sound; /*!< File to play when time is up. */
430 struct volume listen;
431 AST_LIST_ENTRY(ast_conf_user) list;
434 enum sla_which_trunk_refs {
439 enum sla_trunk_state {
440 SLA_TRUNK_STATE_IDLE,
441 SLA_TRUNK_STATE_RINGING,
443 SLA_TRUNK_STATE_ONHOLD,
444 SLA_TRUNK_STATE_ONHOLD_BYME,
447 enum sla_hold_access {
448 /*! This means that any station can put it on hold, and any station
449 * can retrieve the call from hold. */
451 /*! This means that only the station that put the call on hold may
452 * retrieve it from hold. */
456 struct sla_trunk_ref;
459 AST_RWLIST_ENTRY(sla_station) entry;
460 AST_DECLARE_STRING_FIELDS(
461 AST_STRING_FIELD(name);
462 AST_STRING_FIELD(device);
463 AST_STRING_FIELD(autocontext);
465 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
466 struct ast_dial *dial;
467 /*! Ring timeout for this station, for any trunk. If a ring timeout
468 * is set for a specific trunk on this station, that will take
469 * priority over this value. */
470 unsigned int ring_timeout;
471 /*! Ring delay for this station, for any trunk. If a ring delay
472 * is set for a specific trunk on this station, that will take
473 * priority over this value. */
474 unsigned int ring_delay;
475 /*! This option uses the values in the sla_hold_access enum and sets the
476 * access control type for hold on this station. */
477 unsigned int hold_access:1;
478 /*! Use count for inside sla_station_exec */
479 unsigned int ref_count;
482 struct sla_station_ref {
483 AST_LIST_ENTRY(sla_station_ref) entry;
484 struct sla_station *station;
488 AST_RWLIST_ENTRY(sla_trunk) entry;
489 AST_DECLARE_STRING_FIELDS(
490 AST_STRING_FIELD(name);
491 AST_STRING_FIELD(device);
492 AST_STRING_FIELD(autocontext);
494 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
495 /*! Number of stations that use this trunk */
496 unsigned int num_stations;
497 /*! Number of stations currently on a call with this trunk */
498 unsigned int active_stations;
499 /*! Number of stations that have this trunk on hold. */
500 unsigned int hold_stations;
501 struct ast_channel *chan;
502 unsigned int ring_timeout;
503 /*! If set to 1, no station will be able to join an active call with
505 unsigned int barge_disabled:1;
506 /*! This option uses the values in the sla_hold_access enum and sets the
507 * access control type for hold on this trunk. */
508 unsigned int hold_access:1;
509 /*! Whether this trunk is currently on hold, meaning that once a station
510 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
511 unsigned int on_hold:1;
512 /*! Use count for inside sla_trunk_exec */
513 unsigned int ref_count;
516 struct sla_trunk_ref {
517 AST_LIST_ENTRY(sla_trunk_ref) entry;
518 struct sla_trunk *trunk;
519 enum sla_trunk_state state;
520 struct ast_channel *chan;
521 /*! Ring timeout to use when this trunk is ringing on this specific
522 * station. This takes higher priority than a ring timeout set at
523 * the station level. */
524 unsigned int ring_timeout;
525 /*! Ring delay to use when this trunk is ringing on this specific
526 * station. This takes higher priority than a ring delay set at
527 * the station level. */
528 unsigned int ring_delay;
531 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
532 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
534 static const char sla_registrar[] = "SLA";
536 /*! \brief Event types that can be queued up for the SLA thread */
537 enum sla_event_type {
538 /*! A station has put the call on hold */
540 /*! The state of a dial has changed */
541 SLA_EVENT_DIAL_STATE,
542 /*! The state of a ringing trunk has changed */
543 SLA_EVENT_RINGING_TRUNK,
544 /*! A reload of configuration has been requested */
546 /*! Poke the SLA thread so it can check if it can perform a reload */
547 SLA_EVENT_CHECK_RELOAD,
551 enum sla_event_type type;
552 struct sla_station *station;
553 struct sla_trunk_ref *trunk_ref;
554 AST_LIST_ENTRY(sla_event) entry;
557 /*! \brief A station that failed to be dialed
558 * \note Only used by the SLA thread. */
559 struct sla_failed_station {
560 struct sla_station *station;
561 struct timeval last_try;
562 AST_LIST_ENTRY(sla_failed_station) entry;
565 /*! \brief A trunk that is ringing */
566 struct sla_ringing_trunk {
567 struct sla_trunk *trunk;
568 /*! The time that this trunk started ringing */
569 struct timeval ring_begin;
570 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
571 AST_LIST_ENTRY(sla_ringing_trunk) entry;
574 enum sla_station_hangup {
575 SLA_STATION_HANGUP_NORMAL,
576 SLA_STATION_HANGUP_TIMEOUT,
579 /*! \brief A station that is ringing */
580 struct sla_ringing_station {
581 struct sla_station *station;
582 /*! The time that this station started ringing */
583 struct timeval ring_begin;
584 AST_LIST_ENTRY(sla_ringing_station) entry;
588 * \brief A structure for data used by the sla thread
591 /*! The SLA thread ID */
595 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
596 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
597 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
598 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
600 /*! Attempt to handle CallerID, even though it is known not to work
601 * properly in some situations. */
602 unsigned int attempt_callerid:1;
603 /*! A reload has been requested */
604 unsigned int reload:1;
606 .thread = AST_PTHREADT_NULL,
609 /*! The number of audio buffers to be allocated on pseudo channels
610 * when in a conference */
611 static int audio_buffers;
613 /*! Map 'volume' levels from -5 through +5 into
614 * decibel (dB) settings for channel drivers
615 * Note: these are not a straight linear-to-dB
616 * conversion... the numbers have been modified
617 * to give the user a better level of adjustability
619 static char const gain_map[] = {
634 static int admin_exec(struct ast_channel *chan, void *data);
635 static void *recordthread(void *args);
637 static char *istalking(int x)
642 return "(unmonitored)";
644 return "(not talking)";
647 static int careful_write(int fd, unsigned char *data, int len, int block)
654 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
655 res = ioctl(fd, DAHDI_IOMUX, &x);
659 res = write(fd, data, len);
661 if (errno != EAGAIN) {
662 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
674 static int set_talk_volume(struct ast_conf_user *user, int volume)
678 /* attempt to make the adjustment in the channel driver;
679 if successful, don't adjust in the frame reading routine
681 gain_adjust = gain_map[volume + 5];
683 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
686 static int set_listen_volume(struct ast_conf_user *user, int volume)
690 /* attempt to make the adjustment in the channel driver;
691 if successful, don't adjust in the frame reading routine
693 gain_adjust = gain_map[volume + 5];
695 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
698 static void tweak_volume(struct volume *vol, enum volume_action action)
702 switch (vol->desired) {
717 switch (vol->desired) {
733 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
735 tweak_volume(&user->talk, action);
736 /* attempt to make the adjustment in the channel driver;
737 if successful, don't adjust in the frame reading routine
739 if (!set_talk_volume(user, user->talk.desired))
740 user->talk.actual = 0;
742 user->talk.actual = user->talk.desired;
745 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
747 tweak_volume(&user->listen, action);
748 /* attempt to make the adjustment in the channel driver;
749 if successful, don't adjust in the frame reading routine
751 if (!set_listen_volume(user, user->listen.desired))
752 user->listen.actual = 0;
754 user->listen.actual = user->listen.desired;
757 static void reset_volumes(struct ast_conf_user *user)
759 signed char zero_volume = 0;
761 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
762 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
765 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
771 if (!ast_check_hangup(chan))
772 res = ast_autoservice_start(chan);
774 AST_LIST_LOCK(&confs);
790 careful_write(conf->fd, data, len, 1);
793 AST_LIST_UNLOCK(&confs);
796 ast_autoservice_stop(chan);
800 * \brief Find or create a conference
802 * \param confno The conference name/number
803 * \param pin The regular user pin
804 * \param pinadmin The admin pin
805 * \param make Make the conf if it doesn't exist
806 * \param dynamic Mark the newly created conference as dynamic
807 * \param refcount How many references to mark on the conference
808 * \param chan The asterisk channel
810 * \return A pointer to the conference struct, or NULL if it wasn't found and
811 * make or dynamic were not set.
813 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
815 struct ast_conference *cnf;
816 struct dahdi_confinfo dahdic = { 0, };
819 AST_LIST_LOCK(&confs);
821 AST_LIST_TRAVERSE(&confs, cnf, list) {
822 if (!strcmp(confno, cnf->confno))
826 if (cnf || (!make && !dynamic))
830 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
833 ast_mutex_init(&cnf->playlock);
834 ast_mutex_init(&cnf->listenlock);
835 cnf->recordthread = AST_PTHREADT_NULL;
836 ast_mutex_init(&cnf->recordthreadlock);
837 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
838 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
839 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
840 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
842 /* Setup a new dahdi conference */
844 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
845 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
846 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
847 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
855 cnf->dahdiconf = dahdic.confno;
857 /* Setup a new channel for playback of audio files */
858 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
860 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
861 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
863 dahdic.confno = cnf->dahdiconf;
864 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
865 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
866 ast_log(LOG_WARNING, "Error setting conference\n");
868 ast_hangup(cnf->chan);
878 /* Fill the conference struct */
879 cnf->start = time(NULL);
880 cnf->maxusers = 0x7fffffff;
881 cnf->isdynamic = dynamic ? 1 : 0;
882 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
883 AST_LIST_INSERT_HEAD(&confs, cnf, list);
885 /* Reserve conference number in map */
886 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
887 conf_map[confno_int] = 1;
891 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
893 AST_LIST_UNLOCK(&confs);
898 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
900 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
902 int len = strlen(word);
904 struct ast_conference *cnf = NULL;
905 struct ast_conf_user *usr = NULL;
908 char *myline, *ret = NULL;
910 if (pos == 1) { /* Command */
911 return ast_cli_complete(word, cmds, state);
912 } else if (pos == 2) { /* Conference Number */
913 AST_LIST_LOCK(&confs);
914 AST_LIST_TRAVERSE(&confs, cnf, list) {
915 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
920 ret = ast_strdup(ret); /* dup before releasing the lock */
921 AST_LIST_UNLOCK(&confs);
923 } else if (pos == 3) {
924 /* User Number || Conf Command option*/
925 if (strstr(line, "mute") || strstr(line, "kick")) {
926 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
927 return ast_strdup("all");
929 AST_LIST_LOCK(&confs);
931 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
932 myline = ast_strdupa(line);
933 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
934 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
938 AST_LIST_TRAVERSE(&confs, cnf, list) {
939 if (!strcmp(confno, cnf->confno))
944 /* Search for the user */
945 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
946 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
947 if (!strncasecmp(word, usrno, len) && ++which > state)
951 AST_LIST_UNLOCK(&confs);
952 return usr ? ast_strdup(usrno) : NULL;
959 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
961 /* Process the command */
962 struct ast_conf_user *user;
963 struct ast_conference *cnf;
965 int i = 0, total = 0;
967 struct ast_str *cmdline = NULL;
968 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
969 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
973 e->command = "meetme list [concise]";
975 "Usage: meetme list [concise] <confno> \n"
976 " List all or a specific conference.\n";
979 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
982 /* Check for length so no buffer will overflow... */
983 for (i = 0; i < a->argc; i++) {
984 if (strlen(a->argv[i]) > 100)
985 ast_cli(a->fd, "Invalid Arguments.\n");
988 /* Max confno length */
989 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
993 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
994 /* List all the conferences */
995 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
997 AST_LIST_LOCK(&confs);
998 if (AST_LIST_EMPTY(&confs)) {
1000 ast_cli(a->fd, "No active MeetMe conferences.\n");
1002 AST_LIST_UNLOCK(&confs);
1007 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1009 AST_LIST_TRAVERSE(&confs, cnf, list) {
1010 if (cnf->markedusers == 0) {
1011 ast_str_set(&cmdline, 0, "N/A ");
1013 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1015 hr = (now - cnf->start) / 3600;
1016 min = ((now - cnf->start) % 3600) / 60;
1017 sec = (now - cnf->start) % 60;
1019 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1021 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1030 total += cnf->users;
1032 AST_LIST_UNLOCK(&confs);
1034 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1038 } else if (strcmp(a->argv[1], "list") == 0) {
1039 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1040 /* List all the users in a conference */
1041 if (AST_LIST_EMPTY(&confs)) {
1043 ast_cli(a->fd, "No active MeetMe conferences.\n");
1048 /* Find the right conference */
1049 AST_LIST_LOCK(&confs);
1050 AST_LIST_TRAVERSE(&confs, cnf, list) {
1051 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1057 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1058 AST_LIST_UNLOCK(&confs);
1062 /* Show all the users */
1064 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1065 hr = (now - user->jointime) / 3600;
1066 min = ((now - user->jointime) % 3600) / 60;
1067 sec = (now - user->jointime) % 60;
1069 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1071 S_OR(user->chan->cid.cid_num, "<unknown>"),
1072 S_OR(user->chan->cid.cid_name, "<no name>"),
1074 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1075 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1076 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1077 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1078 istalking(user->talking), hr, min, sec);
1080 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1082 S_OR(user->chan->cid.cid_num, ""),
1083 S_OR(user->chan->cid.cid_name, ""),
1085 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1086 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1087 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1088 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1089 user->talking, hr, min, sec);
1093 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1095 AST_LIST_UNLOCK(&confs);
1101 return CLI_SHOWUSAGE;
1104 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1106 admin_exec(NULL, cmdline->str);
1113 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1115 /* Process the command */
1116 struct ast_str *cmdline = NULL;
1121 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1123 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1124 " Executes a command for the conference or on a conferee\n";
1127 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1131 ast_cli(a->fd, "Invalid Arguments.\n");
1132 /* Check for length so no buffer will overflow... */
1133 for (i = 0; i < a->argc; i++) {
1134 if (strlen(a->argv[i]) > 100)
1135 ast_cli(a->fd, "Invalid Arguments.\n");
1138 /* Max confno length */
1139 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1145 return CLI_SHOWUSAGE;
1148 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1149 if (strstr(a->argv[1], "lock")) {
1150 if (strcmp(a->argv[1], "lock") == 0) {
1152 ast_str_append(&cmdline, 0, ",L");
1155 ast_str_append(&cmdline, 0, ",l");
1157 } else if (strstr(a->argv[1], "mute")) {
1160 return CLI_SHOWUSAGE;
1162 if (strcmp(a->argv[1], "mute") == 0) {
1164 if (strcmp(a->argv[3], "all") == 0) {
1165 ast_str_append(&cmdline, 0, ",N");
1167 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1171 if (strcmp(a->argv[3], "all") == 0) {
1172 ast_str_append(&cmdline, 0, ",n");
1174 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1177 } else if (strcmp(a->argv[1], "kick") == 0) {
1180 return CLI_SHOWUSAGE;
1182 if (strcmp(a->argv[3], "all") == 0) {
1184 ast_str_append(&cmdline, 0, ",K");
1186 /* Kick a single user */
1187 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1191 return CLI_SHOWUSAGE;
1194 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1196 admin_exec(NULL, cmdline->str);
1202 static const char *sla_hold_str(unsigned int hold_access)
1204 const char *hold = "Unknown";
1206 switch (hold_access) {
1210 case SLA_HOLD_PRIVATE:
1219 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1221 const struct sla_trunk *trunk;
1225 e->command = "sla show trunks";
1227 "Usage: sla show trunks\n"
1228 " This will list all trunks defined in sla.conf\n";
1235 "=============================================================\n"
1236 "=== Configured SLA Trunks ===================================\n"
1237 "=============================================================\n"
1239 AST_RWLIST_RDLOCK(&sla_trunks);
1240 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1241 struct sla_station_ref *station_ref;
1242 char ring_timeout[16] = "(none)";
1243 if (trunk->ring_timeout)
1244 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1245 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1246 "=== Trunk Name: %s\n"
1247 "=== ==> Device: %s\n"
1248 "=== ==> AutoContext: %s\n"
1249 "=== ==> RingTimeout: %s\n"
1250 "=== ==> BargeAllowed: %s\n"
1251 "=== ==> HoldAccess: %s\n"
1252 "=== ==> Stations ...\n",
1253 trunk->name, trunk->device,
1254 S_OR(trunk->autocontext, "(none)"),
1256 trunk->barge_disabled ? "No" : "Yes",
1257 sla_hold_str(trunk->hold_access));
1258 AST_RWLIST_RDLOCK(&sla_stations);
1259 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1260 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1261 AST_RWLIST_UNLOCK(&sla_stations);
1262 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1264 AST_RWLIST_UNLOCK(&sla_trunks);
1265 ast_cli(a->fd, "=============================================================\n\n");
1270 static const char *trunkstate2str(enum sla_trunk_state state)
1272 #define S(e) case e: return # e;
1274 S(SLA_TRUNK_STATE_IDLE)
1275 S(SLA_TRUNK_STATE_RINGING)
1276 S(SLA_TRUNK_STATE_UP)
1277 S(SLA_TRUNK_STATE_ONHOLD)
1278 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1280 return "Uknown State";
1284 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1286 const struct sla_station *station;
1290 e->command = "sla show stations";
1292 "Usage: sla show stations\n"
1293 " This will list all stations defined in sla.conf\n";
1300 "=============================================================\n"
1301 "=== Configured SLA Stations =================================\n"
1302 "=============================================================\n"
1304 AST_RWLIST_RDLOCK(&sla_stations);
1305 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1306 struct sla_trunk_ref *trunk_ref;
1307 char ring_timeout[16] = "(none)";
1308 char ring_delay[16] = "(none)";
1309 if (station->ring_timeout) {
1310 snprintf(ring_timeout, sizeof(ring_timeout),
1311 "%u", station->ring_timeout);
1313 if (station->ring_delay) {
1314 snprintf(ring_delay, sizeof(ring_delay),
1315 "%u", station->ring_delay);
1317 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1318 "=== Station Name: %s\n"
1319 "=== ==> Device: %s\n"
1320 "=== ==> AutoContext: %s\n"
1321 "=== ==> RingTimeout: %s\n"
1322 "=== ==> RingDelay: %s\n"
1323 "=== ==> HoldAccess: %s\n"
1324 "=== ==> Trunks ...\n",
1325 station->name, station->device,
1326 S_OR(station->autocontext, "(none)"),
1327 ring_timeout, ring_delay,
1328 sla_hold_str(station->hold_access));
1329 AST_RWLIST_RDLOCK(&sla_trunks);
1330 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1331 if (trunk_ref->ring_timeout) {
1332 snprintf(ring_timeout, sizeof(ring_timeout),
1333 "%u", trunk_ref->ring_timeout);
1335 strcpy(ring_timeout, "(none)");
1336 if (trunk_ref->ring_delay) {
1337 snprintf(ring_delay, sizeof(ring_delay),
1338 "%u", trunk_ref->ring_delay);
1340 strcpy(ring_delay, "(none)");
1341 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1342 "=== ==> State: %s\n"
1343 "=== ==> RingTimeout: %s\n"
1344 "=== ==> RingDelay: %s\n",
1345 trunk_ref->trunk->name,
1346 trunkstate2str(trunk_ref->state),
1347 ring_timeout, ring_delay);
1349 AST_RWLIST_UNLOCK(&sla_trunks);
1350 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1353 AST_RWLIST_UNLOCK(&sla_stations);
1354 ast_cli(a->fd, "============================================================\n"
1360 static struct ast_cli_entry cli_meetme[] = {
1361 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1362 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1363 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1364 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1367 static void conf_flush(int fd, struct ast_channel *chan)
1371 /* read any frames that may be waiting on the channel
1375 struct ast_frame *f;
1377 /* when no frames are available, this will wait
1378 for 1 millisecond maximum
1380 while (ast_waitfor(chan, 1)) {
1384 else /* channel was hung up or something else happened */
1389 /* flush any data sitting in the pseudo channel */
1390 x = DAHDI_FLUSH_ALL;
1391 if (ioctl(fd, DAHDI_FLUSH, &x))
1392 ast_log(LOG_WARNING, "Error flushing channel\n");
1396 /* Remove the conference from the list and free it.
1397 We assume that this was called while holding conflock. */
1398 static int conf_free(struct ast_conference *conf)
1402 AST_LIST_REMOVE(&confs, conf, list);
1403 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1405 if (conf->recording == MEETME_RECORD_ACTIVE) {
1406 conf->recording = MEETME_RECORD_TERMINATE;
1407 AST_LIST_UNLOCK(&confs);
1410 AST_LIST_LOCK(&confs);
1411 if (conf->recording == MEETME_RECORD_OFF)
1413 AST_LIST_UNLOCK(&confs);
1417 for (x = 0; x < AST_FRAME_BITS; x++) {
1418 if (conf->transframe[x])
1419 ast_frfree(conf->transframe[x]);
1420 if (conf->transpath[x])
1421 ast_translator_free_path(conf->transpath[x]);
1423 if (conf->origframe)
1424 ast_frfree(conf->origframe);
1426 ast_hangup(conf->lchan);
1428 ast_hangup(conf->chan);
1431 if (conf->recordingfilename) {
1432 ast_free(conf->recordingfilename);
1434 if (conf->recordingformat) {
1435 ast_free(conf->recordingformat);
1437 ast_mutex_destroy(&conf->playlock);
1438 ast_mutex_destroy(&conf->listenlock);
1439 ast_mutex_destroy(&conf->recordthreadlock);
1445 static void conf_queue_dtmf(const struct ast_conference *conf,
1446 const struct ast_conf_user *sender, struct ast_frame *f)
1448 struct ast_conf_user *user;
1450 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1453 if (ast_write(user->chan, f) < 0)
1454 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1458 static void sla_queue_event_full(enum sla_event_type type,
1459 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1461 struct sla_event *event;
1463 if (!(event = ast_calloc(1, sizeof(*event))))
1467 event->trunk_ref = trunk_ref;
1468 event->station = station;
1471 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1475 ast_mutex_lock(&sla.lock);
1476 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1477 ast_cond_signal(&sla.cond);
1478 ast_mutex_unlock(&sla.lock);
1481 static void sla_queue_event_nolock(enum sla_event_type type)
1483 sla_queue_event_full(type, NULL, NULL, 0);
1486 static void sla_queue_event(enum sla_event_type type)
1488 sla_queue_event_full(type, NULL, NULL, 1);
1491 /*! \brief Queue a SLA event from the conference */
1492 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1493 struct ast_conference *conf)
1495 struct sla_station *station;
1496 struct sla_trunk_ref *trunk_ref = NULL;
1499 trunk_name = ast_strdupa(conf->confno);
1500 strsep(&trunk_name, "_");
1501 if (ast_strlen_zero(trunk_name)) {
1502 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1506 AST_RWLIST_RDLOCK(&sla_stations);
1507 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1508 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1509 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1515 AST_RWLIST_UNLOCK(&sla_stations);
1518 ast_debug(1, "Trunk not found for event!\n");
1522 sla_queue_event_full(type, trunk_ref, station, 1);
1525 /* Decrement reference counts, as incremented by find_conf() */
1526 static int dispose_conf(struct ast_conference *conf)
1531 AST_LIST_LOCK(&confs);
1532 if (ast_atomic_dec_and_test(&conf->refcount)) {
1533 /* Take the conference room number out of an inuse state */
1534 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1535 conf_map[confno_int] = 0;
1540 AST_LIST_UNLOCK(&confs);
1545 static int rt_extend_conf(char *confno)
1547 char currenttime[32];
1551 struct ast_variable *var;
1560 ast_localtime(&now, &tm, NULL);
1561 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1563 var = ast_load_realtime("meetme", "confno",
1564 confno, "startTime<= ", currenttime,
1565 "endtime>= ", currenttime, NULL);
1567 /* Identify the specific RealTime conference */
1569 if (!strcasecmp(var->name, "bookid")) {
1570 ast_copy_string(bookid, var->value, sizeof(bookid));
1572 if (!strcasecmp(var->name, "endtime")) {
1573 ast_copy_string(endtime, var->value, sizeof(endtime));
1578 ast_variables_destroy(var);
1580 ast_strptime(endtime, DATE_FORMAT, &tm);
1581 now = ast_mktime(&tm, NULL);
1583 now.tv_sec += extendby;
1585 ast_localtime(&now, &tm, NULL);
1586 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1587 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1589 var = ast_load_realtime("meetme", "confno",
1590 confno, "startTime<= ", currenttime,
1591 "endtime>= ", currenttime, NULL);
1593 /* If there is no conflict with extending the conference, update the DB */
1595 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1596 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1601 ast_variables_destroy(var);
1605 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1609 ast_channel_lock(chan);
1610 original_moh = ast_strdupa(chan->musicclass);
1611 ast_string_field_set(chan, musicclass, musicclass);
1612 ast_channel_unlock(chan);
1614 ast_moh_start(chan, original_moh, NULL);
1616 ast_channel_lock(chan);
1617 ast_string_field_set(chan, musicclass, original_moh);
1618 ast_channel_unlock(chan);
1621 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1623 struct ast_conf_user *user = NULL;
1624 struct ast_conf_user *usr = NULL;
1626 struct dahdi_confinfo dahdic, dahdic_empty;
1627 struct ast_frame *f;
1628 struct ast_channel *c;
1629 struct ast_frame fr;
1637 int musiconhold = 0;
1640 int currentmarked = 0;
1643 int menu_active = 0;
1644 int talkreq_manager = 0;
1645 int using_pseudo = 0;
1650 int announcement_played = 0;
1652 struct ast_dsp *dsp = NULL;
1653 struct ast_app *agi_app;
1655 const char *agifiledefault = "conf-background.agi", *tmpvar;
1656 char meetmesecs[30] = "";
1657 char exitcontext[AST_MAX_CONTEXT] = "";
1658 char recordingtmp[AST_MAX_EXTENSION] = "";
1659 char members[10] = "";
1660 int dtmf, opt_waitmarked_timeout = 0;
1662 struct dahdi_bufferinfo bi;
1663 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1664 char *buf = __buf + AST_FRIENDLY_OFFSET;
1665 char *exitkeys = NULL;
1666 unsigned int calldurationlimit = 0;
1668 long play_warning = 0;
1669 long warning_freq = 0;
1670 const char *warning_sound = NULL;
1671 const char *end_sound = NULL;
1673 long time_left_ms = 0;
1674 struct timeval nexteventts = { 0, };
1676 int setusercount = 0;
1678 if (!(user = ast_calloc(1, sizeof(*user))))
1681 /* Possible timeout waiting for marked user */
1682 if ((confflags & CONFFLAG_WAITMARKED) &&
1683 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1684 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1685 (opt_waitmarked_timeout > 0)) {
1686 timeout = time(NULL) + opt_waitmarked_timeout;
1689 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1690 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1691 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1694 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1695 char *limit_str, *warning_str, *warnfreq_str;
1698 parse = optargs[OPT_ARG_DURATION_LIMIT];
1699 limit_str = strsep(&parse, ":");
1700 warning_str = strsep(&parse, ":");
1701 warnfreq_str = parse;
1703 timelimit = atol(limit_str);
1705 play_warning = atol(warning_str);
1707 warning_freq = atol(warnfreq_str);
1710 timelimit = play_warning = warning_freq = 0;
1711 warning_sound = NULL;
1712 } else if (play_warning > timelimit) {
1713 if (!warning_freq) {
1716 while (play_warning > timelimit)
1717 play_warning -= warning_freq;
1718 if (play_warning < 1)
1719 play_warning = warning_freq = 0;
1723 ast_channel_lock(chan);
1724 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1725 var = ast_strdupa(var);
1727 ast_channel_unlock(chan);
1729 warning_sound = var ? var : "timeleft";
1731 ast_channel_lock(chan);
1732 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1733 var = ast_strdupa(var);
1735 ast_channel_unlock(chan);
1737 end_sound = var ? var : NULL;
1739 /* undo effect of S(x) in case they are both used */
1740 calldurationlimit = 0;
1741 /* more efficient do it like S(x) does since no advanced opts */
1742 if (!play_warning && !end_sound && timelimit) {
1743 calldurationlimit = timelimit / 1000;
1744 timelimit = play_warning = warning_freq = 0;
1746 ast_debug(2, "Limit Data for this call:\n");
1747 ast_debug(2, "- timelimit = %ld\n", timelimit);
1748 ast_debug(2, "- play_warning = %ld\n", play_warning);
1749 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1750 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1751 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1756 if ((confflags & CONFFLAG_KEYEXIT)) {
1757 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1758 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1760 exitkeys = ast_strdupa("#"); /* Default */
1763 if (confflags & CONFFLAG_RECORDCONF) {
1764 if (!conf->recordingfilename) {
1766 ast_channel_lock(chan);
1767 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1768 conf->recordingfilename = ast_strdup(var);
1770 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1771 conf->recordingformat = ast_strdup(var);
1773 ast_channel_unlock(chan);
1774 if (!conf->recordingfilename) {
1775 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1776 conf->recordingfilename = ast_strdup(recordingtmp);
1778 if (!conf->recordingformat) {
1779 conf->recordingformat = ast_strdup("wav");
1781 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1782 conf->confno, conf->recordingfilename, conf->recordingformat);
1786 ast_mutex_lock(&conf->recordthreadlock);
1787 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1788 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1789 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1791 dahdic.confno = conf->dahdiconf;
1792 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1793 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1794 ast_log(LOG_WARNING, "Error starting listen channel\n");
1795 ast_hangup(conf->lchan);
1798 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1801 ast_mutex_unlock(&conf->recordthreadlock);
1803 time(&user->jointime);
1805 user->timelimit = timelimit;
1806 user->play_warning = play_warning;
1807 user->warning_freq = warning_freq;
1808 user->warning_sound = warning_sound;
1809 user->end_sound = end_sound;
1811 if (calldurationlimit > 0) {
1812 time(&user->kicktime);
1813 user->kicktime = user->kicktime + calldurationlimit;
1816 if (ast_tvzero(user->start_time))
1817 user->start_time = ast_tvnow();
1818 time_left_ms = user->timelimit;
1820 if (user->timelimit) {
1821 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1822 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1825 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1826 /* Sorry, but this conference is locked! */
1827 if (!ast_streamfile(chan, "conf-locked", chan->language))
1828 ast_waitstream(chan, "");
1832 ast_mutex_lock(&conf->playlock);
1834 if (AST_LIST_EMPTY(&conf->userlist))
1837 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1839 if (rt_schedule && conf->maxusers)
1840 if (user->user_no > conf->maxusers) {
1841 /* Sorry, but this confernce has reached the participant limit! */
1842 if (!ast_streamfile(chan, "conf-full", chan->language))
1843 ast_waitstream(chan, "");
1847 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1850 user->userflags = confflags;
1851 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1854 ast_mutex_unlock(&conf->playlock);
1856 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1857 char destdir[PATH_MAX];
1859 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1861 if (ast_mkdir(destdir, 0777) != 0) {
1862 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1866 snprintf(user->namerecloc, sizeof(user->namerecloc),
1867 "%s/meetme-username-%s-%d", destdir,
1868 conf->confno, user->user_no);
1869 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1870 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1872 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1877 ast_mutex_lock(&conf->playlock);
1879 if (confflags & CONFFLAG_MARKEDUSER)
1880 conf->markedusers++;
1882 if (rt_log_members) {
1884 snprintf(members, sizeof(members), "%d", conf->users);
1885 ast_realtime_require_field("meetme",
1886 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
1887 "members", RQ_UINTEGER1, strlen(members),
1889 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1893 /* This device changed state now - if this is the first user */
1894 if (conf->users == 1)
1895 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1897 ast_mutex_unlock(&conf->playlock);
1899 /* return the unique ID of the conference */
1900 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1902 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1903 ast_channel_lock(chan);
1904 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
1905 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
1906 } else if (!ast_strlen_zero(chan->macrocontext)) {
1907 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1909 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1911 ast_channel_unlock(chan);
1914 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1915 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1916 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1917 ast_waitstream(chan, "");
1918 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1919 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1920 ast_waitstream(chan, "");
1923 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1924 int keepplaying = 1;
1926 if (conf->users == 2) {
1927 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1928 res = ast_waitstream(chan, AST_DIGIT_ANY);
1929 ast_stopstream(chan);
1936 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1937 res = ast_waitstream(chan, AST_DIGIT_ANY);
1938 ast_stopstream(chan);
1945 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1951 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1952 res = ast_waitstream(chan, AST_DIGIT_ANY);
1953 ast_stopstream(chan);
1962 ast_indicate(chan, -1);
1964 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1965 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1969 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1970 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1974 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1975 user->dahdichannel = !retrydahdi;
1978 origfd = chan->fds[0];
1980 fd = open("/dev/dahdi/pseudo", O_RDWR);
1982 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1986 /* Make non-blocking */
1987 flags = fcntl(fd, F_GETFL);
1989 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1993 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1994 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1998 /* Setup buffering information */
1999 memset(&bi, 0, sizeof(bi));
2000 bi.bufsize = CONF_SIZE / 2;
2001 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2002 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2003 bi.numbufs = audio_buffers;
2004 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2005 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2010 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2011 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2017 /* XXX Make sure we're not running on a pseudo channel XXX */
2021 memset(&dahdic, 0, sizeof(dahdic));
2022 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2023 /* Check to see if we're in a conference... */
2025 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2026 ast_log(LOG_WARNING, "Error getting conference\n");
2030 if (dahdic.confmode) {
2031 /* Whoa, already in a conference... Retry... */
2033 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2038 memset(&dahdic, 0, sizeof(dahdic));
2039 /* Add us to the conference */
2041 dahdic.confno = conf->dahdiconf;
2043 ast_mutex_lock(&conf->playlock);
2045 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2046 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
2047 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2048 ast_waitstream(conf->chan, "");
2049 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
2050 ast_waitstream(conf->chan, "");
2054 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2055 dahdic.confmode = DAHDI_CONF_CONF;
2056 else if (confflags & CONFFLAG_MONITOR)
2057 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2058 else if (confflags & CONFFLAG_TALKER)
2059 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2061 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2063 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2064 ast_log(LOG_WARNING, "Error setting conference\n");
2066 ast_mutex_unlock(&conf->playlock);
2069 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2072 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
2077 "CallerIDnum: %s\r\n"
2078 "CallerIDname: %s\r\n",
2079 chan->name, chan->uniqueid, conf->confno,
2081 S_OR(user->chan->cid.cid_num, "<unknown>"),
2082 S_OR(user->chan->cid.cid_name, "<unknown>")
2087 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2089 if (!(confflags & CONFFLAG_QUIET))
2090 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2091 conf_play(chan, conf, ENTER);
2094 ast_mutex_unlock(&conf->playlock);
2096 conf_flush(fd, chan);
2098 if (confflags & CONFFLAG_AGI) {
2099 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2100 or use default filename of conf-background.agi */
2102 ast_channel_lock(chan);
2103 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2104 agifile = ast_strdupa(tmpvar);
2106 agifile = ast_strdupa(agifiledefault);
2108 ast_channel_unlock(chan);
2110 if (user->dahdichannel) {
2111 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2113 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2115 /* Find a pointer to the agi app and execute the script */
2116 agi_app = pbx_findapp("agi");
2118 ret = pbx_exec(chan, agi_app, agifile);
2120 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2123 if (user->dahdichannel) {
2124 /* Remove CONFMUTE mode on DAHDI channel */
2126 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2129 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2130 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2132 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2134 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2135 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2139 int menu_was_active = 0;
2145 if (rt_schedule && conf->endtime) {
2146 char currenttime[32];
2147 long localendtime = 0;
2150 struct ast_variable *var, *origvar;
2153 if (now.tv_sec % 60 == 0) {
2155 ast_localtime(&now, &tm, NULL);
2156 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2157 var = origvar = ast_load_realtime("meetme", "confno",
2158 conf->confno, "starttime <=", currenttime,
2159 "endtime >=", currenttime, NULL);
2161 for ( ; var; var = var->next) {
2162 if (!strcasecmp(var->name, "endtime")) {
2163 struct ast_tm endtime_tm;
2164 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2165 tmp = ast_mktime(&endtime_tm, NULL);
2166 localendtime = tmp.tv_sec;
2169 ast_variables_destroy(origvar);
2171 /* A conference can be extended from the
2172 Admin/User menu or by an external source */
2173 if (localendtime > conf->endtime){
2174 conf->endtime = localendtime;
2178 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2179 ast_verbose("Quitting time...\n");
2183 if (!announcement_played && conf->endalert) {
2184 if (now.tv_sec + conf->endalert >= conf->endtime) {
2185 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2186 ast_waitstream(chan, "");
2187 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2188 if (!ast_streamfile(chan, "minutes", chan->language))
2189 ast_waitstream(chan, "");
2190 announcement_played = 1;
2195 announcement_played = 0;
2205 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2210 if (user->timelimit) {
2211 int minutes = 0, seconds = 0, remain = 0;
2213 to = ast_tvdiff_ms(nexteventts, now);
2217 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2218 if (time_left_ms < to) {
2222 if (time_left_ms <= 0) {
2223 if (user->end_sound) {
2224 res = ast_streamfile(chan, user->end_sound, chan->language);
2225 res = ast_waitstream(chan, "");
2231 if (time_left_ms >= 5000) {
2233 remain = (time_left_ms + 500) / 1000;
2234 if (remain / 60 >= 1) {
2235 minutes = remain / 60;
2236 seconds = remain % 60;
2241 /* force the time left to round up if appropriate */
2242 if (user->warning_sound && user->play_warning) {
2243 if (!strcmp(user->warning_sound, "timeleft")) {
2245 res = ast_streamfile(chan, "vm-youhave", chan->language);
2246 res = ast_waitstream(chan, "");
2248 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2249 res = ast_streamfile(chan, "queue-minutes", chan->language);
2250 res = ast_waitstream(chan, "");
2253 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2254 res = ast_streamfile(chan, "queue-seconds", chan->language);
2255 res = ast_waitstream(chan, "");
2258 res = ast_streamfile(chan, user->warning_sound, chan->language);
2259 res = ast_waitstream(chan, "");
2263 if (user->warning_freq) {
2264 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2266 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2272 if (timeout && now.tv_sec >= timeout) {
2276 /* if we have just exited from the menu, and the user had a channel-driver
2277 volume adjustment, restore it
2279 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2280 set_talk_volume(user, user->listen.desired);
2283 menu_was_active = menu_active;
2285 currentmarked = conf->markedusers;
2286 if (!(confflags & CONFFLAG_QUIET) &&
2287 (confflags & CONFFLAG_MARKEDUSER) &&
2288 (confflags & CONFFLAG_WAITMARKED) &&
2290 if (currentmarked == 1 && conf->users > 1) {
2291 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2292 if (conf->users - 1 == 1) {
2293 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2294 ast_waitstream(chan, "");
2297 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2298 ast_waitstream(chan, "");
2302 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2303 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2304 ast_waitstream(chan, "");
2309 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2311 /* Update the struct with the actual confflags */
2312 user->userflags = confflags;
2314 if (confflags & CONFFLAG_WAITMARKED) {
2315 if (currentmarked == 0) {
2316 if (lastmarked != 0) {
2317 if (!(confflags & CONFFLAG_QUIET)) {
2318 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2319 ast_waitstream(chan, "");
2322 if (confflags & CONFFLAG_MARKEDEXIT) {
2323 if (confflags & CONFFLAG_KICK_CONTINUE) {
2328 dahdic.confmode = DAHDI_CONF_CONF;
2329 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2330 ast_log(LOG_WARNING, "Error setting conference\n");
2336 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2337 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2340 } else if (currentmarked >= 1 && lastmarked == 0) {
2341 /* Marked user entered, so cancel timeout */
2343 if (confflags & CONFFLAG_MONITOR) {
2344 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2345 } else if (confflags & CONFFLAG_TALKER) {
2346 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2348 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2350 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2351 ast_log(LOG_WARNING, "Error setting conference\n");
2355 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2359 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2360 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2361 ast_waitstream(chan, "");
2363 conf_play(chan, conf, ENTER);
2368 /* trying to add moh for single person conf */
2369 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2370 if (conf->users == 1) {
2372 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2383 /* Leave if the last marked user left */
2384 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2385 if (confflags & CONFFLAG_KICK_CONTINUE) {
2393 /* Check if my modes have changed */
2395 /* If I should be muted but am still talker, mute me */
2396 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2397 dahdic.confmode ^= DAHDI_CONF_TALKER;
2398 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2399 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2404 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2410 chan->name, chan->uniqueid, conf->confno, user->user_no);
2413 /* If I should be un-muted but am not talker, un-mute me */
2414 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2415 dahdic.confmode |= DAHDI_CONF_TALKER;
2416 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2417 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2422 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2428 chan->name, chan->uniqueid, conf->confno, user->user_no);
2431 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2432 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2433 talkreq_manager = 1;
2435 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2441 chan->name, chan->uniqueid, conf->confno, user->user_no);
2445 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2446 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2447 talkreq_manager = 0;
2448 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2454 chan->name, chan->uniqueid, conf->confno, user->user_no);
2457 /* If I have been kicked, exit the conference */
2458 if (user->adminflags & ADMINFLAG_KICKME) {
2459 /* You have been kicked. */
2460 if (!(confflags & CONFFLAG_QUIET) &&
2461 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2462 ast_waitstream(chan, "");
2468 /* Perform an extra hangup check just in case */
2469 if (ast_check_hangup(chan)) {
2474 char dtmfstr[2] = "";
2476 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2478 /* Kill old pseudo */
2482 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2483 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2484 user->dahdichannel = !retrydahdi;
2487 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2488 f = ast_read_noaudio(c);
2495 if (f->frametype == AST_FRAME_DTMF) {
2496 dtmfstr[0] = f->subclass;
2500 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2501 if (user->talk.actual) {
2502 ast_frame_adjust_volume(f, user->talk.actual);
2505 if (!(confflags & CONFFLAG_MONITOR)) {
2508 if (user->talking == -1) {
2512 res = ast_dsp_silence(dsp, f, &totalsilence);
2513 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2515 if (confflags & CONFFLAG_MONITORTALKER)
2516 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2522 chan->name, chan->uniqueid, conf->confno, user->user_no);
2524 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2526 if (confflags & CONFFLAG_MONITORTALKER) {
2527 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2533 chan->name, chan->uniqueid, conf->confno, user->user_no);
2538 /* Absolutely do _not_ use careful_write here...
2539 it is important that we read data from the channel
2540 as fast as it arrives, and feed it into the conference.
2541 The buffering in the pseudo channel will take care of any
2542 timing differences, unless they are so drastic as to lose
2543 audio frames (in which case carefully writing would only
2544 have delayed the audio even further).
2546 /* As it turns out, we do want to use careful write. We just
2547 don't want to block, but we do want to at least *try*
2548 to write out all the samples.
2550 if (user->talking) {
2551 careful_write(fd, f->data.ptr, f->datalen, 0);
2554 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2555 if (confflags & CONFFLAG_PASS_DTMF) {
2556 conf_queue_dtmf(conf, user, f);
2558 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2559 ast_log(LOG_WARNING, "Error setting conference\n");
2565 /* if we are entering the menu, and the user has a channel-driver
2566 volume adjustment, clear it
2568 if (!menu_active && user->talk.desired && !user->talk.actual) {
2569 set_talk_volume(user, 0);
2575 if ((confflags & CONFFLAG_ADMIN)) {
2579 /* Record this sound! */
2580 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2581 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2582 ast_stopstream(chan);
2591 case '1': /* Un/Mute */
2594 /* for admin, change both admin and use flags */
2595 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2596 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2598 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2601 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2602 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2603 ast_waitstream(chan, "");
2606 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2607 ast_waitstream(chan, "");
2611 case '2': /* Un/Lock the Conference */
2615 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2616 ast_waitstream(chan, "");
2620 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2621 ast_waitstream(chan, "");
2625 case '3': /* Eject last user */
2627 usr = AST_LIST_LAST(&conf->userlist);
2628 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2629 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2630 ast_waitstream(chan, "");
2633 usr->adminflags |= ADMINFLAG_KICKME;
2635 ast_stopstream(chan);
2638 tweak_listen_volume(user, VOL_DOWN);
2641 /* Extend RT conference */
2643 if (!rt_extend_conf(conf->confno)) {
2644 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2645 ast_waitstream(chan, "");
2648 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2649 ast_waitstream(chan, "");
2652 ast_stopstream(chan);
2657 tweak_listen_volume(user, VOL_UP);
2660 tweak_talk_volume(user, VOL_DOWN);
2666 tweak_talk_volume(user, VOL_UP);
2670 /* Play an error message! */
2671 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2672 ast_waitstream(chan, "");
2681 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2682 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2683 ast_stopstream(chan);
2692 case '1': /* Un/Mute */
2695 /* user can only toggle the self-muted state */
2696 user->adminflags ^= ADMINFLAG_SELFMUTED;
2698 /* they can't override the admin mute state */
2699 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2700 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2701 ast_waitstream(chan, "");
2704 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2705 ast_waitstream(chan, "");
2711 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2712 user->adminflags |= ADMINFLAG_T_REQUEST;
2715 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2716 if (!ast_streamfile(chan, "beep", chan->language)) {
2717 ast_waitstream(chan, "");
2722 tweak_listen_volume(user, VOL_DOWN);
2725 /* Extend RT conference */
2727 rt_extend_conf(conf->confno);
2732 tweak_listen_volume(user, VOL_UP);
2735 tweak_talk_volume(user, VOL_DOWN);
2741 tweak_talk_volume(user, VOL_UP);
2745 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2746 ast_waitstream(chan, "");
2753 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2756 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2757 ast_log(LOG_WARNING, "Error setting conference\n");
2763 conf_flush(fd, chan);
2764 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2765 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2766 if (confflags & CONFFLAG_PASS_DTMF) {
2767 conf_queue_dtmf(conf, user, f);
2770 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2771 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2776 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2778 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2779 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2781 if (confflags & CONFFLAG_PASS_DTMF) {
2782 conf_queue_dtmf(conf, user, f);
2787 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2788 && confflags & CONFFLAG_PASS_DTMF) {
2789 conf_queue_dtmf(conf, user, f);
2790 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2791 switch (f->subclass) {
2792 case AST_CONTROL_HOLD:
2793 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2798 } else if (f->frametype == AST_FRAME_NULL) {
2799 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2802 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2803 chan->name, f->frametype, f->subclass);
2806 } else if (outfd > -1) {
2807 res = read(outfd, buf, CONF_SIZE);
2809 memset(&fr, 0, sizeof(fr));
2810 fr.frametype = AST_FRAME_VOICE;
2811 fr.subclass = AST_FORMAT_SLINEAR;
2813 fr.samples = res / 2;
2815 fr.offset = AST_FRIENDLY_OFFSET;
2816 if (!user->listen.actual &&
2817 ((confflags & CONFFLAG_MONITOR) ||
2818 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2819 (!user->talking)) ) {
2821 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2822 if (chan->rawwriteformat & (1 << idx)) {
2826 if (idx >= AST_FRAME_BITS) {
2827 goto bailoutandtrynormal;
2829 ast_mutex_lock(&conf->listenlock);
2830 if (!conf->transframe[idx]) {
2831 if (conf->origframe) {
2832 if (!conf->transpath[idx]) {
2833 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2835 if (conf->transpath[idx]) {
2836 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2837 if (!conf->transframe[idx]) {
2838 conf->transframe[idx] = &ast_null_frame;
2843 if (conf->transframe[idx]) {
2844 if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2845 if (ast_write(chan, conf->transframe[idx])) {
2846 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2850 ast_mutex_unlock(&conf->listenlock);
2851 goto bailoutandtrynormal;
2853 ast_mutex_unlock(&conf->listenlock);
2855 bailoutandtrynormal:
2856 if (user->listen.actual) {
2857 ast_frame_adjust_volume(&fr, user->listen.actual);
2859 if (ast_write(chan, &fr) < 0) {
2860 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2864 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2867 lastmarked = currentmarked;
2878 /* Take out of conference */
2881 dahdic.confmode = 0;
2882 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2883 ast_log(LOG_WARNING, "Error setting conference\n");
2887 reset_volumes(user);
2889 AST_LIST_LOCK(&confs);
2890 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2891 conf_play(chan, conf, LEAVE);
2894 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2895 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2896 if ((conf->chan) && (conf->users > 1)) {
2897 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language)) {
2898 ast_waitstream(conf->chan, "");
2900 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language)) {
2901 ast_waitstream(conf->chan, "");
2904 ast_filedelete(user->namerecloc, NULL);
2907 AST_LIST_UNLOCK(&confs);
2910 AST_LIST_LOCK(&confs);
2916 if (user->user_no) { /* Only cleanup users who really joined! */
2918 hr = (now.tv_sec - user->jointime) / 3600;
2919 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2920 sec = (now.tv_sec - user->jointime) % 60;
2923 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2928 "CallerIDNum: %s\r\n"
2929 "CallerIDName: %s\r\n"
2930 "Duration: %ld\r\n",
2931 chan->name, chan->uniqueid, conf->confno,
2933 S_OR(user->chan->cid.cid_num, "<unknown>"),
2934 S_OR(user->chan->cid.cid_name, "<unknown>"),
2935 (long)(now.tv_sec - user->jointime));
2940 if (rt_log_members) {
2942 snprintf(members, sizeof(members), "%d", conf->users);
2943 ast_realtime_require_field("meetme",
2944 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2945 "members", RQ_UINTEGER1, strlen(members),
2947 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2949 if (confflags & CONFFLAG_MARKEDUSER) {
2950 conf->markedusers--;
2953 /* Remove ourselves from the list */
2954 AST_LIST_REMOVE(&conf->userlist, user, list);
2956 /* Change any states */
2958 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2961 /* Return the number of seconds the user was in the conf */
2962 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2963 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2966 AST_LIST_UNLOCK(&confs);
2971 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2972 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags, int *too_early)
2974 struct ast_variable *var, *origvar;
2975 struct ast_conference *cnf;
2979 /* Check first in the conference list */
2980 AST_LIST_LOCK(&confs);
2981 AST_LIST_TRAVERSE(&confs, cnf, list) {
2982 if (!strcmp(confno, cnf->confno))
2986 cnf->refcount += refcount;
2988 AST_LIST_UNLOCK(&confs);
2991 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2994 char currenttime[19] = "";
2995 char eatime[19] = "";
2996 char bookid[19] = "";
2997 char recordingtmp[AST_MAX_EXTENSION] = "";
2998 char useropts[OPTIONS_LEN]; /* Used for RealTime conferences */
2999 char adminopts[OPTIONS_LEN];
3000 struct ast_tm tm, etm;
3001 struct timeval endtime = { .tv_sec = 0 };
3006 ast_localtime(&now, &tm, NULL);
3007 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3009 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
3011 var = ast_load_realtime("meetme", "confno",
3012 confno, "starttime <= ", currenttime, "endtime >= ",
3015 if (!var && fuzzystart) {
3017 now.tv_sec += fuzzystart;
3019 ast_localtime(&now, &tm, NULL);
3020 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3021 var = ast_load_realtime("meetme", "confno",
3022 confno, "starttime <= ", currenttime, "endtime >= ",
3026 if (!var && earlyalert) {
3028 now.tv_sec += earlyalert;
3029 ast_localtime(&now, &etm, NULL);
3030 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
3031 var = ast_load_realtime("meetme", "confno",
3032 confno, "starttime <= ", eatime, "endtime >= ",