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 struct ast_frame *transframe[32];
393 struct ast_frame *origframe;
394 struct ast_trans_pvt *transpath[32];
395 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
396 AST_LIST_ENTRY(ast_conference) list;
399 static AST_LIST_HEAD_STATIC(confs, ast_conference);
401 static unsigned int conf_map[1024] = {0, };
404 int desired; /*!< Desired volume adjustment */
405 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
408 /*! \brief The MeetMe User object */
409 struct ast_conf_user {
410 int user_no; /*!< User Number */
411 int userflags; /*!< Flags as set in the conference */
412 int adminflags; /*!< Flags set by the Admin */
413 struct ast_channel *chan; /*!< Connected channel */
414 int talking; /*!< Is user talking */
415 int dahdichannel; /*!< Is a DAHDI channel */
416 char usrvalue[50]; /*!< Custom User Value */
417 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
418 time_t jointime; /*!< Time the user joined the conference */
419 time_t kicktime; /*!< Time the user will be kicked from the conference */
420 struct timeval start_time; /*!< Time the user entered into the conference */
421 long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
422 long play_warning; /*!< Play a warning when 'y' ms are left */
423 long warning_freq; /*!< Repeat the warning every 'z' ms */
424 const char *warning_sound; /*!< File to play as warning if 'y' is defined */
425 const char *end_sound; /*!< File to play when time is up. */
427 struct volume listen;
428 AST_LIST_ENTRY(ast_conf_user) list;
431 enum sla_which_trunk_refs {
436 enum sla_trunk_state {
437 SLA_TRUNK_STATE_IDLE,
438 SLA_TRUNK_STATE_RINGING,
440 SLA_TRUNK_STATE_ONHOLD,
441 SLA_TRUNK_STATE_ONHOLD_BYME,
444 enum sla_hold_access {
445 /*! This means that any station can put it on hold, and any station
446 * can retrieve the call from hold. */
448 /*! This means that only the station that put the call on hold may
449 * retrieve it from hold. */
453 struct sla_trunk_ref;
456 AST_RWLIST_ENTRY(sla_station) entry;
457 AST_DECLARE_STRING_FIELDS(
458 AST_STRING_FIELD(name);
459 AST_STRING_FIELD(device);
460 AST_STRING_FIELD(autocontext);
462 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
463 struct ast_dial *dial;
464 /*! Ring timeout for this station, for any trunk. If a ring timeout
465 * is set for a specific trunk on this station, that will take
466 * priority over this value. */
467 unsigned int ring_timeout;
468 /*! Ring delay for this station, for any trunk. If a ring delay
469 * is set for a specific trunk on this station, that will take
470 * priority over this value. */
471 unsigned int ring_delay;
472 /*! This option uses the values in the sla_hold_access enum and sets the
473 * access control type for hold on this station. */
474 unsigned int hold_access:1;
475 /*! Use count for inside sla_station_exec */
476 unsigned int ref_count;
479 struct sla_station_ref {
480 AST_LIST_ENTRY(sla_station_ref) entry;
481 struct sla_station *station;
485 AST_RWLIST_ENTRY(sla_trunk) entry;
486 AST_DECLARE_STRING_FIELDS(
487 AST_STRING_FIELD(name);
488 AST_STRING_FIELD(device);
489 AST_STRING_FIELD(autocontext);
491 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
492 /*! Number of stations that use this trunk */
493 unsigned int num_stations;
494 /*! Number of stations currently on a call with this trunk */
495 unsigned int active_stations;
496 /*! Number of stations that have this trunk on hold. */
497 unsigned int hold_stations;
498 struct ast_channel *chan;
499 unsigned int ring_timeout;
500 /*! If set to 1, no station will be able to join an active call with
502 unsigned int barge_disabled:1;
503 /*! This option uses the values in the sla_hold_access enum and sets the
504 * access control type for hold on this trunk. */
505 unsigned int hold_access:1;
506 /*! Whether this trunk is currently on hold, meaning that once a station
507 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
508 unsigned int on_hold:1;
509 /*! Use count for inside sla_trunk_exec */
510 unsigned int ref_count;
513 struct sla_trunk_ref {
514 AST_LIST_ENTRY(sla_trunk_ref) entry;
515 struct sla_trunk *trunk;
516 enum sla_trunk_state state;
517 struct ast_channel *chan;
518 /*! Ring timeout to use when this trunk is ringing on this specific
519 * station. This takes higher priority than a ring timeout set at
520 * the station level. */
521 unsigned int ring_timeout;
522 /*! Ring delay to use when this trunk is ringing on this specific
523 * station. This takes higher priority than a ring delay set at
524 * the station level. */
525 unsigned int ring_delay;
528 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
529 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
531 static const char sla_registrar[] = "SLA";
533 /*! \brief Event types that can be queued up for the SLA thread */
534 enum sla_event_type {
535 /*! A station has put the call on hold */
537 /*! The state of a dial has changed */
538 SLA_EVENT_DIAL_STATE,
539 /*! The state of a ringing trunk has changed */
540 SLA_EVENT_RINGING_TRUNK,
541 /*! A reload of configuration has been requested */
543 /*! Poke the SLA thread so it can check if it can perform a reload */
544 SLA_EVENT_CHECK_RELOAD,
548 enum sla_event_type type;
549 struct sla_station *station;
550 struct sla_trunk_ref *trunk_ref;
551 AST_LIST_ENTRY(sla_event) entry;
554 /*! \brief A station that failed to be dialed
555 * \note Only used by the SLA thread. */
556 struct sla_failed_station {
557 struct sla_station *station;
558 struct timeval last_try;
559 AST_LIST_ENTRY(sla_failed_station) entry;
562 /*! \brief A trunk that is ringing */
563 struct sla_ringing_trunk {
564 struct sla_trunk *trunk;
565 /*! The time that this trunk started ringing */
566 struct timeval ring_begin;
567 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
568 AST_LIST_ENTRY(sla_ringing_trunk) entry;
571 enum sla_station_hangup {
572 SLA_STATION_HANGUP_NORMAL,
573 SLA_STATION_HANGUP_TIMEOUT,
576 /*! \brief A station that is ringing */
577 struct sla_ringing_station {
578 struct sla_station *station;
579 /*! The time that this station started ringing */
580 struct timeval ring_begin;
581 AST_LIST_ENTRY(sla_ringing_station) entry;
585 * \brief A structure for data used by the sla thread
588 /*! The SLA thread ID */
592 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
593 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
594 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
595 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
597 /*! Attempt to handle CallerID, even though it is known not to work
598 * properly in some situations. */
599 unsigned int attempt_callerid:1;
600 /*! A reload has been requested */
601 unsigned int reload:1;
603 .thread = AST_PTHREADT_NULL,
606 /*! The number of audio buffers to be allocated on pseudo channels
607 * when in a conference */
608 static int audio_buffers;
610 /*! Map 'volume' levels from -5 through +5 into
611 * decibel (dB) settings for channel drivers
612 * Note: these are not a straight linear-to-dB
613 * conversion... the numbers have been modified
614 * to give the user a better level of adjustability
616 static char const gain_map[] = {
631 static int admin_exec(struct ast_channel *chan, void *data);
632 static void *recordthread(void *args);
634 static char *istalking(int x)
639 return "(unmonitored)";
641 return "(not talking)";
644 static int careful_write(int fd, unsigned char *data, int len, int block)
651 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
652 res = ioctl(fd, DAHDI_IOMUX, &x);
656 res = write(fd, data, len);
658 if (errno != EAGAIN) {
659 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
671 static int set_talk_volume(struct ast_conf_user *user, int volume)
675 /* attempt to make the adjustment in the channel driver;
676 if successful, don't adjust in the frame reading routine
678 gain_adjust = gain_map[volume + 5];
680 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
683 static int set_listen_volume(struct ast_conf_user *user, int volume)
687 /* attempt to make the adjustment in the channel driver;
688 if successful, don't adjust in the frame reading routine
690 gain_adjust = gain_map[volume + 5];
692 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
695 static void tweak_volume(struct volume *vol, enum volume_action action)
699 switch (vol->desired) {
714 switch (vol->desired) {
730 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
732 tweak_volume(&user->talk, action);
733 /* attempt to make the adjustment in the channel driver;
734 if successful, don't adjust in the frame reading routine
736 if (!set_talk_volume(user, user->talk.desired))
737 user->talk.actual = 0;
739 user->talk.actual = user->talk.desired;
742 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
744 tweak_volume(&user->listen, action);
745 /* attempt to make the adjustment in the channel driver;
746 if successful, don't adjust in the frame reading routine
748 if (!set_listen_volume(user, user->listen.desired))
749 user->listen.actual = 0;
751 user->listen.actual = user->listen.desired;
754 static void reset_volumes(struct ast_conf_user *user)
756 signed char zero_volume = 0;
758 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
759 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
762 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
768 if (!ast_check_hangup(chan))
769 res = ast_autoservice_start(chan);
771 AST_LIST_LOCK(&confs);
787 careful_write(conf->fd, data, len, 1);
790 AST_LIST_UNLOCK(&confs);
793 ast_autoservice_stop(chan);
797 * \brief Find or create a conference
799 * \param confno The conference name/number
800 * \param pin The regular user pin
801 * \param pinadmin The admin pin
802 * \param make Make the conf if it doesn't exist
803 * \param dynamic Mark the newly created conference as dynamic
804 * \param refcount How many references to mark on the conference
805 * \param chan The asterisk channel
807 * \return A pointer to the conference struct, or NULL if it wasn't found and
808 * make or dynamic were not set.
810 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
812 struct ast_conference *cnf;
813 struct dahdi_confinfo dahdic = { 0, };
816 AST_LIST_LOCK(&confs);
818 AST_LIST_TRAVERSE(&confs, cnf, list) {
819 if (!strcmp(confno, cnf->confno))
823 if (cnf || (!make && !dynamic))
827 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
830 ast_mutex_init(&cnf->playlock);
831 ast_mutex_init(&cnf->listenlock);
832 cnf->recordthread = AST_PTHREADT_NULL;
833 ast_mutex_init(&cnf->recordthreadlock);
834 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
835 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
836 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
837 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
839 /* Setup a new dahdi conference */
841 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
842 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
843 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
844 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
852 cnf->dahdiconf = dahdic.confno;
854 /* Setup a new channel for playback of audio files */
855 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
857 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
858 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
860 dahdic.confno = cnf->dahdiconf;
861 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
862 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
863 ast_log(LOG_WARNING, "Error setting conference\n");
865 ast_hangup(cnf->chan);
875 /* Fill the conference struct */
876 cnf->start = time(NULL);
877 cnf->maxusers = 0x7fffffff;
878 cnf->isdynamic = dynamic ? 1 : 0;
879 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
880 AST_LIST_INSERT_HEAD(&confs, cnf, list);
882 /* Reserve conference number in map */
883 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
884 conf_map[confno_int] = 1;
888 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
890 AST_LIST_UNLOCK(&confs);
895 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
897 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
899 int len = strlen(word);
901 struct ast_conference *cnf = NULL;
902 struct ast_conf_user *usr = NULL;
905 char *myline, *ret = NULL;
907 if (pos == 1) { /* Command */
908 return ast_cli_complete(word, cmds, state);
909 } else if (pos == 2) { /* Conference Number */
910 AST_LIST_LOCK(&confs);
911 AST_LIST_TRAVERSE(&confs, cnf, list) {
912 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
917 ret = ast_strdup(ret); /* dup before releasing the lock */
918 AST_LIST_UNLOCK(&confs);
920 } else if (pos == 3) {
921 /* User Number || Conf Command option*/
922 if (strstr(line, "mute") || strstr(line, "kick")) {
923 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
924 return ast_strdup("all");
926 AST_LIST_LOCK(&confs);
928 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
929 myline = ast_strdupa(line);
930 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
931 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
935 AST_LIST_TRAVERSE(&confs, cnf, list) {
936 if (!strcmp(confno, cnf->confno))
941 /* Search for the user */
942 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
943 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
944 if (!strncasecmp(word, usrno, len) && ++which > state)
948 AST_LIST_UNLOCK(&confs);
949 return usr ? ast_strdup(usrno) : NULL;
950 } else if (strstr(line, "list") && (state == 0))
951 return ast_strdup("concise");
957 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
959 /* Process the command */
960 struct ast_conference *cnf;
962 int i = 0, total = 0;
964 struct ast_str *cmdline = NULL;
965 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
966 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
970 e->command = "meetme list [concise]";
972 "Usage: meetme list [concise] <confno> \n"
973 " List all or a specific conference.\n";
976 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
979 /* Check for length so no buffer will overflow... */
980 for (i = 0; i < a->argc; i++) {
981 if (strlen(a->argv[i]) > 100)
982 ast_cli(a->fd, "Invalid Arguments.\n");
985 /* Max confno length */
986 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
990 if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
991 /* 'MeetMe': List all the conferences */
992 int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
994 AST_LIST_LOCK(&confs);
995 if (AST_LIST_EMPTY(&confs)) {
997 ast_cli(a->fd, "No active MeetMe conferences.\n");
999 AST_LIST_UNLOCK(&confs);
1004 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1006 AST_LIST_TRAVERSE(&confs, cnf, list) {
1007 if (cnf->markedusers == 0) {
1008 ast_str_set(&cmdline, 0, "N/A ");
1010 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1012 hr = (now - cnf->start) / 3600;
1013 min = ((now - cnf->start) % 3600) / 60;
1014 sec = (now - cnf->start) % 60;
1016 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1018 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1027 total += cnf->users;
1029 AST_LIST_UNLOCK(&confs);
1031 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1038 return CLI_SHOWUSAGE;
1041 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1043 admin_exec(NULL, cmdline->str);
1050 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1052 /* Process the command */
1053 struct ast_conference *cnf;
1054 struct ast_conf_user *user;
1058 struct ast_str *cmdline = NULL;
1062 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1064 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1065 " Executes a command for the conference or on a conferee\n";
1068 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1072 ast_cli(a->fd, "Invalid Arguments.\n");
1073 /* Check for length so no buffer will overflow... */
1074 for (i = 0; i < a->argc; i++) {
1075 if (strlen(a->argv[i]) > 100)
1076 ast_cli(a->fd, "Invalid Arguments.\n");
1079 /* Max confno length */
1080 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1086 return CLI_SHOWUSAGE;
1089 ast_str_set(&cmdline, 0, "%s", a->argv[2]); /* Argv 2: conference number */
1090 if (strstr(a->argv[1], "lock")) {
1091 if (strcmp(a->argv[1], "lock") == 0) {
1093 ast_str_append(&cmdline, 0, ",L");
1096 ast_str_append(&cmdline, 0, ",l");
1098 } else if (strstr(a->argv[1], "mute")) {
1101 return CLI_SHOWUSAGE;
1103 if (strcmp(a->argv[1], "mute") == 0) {
1105 if (strcmp(a->argv[3], "all") == 0) {
1106 ast_str_append(&cmdline, 0, ",N");
1108 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
1112 if (strcmp(a->argv[3], "all") == 0) {
1113 ast_str_append(&cmdline, 0, ",n");
1115 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1118 } else if (strcmp(a->argv[1], "kick") == 0) {
1121 return CLI_SHOWUSAGE;
1123 if (strcmp(a->argv[3], "all") == 0) {
1125 ast_str_append(&cmdline, 0, ",K");
1127 /* Kick a single user */
1128 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1130 } else if (strcmp(a->argv[1], "list") == 0) {
1131 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1132 /* List all the users in a conference */
1133 if (AST_LIST_EMPTY(&confs)) {
1135 ast_cli(a->fd, "No active conferences.\n");
1140 /* Find the right conference */
1141 AST_LIST_LOCK(&confs);
1142 AST_LIST_TRAVERSE(&confs, cnf, list) {
1143 if (strcmp(cnf->confno, a->argv[2]) == 0) {
1149 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1150 AST_LIST_UNLOCK(&confs);
1154 /* Show all the users */
1156 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1157 hr = (now - user->jointime) / 3600;
1158 min = ((now - user->jointime) % 3600) / 60;
1159 sec = (now - user->jointime) % 60;
1161 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1163 S_OR(user->chan->cid.cid_num, "<unknown>"),
1164 S_OR(user->chan->cid.cid_name, "<no name>"),
1166 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1167 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1168 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1169 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1170 istalking(user->talking), hr, min, sec);
1172 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1174 S_OR(user->chan->cid.cid_num, ""),
1175 S_OR(user->chan->cid.cid_name, ""),
1177 user->userflags & CONFFLAG_ADMIN ? "1" : "",
1178 user->userflags & CONFFLAG_MONITOR ? "1" : "",
1179 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1180 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1181 user->talking, hr, min, sec);
1185 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1187 AST_LIST_UNLOCK(&confs);
1192 return CLI_SHOWUSAGE;
1195 ast_debug(1, "Cmdline: %s\n", cmdline->str);
1197 admin_exec(NULL, cmdline->str);
1203 static const char *sla_hold_str(unsigned int hold_access)
1205 const char *hold = "Unknown";
1207 switch (hold_access) {
1211 case SLA_HOLD_PRIVATE:
1220 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1222 const struct sla_trunk *trunk;
1226 e->command = "sla show trunks";
1228 "Usage: sla show trunks\n"
1229 " This will list all trunks defined in sla.conf\n";
1236 "=============================================================\n"
1237 "=== Configured SLA Trunks ===================================\n"
1238 "=============================================================\n"
1240 AST_RWLIST_RDLOCK(&sla_trunks);
1241 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1242 struct sla_station_ref *station_ref;
1243 char ring_timeout[16] = "(none)";
1244 if (trunk->ring_timeout)
1245 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1246 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1247 "=== Trunk Name: %s\n"
1248 "=== ==> Device: %s\n"
1249 "=== ==> AutoContext: %s\n"
1250 "=== ==> RingTimeout: %s\n"
1251 "=== ==> BargeAllowed: %s\n"
1252 "=== ==> HoldAccess: %s\n"
1253 "=== ==> Stations ...\n",
1254 trunk->name, trunk->device,
1255 S_OR(trunk->autocontext, "(none)"),
1257 trunk->barge_disabled ? "No" : "Yes",
1258 sla_hold_str(trunk->hold_access));
1259 AST_RWLIST_RDLOCK(&sla_stations);
1260 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1261 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
1262 AST_RWLIST_UNLOCK(&sla_stations);
1263 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1265 AST_RWLIST_UNLOCK(&sla_trunks);
1266 ast_cli(a->fd, "=============================================================\n\n");
1271 static const char *trunkstate2str(enum sla_trunk_state state)
1273 #define S(e) case e: return # e;
1275 S(SLA_TRUNK_STATE_IDLE)
1276 S(SLA_TRUNK_STATE_RINGING)
1277 S(SLA_TRUNK_STATE_UP)
1278 S(SLA_TRUNK_STATE_ONHOLD)
1279 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1281 return "Uknown State";
1285 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1287 const struct sla_station *station;
1291 e->command = "sla show stations";
1293 "Usage: sla show stations\n"
1294 " This will list all stations defined in sla.conf\n";
1301 "=============================================================\n"
1302 "=== Configured SLA Stations =================================\n"
1303 "=============================================================\n"
1305 AST_RWLIST_RDLOCK(&sla_stations);
1306 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1307 struct sla_trunk_ref *trunk_ref;
1308 char ring_timeout[16] = "(none)";
1309 char ring_delay[16] = "(none)";
1310 if (station->ring_timeout) {
1311 snprintf(ring_timeout, sizeof(ring_timeout),
1312 "%u", station->ring_timeout);
1314 if (station->ring_delay) {
1315 snprintf(ring_delay, sizeof(ring_delay),
1316 "%u", station->ring_delay);
1318 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1319 "=== Station Name: %s\n"
1320 "=== ==> Device: %s\n"
1321 "=== ==> AutoContext: %s\n"
1322 "=== ==> RingTimeout: %s\n"
1323 "=== ==> RingDelay: %s\n"
1324 "=== ==> HoldAccess: %s\n"
1325 "=== ==> Trunks ...\n",
1326 station->name, station->device,
1327 S_OR(station->autocontext, "(none)"),
1328 ring_timeout, ring_delay,
1329 sla_hold_str(station->hold_access));
1330 AST_RWLIST_RDLOCK(&sla_trunks);
1331 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1332 if (trunk_ref->ring_timeout) {
1333 snprintf(ring_timeout, sizeof(ring_timeout),
1334 "%u", trunk_ref->ring_timeout);
1336 strcpy(ring_timeout, "(none)");
1337 if (trunk_ref->ring_delay) {
1338 snprintf(ring_delay, sizeof(ring_delay),
1339 "%u", trunk_ref->ring_delay);
1341 strcpy(ring_delay, "(none)");
1342 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
1343 "=== ==> State: %s\n"
1344 "=== ==> RingTimeout: %s\n"
1345 "=== ==> RingDelay: %s\n",
1346 trunk_ref->trunk->name,
1347 trunkstate2str(trunk_ref->state),
1348 ring_timeout, ring_delay);
1350 AST_RWLIST_UNLOCK(&sla_trunks);
1351 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1354 AST_RWLIST_UNLOCK(&sla_stations);
1355 ast_cli(a->fd, "============================================================\n"
1361 static struct ast_cli_entry cli_meetme[] = {
1362 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1363 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1364 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1365 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1368 static void conf_flush(int fd, struct ast_channel *chan)
1372 /* read any frames that may be waiting on the channel
1376 struct ast_frame *f;
1378 /* when no frames are available, this will wait
1379 for 1 millisecond maximum
1381 while (ast_waitfor(chan, 1)) {
1385 else /* channel was hung up or something else happened */
1390 /* flush any data sitting in the pseudo channel */
1391 x = DAHDI_FLUSH_ALL;
1392 if (ioctl(fd, DAHDI_FLUSH, &x))
1393 ast_log(LOG_WARNING, "Error flushing channel\n");
1397 /* Remove the conference from the list and free it.
1398 We assume that this was called while holding conflock. */
1399 static int conf_free(struct ast_conference *conf)
1403 AST_LIST_REMOVE(&confs, conf, list);
1404 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1406 if (conf->recording == MEETME_RECORD_ACTIVE) {
1407 conf->recording = MEETME_RECORD_TERMINATE;
1408 AST_LIST_UNLOCK(&confs);
1411 AST_LIST_LOCK(&confs);
1412 if (conf->recording == MEETME_RECORD_OFF)
1414 AST_LIST_UNLOCK(&confs);
1418 for (x = 0; x < AST_FRAME_BITS; x++) {
1419 if (conf->transframe[x])
1420 ast_frfree(conf->transframe[x]);
1421 if (conf->transpath[x])
1422 ast_translator_free_path(conf->transpath[x]);
1424 if (conf->origframe)
1425 ast_frfree(conf->origframe);
1427 ast_hangup(conf->lchan);
1429 ast_hangup(conf->chan);
1432 if (conf->recordingfilename) {
1433 ast_free(conf->recordingfilename);
1435 if (conf->recordingformat) {
1436 ast_free(conf->recordingformat);
1438 ast_mutex_destroy(&conf->playlock);
1439 ast_mutex_destroy(&conf->listenlock);
1440 ast_mutex_destroy(&conf->recordthreadlock);
1446 static void conf_queue_dtmf(const struct ast_conference *conf,
1447 const struct ast_conf_user *sender, struct ast_frame *f)
1449 struct ast_conf_user *user;
1451 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1454 if (ast_write(user->chan, f) < 0)
1455 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1459 static void sla_queue_event_full(enum sla_event_type type,
1460 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1462 struct sla_event *event;
1464 if (!(event = ast_calloc(1, sizeof(*event))))
1468 event->trunk_ref = trunk_ref;
1469 event->station = station;
1472 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1476 ast_mutex_lock(&sla.lock);
1477 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1478 ast_cond_signal(&sla.cond);
1479 ast_mutex_unlock(&sla.lock);
1482 static void sla_queue_event_nolock(enum sla_event_type type)
1484 sla_queue_event_full(type, NULL, NULL, 0);
1487 static void sla_queue_event(enum sla_event_type type)
1489 sla_queue_event_full(type, NULL, NULL, 1);
1492 /*! \brief Queue a SLA event from the conference */
1493 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1494 struct ast_conference *conf)
1496 struct sla_station *station;
1497 struct sla_trunk_ref *trunk_ref = NULL;
1500 trunk_name = ast_strdupa(conf->confno);
1501 strsep(&trunk_name, "_");
1502 if (ast_strlen_zero(trunk_name)) {
1503 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1507 AST_RWLIST_RDLOCK(&sla_stations);
1508 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1509 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1510 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1516 AST_RWLIST_UNLOCK(&sla_stations);
1519 ast_debug(1, "Trunk not found for event!\n");
1523 sla_queue_event_full(type, trunk_ref, station, 1);
1526 /* Decrement reference counts, as incremented by find_conf() */
1527 static int dispose_conf(struct ast_conference *conf)
1532 AST_LIST_LOCK(&confs);
1533 if (ast_atomic_dec_and_test(&conf->refcount)) {
1534 /* Take the conference room number out of an inuse state */
1535 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1536 conf_map[confno_int] = 0;
1541 AST_LIST_UNLOCK(&confs);
1546 static int rt_extend_conf(char *confno)
1548 char currenttime[32];
1552 struct ast_variable *var;
1561 ast_localtime(&now, &tm, NULL);
1562 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1564 var = ast_load_realtime("meetme", "confno",
1565 confno, "startTime<= ", currenttime,
1566 "endtime>= ", currenttime, NULL);
1568 /* Identify the specific RealTime conference */
1570 if (!strcasecmp(var->name, "bookid")) {
1571 ast_copy_string(bookid, var->value, sizeof(bookid));
1573 if (!strcasecmp(var->name, "endtime")) {
1574 ast_copy_string(endtime, var->value, sizeof(endtime));
1579 ast_variables_destroy(var);
1581 ast_strptime(endtime, DATE_FORMAT, &tm);
1582 now = ast_mktime(&tm, NULL);
1584 now.tv_sec += extendby;
1586 ast_localtime(&now, &tm, NULL);
1587 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1588 strcat(currenttime, "0"); /* Seconds needs to be 00 */
1590 var = ast_load_realtime("meetme", "confno",
1591 confno, "startTime<= ", currenttime,
1592 "endtime>= ", currenttime, NULL);
1594 /* If there is no conflict with extending the conference, update the DB */
1596 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1597 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1602 ast_variables_destroy(var);
1606 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1610 ast_channel_lock(chan);
1611 original_moh = ast_strdupa(chan->musicclass);
1612 ast_string_field_set(chan, musicclass, musicclass);
1613 ast_channel_unlock(chan);
1615 ast_moh_start(chan, original_moh, NULL);
1617 ast_channel_lock(chan);
1618 ast_string_field_set(chan, musicclass, original_moh);
1619 ast_channel_unlock(chan);
1622 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1624 struct ast_conf_user *user = NULL;
1625 struct ast_conf_user *usr = NULL;
1627 struct dahdi_confinfo dahdic, dahdic_empty;
1628 struct ast_frame *f;
1629 struct ast_channel *c;
1630 struct ast_frame fr;
1638 int musiconhold = 0;
1641 int currentmarked = 0;
1644 int menu_active = 0;
1645 int talkreq_manager = 0;
1646 int using_pseudo = 0;
1651 int announcement_played = 0;
1653 struct ast_dsp *dsp = NULL;
1654 struct ast_app *agi_app;
1656 const char *agifiledefault = "conf-background.agi", *tmpvar;
1657 char meetmesecs[30] = "";
1658 char exitcontext[AST_MAX_CONTEXT] = "";
1659 char recordingtmp[AST_MAX_EXTENSION] = "";
1660 char members[10] = "";
1661 int dtmf, opt_waitmarked_timeout = 0;
1663 struct dahdi_bufferinfo bi;
1664 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1665 char *buf = __buf + AST_FRIENDLY_OFFSET;
1666 char *exitkeys = NULL;
1667 unsigned int calldurationlimit = 0;
1669 long play_warning = 0;
1670 long warning_freq = 0;
1671 const char *warning_sound = NULL;
1672 const char *end_sound = NULL;
1674 long time_left_ms = 0;
1675 struct timeval nexteventts = { 0, };
1677 int setusercount = 0;
1679 if (!(user = ast_calloc(1, sizeof(*user))))
1682 /* Possible timeout waiting for marked user */
1683 if ((confflags & CONFFLAG_WAITMARKED) &&
1684 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1685 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1686 (opt_waitmarked_timeout > 0)) {
1687 timeout = time(NULL) + opt_waitmarked_timeout;
1690 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1691 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1692 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1695 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1696 char *limit_str, *warning_str, *warnfreq_str;
1699 parse = optargs[OPT_ARG_DURATION_LIMIT];
1700 limit_str = strsep(&parse, ":");
1701 warning_str = strsep(&parse, ":");
1702 warnfreq_str = parse;
1704 timelimit = atol(limit_str);
1706 play_warning = atol(warning_str);
1708 warning_freq = atol(warnfreq_str);
1711 timelimit = play_warning = warning_freq = 0;
1712 warning_sound = NULL;
1713 } else if (play_warning > timelimit) {
1714 if (!warning_freq) {
1717 while (play_warning > timelimit)
1718 play_warning -= warning_freq;
1719 if (play_warning < 1)
1720 play_warning = warning_freq = 0;
1724 ast_channel_lock(chan);
1725 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1726 var = ast_strdupa(var);
1728 ast_channel_unlock(chan);
1730 warning_sound = var ? var : "timeleft";
1732 ast_channel_lock(chan);
1733 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1734 var = ast_strdupa(var);
1736 ast_channel_unlock(chan);
1738 end_sound = var ? var : NULL;
1740 /* undo effect of S(x) in case they are both used */
1741 calldurationlimit = 0;
1742 /* more efficient do it like S(x) does since no advanced opts */
1743 if (!play_warning && !end_sound && timelimit) {
1744 calldurationlimit = timelimit / 1000;
1745 timelimit = play_warning = warning_freq = 0;
1747 ast_debug(2, "Limit Data for this call:\n");
1748 ast_debug(2, "- timelimit = %ld\n", timelimit);
1749 ast_debug(2, "- play_warning = %ld\n", play_warning);
1750 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
1751 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1752 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
1757 if ((confflags & CONFFLAG_KEYEXIT)) {
1758 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1759 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1761 exitkeys = ast_strdupa("#"); /* Default */
1764 if (confflags & CONFFLAG_RECORDCONF) {
1765 if (!conf->recordingfilename) {
1767 ast_channel_lock(chan);
1768 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1769 conf->recordingfilename = ast_strdup(var);
1771 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1772 conf->recordingformat = ast_strdup(var);
1774 ast_channel_unlock(chan);
1775 if (!conf->recordingfilename) {
1776 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1777 conf->recordingfilename = ast_strdup(recordingtmp);
1779 if (!conf->recordingformat) {
1780 conf->recordingformat = ast_strdup("wav");
1782 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1783 conf->confno, conf->recordingfilename, conf->recordingformat);
1787 ast_mutex_lock(&conf->recordthreadlock);
1788 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1789 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1790 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1792 dahdic.confno = conf->dahdiconf;
1793 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1794 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1795 ast_log(LOG_WARNING, "Error starting listen channel\n");
1796 ast_hangup(conf->lchan);
1799 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1802 ast_mutex_unlock(&conf->recordthreadlock);
1804 time(&user->jointime);
1806 user->timelimit = timelimit;
1807 user->play_warning = play_warning;
1808 user->warning_freq = warning_freq;
1809 user->warning_sound = warning_sound;
1810 user->end_sound = end_sound;
1812 if (calldurationlimit > 0) {
1813 time(&user->kicktime);
1814 user->kicktime = user->kicktime + calldurationlimit;
1817 if (ast_tvzero(user->start_time))
1818 user->start_time = ast_tvnow();
1819 time_left_ms = user->timelimit;
1821 if (user->timelimit) {
1822 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1823 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1826 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1827 /* Sorry, but this conference is locked! */
1828 if (!ast_streamfile(chan, "conf-locked", chan->language))
1829 ast_waitstream(chan, "");
1833 ast_mutex_lock(&conf->playlock);
1835 if (AST_LIST_EMPTY(&conf->userlist))
1838 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1840 if (rt_schedule && conf->maxusers)
1841 if (user->user_no > conf->maxusers) {
1842 /* Sorry, but this confernce has reached the participant limit! */
1843 if (!ast_streamfile(chan, "conf-full", chan->language))
1844 ast_waitstream(chan, "");
1848 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1851 user->userflags = confflags;
1852 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1855 ast_mutex_unlock(&conf->playlock);
1857 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1858 char destdir[PATH_MAX];
1860 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1862 if (ast_mkdir(destdir, 0777) != 0) {
1863 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1867 snprintf(user->namerecloc, sizeof(user->namerecloc),
1868 "%s/meetme-username-%s-%d", destdir,
1869 conf->confno, user->user_no);
1870 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1871 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1873 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1878 ast_mutex_lock(&conf->playlock);
1880 if (confflags & CONFFLAG_MARKEDUSER)
1881 conf->markedusers++;
1883 if (rt_log_members) {
1885 snprintf(members, sizeof(members), "%d", conf->users);
1886 ast_realtime_require_field("meetme",
1887 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
1888 "members", RQ_UINTEGER1, strlen(members),
1890 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1894 /* This device changed state now - if this is the first user */
1895 if (conf->users == 1)
1896 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1898 ast_mutex_unlock(&conf->playlock);
1900 /* return the unique ID of the conference */
1901 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1903 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1904 ast_channel_lock(chan);
1905 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
1906 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
1907 } else if (!ast_strlen_zero(chan->macrocontext)) {
1908 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1910 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1912 ast_channel_unlock(chan);
1915 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1916 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1917 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1918 ast_waitstream(chan, "");
1919 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1920 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1921 ast_waitstream(chan, "");
1924 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1925 int keepplaying = 1;
1927 if (conf->users == 2) {
1928 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1929 res = ast_waitstream(chan, AST_DIGIT_ANY);
1930 ast_stopstream(chan);
1937 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1938 res = ast_waitstream(chan, AST_DIGIT_ANY);
1939 ast_stopstream(chan);
1946 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1952 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1953 res = ast_waitstream(chan, AST_DIGIT_ANY);
1954 ast_stopstream(chan);
1963 ast_indicate(chan, -1);
1965 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1966 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1970 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1971 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1975 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1976 user->dahdichannel = !retrydahdi;
1979 origfd = chan->fds[0];
1981 fd = open("/dev/dahdi/pseudo", O_RDWR);
1983 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1987 /* Make non-blocking */
1988 flags = fcntl(fd, F_GETFL);
1990 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1994 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1995 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1999 /* Setup buffering information */
2000 memset(&bi, 0, sizeof(bi));
2001 bi.bufsize = CONF_SIZE / 2;
2002 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2003 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2004 bi.numbufs = audio_buffers;
2005 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2006 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2011 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2012 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2018 /* XXX Make sure we're not running on a pseudo channel XXX */
2022 memset(&dahdic, 0, sizeof(dahdic));
2023 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2024 /* Check to see if we're in a conference... */
2026 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2027 ast_log(LOG_WARNING, "Error getting conference\n");
2031 if (dahdic.confmode) {
2032 /* Whoa, already in a conference... Retry... */
2034 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2039 memset(&dahdic, 0, sizeof(dahdic));
2040 /* Add us to the conference */
2042 dahdic.confno = conf->dahdiconf;
2044 ast_mutex_lock(&conf->playlock);
2046 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2047 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
2048 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2049 ast_waitstream(conf->chan, "");
2050 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
2051 ast_waitstream(conf->chan, "");
2055 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2056 dahdic.confmode = DAHDI_CONF_CONF;
2057 else if (confflags & CONFFLAG_MONITOR)
2058 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2059 else if (confflags & CONFFLAG_TALKER)
2060 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2062 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2064 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2065 ast_log(LOG_WARNING, "Error setting conference\n");
2067 ast_mutex_unlock(&conf->playlock);
2070 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2073 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
2078 "CallerIDnum: %s\r\n"
2079 "CallerIDname: %s\r\n",
2080 chan->name, chan->uniqueid, conf->confno,
2082 S_OR(user->chan->cid.cid_num, "<unknown>"),
2083 S_OR(user->chan->cid.cid_name, "<unknown>")
2088 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2090 if (!(confflags & CONFFLAG_QUIET))
2091 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2092 conf_play(chan, conf, ENTER);
2095 ast_mutex_unlock(&conf->playlock);
2097 conf_flush(fd, chan);
2099 if (confflags & CONFFLAG_AGI) {
2100 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2101 or use default filename of conf-background.agi */
2103 ast_channel_lock(chan);
2104 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2105 agifile = ast_strdupa(tmpvar);
2107 agifile = ast_strdupa(agifiledefault);
2109 ast_channel_unlock(chan);
2111 if (user->dahdichannel) {
2112 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2114 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2116 /* Find a pointer to the agi app and execute the script */
2117 agi_app = pbx_findapp("agi");
2119 ret = pbx_exec(chan, agi_app, agifile);
2121 ast_log(LOG_WARNING, "Could not find application (agi)\n");
2124 if (user->dahdichannel) {
2125 /* Remove CONFMUTE mode on DAHDI channel */
2127 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2130 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2131 /* Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2133 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2135 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2136 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2140 int menu_was_active = 0;
2146 if (rt_schedule && conf->endtime) {
2147 char currenttime[32];
2151 struct ast_variable *var, *origvar;
2154 if (now.tv_sec % 60 == 0) {
2156 ast_localtime(&now, &tm, NULL);
2157 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2158 var = origvar = ast_load_realtime("meetme", "confno",
2159 conf->confno, "starttime <=", currenttime,
2160 "endtime >=", currenttime, NULL);
2162 for ( ; var; var = var->next) {
2163 if (!strcasecmp(var->name, "endtime")) {
2165 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &tm);
2166 tmp = ast_mktime(&tm, NULL);
2167 localendtime = tmp.tv_sec;
2170 ast_variables_destroy(origvar);
2172 /* A conference can be extended from the
2173 Admin/User menu or by an external source */
2174 if (localendtime > conf->endtime){
2175 conf->endtime = localendtime;
2179 if (conf->endtime && (now.tv_sec > conf->endtime)) {
2180 ast_verbose("Quitting time...\n");
2184 if (!announcement_played && conf->endalert) {
2185 if (now.tv_sec + conf->endalert > conf->endtime) {
2186 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2187 ast_waitstream(chan, "");
2188 ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
2189 if (!ast_streamfile(chan, "minutes", chan->language))
2190 ast_waitstream(chan, "");
2191 announcement_played = 1;
2196 announcement_played = 0;
2206 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2211 if (user->timelimit) {
2212 int minutes = 0, seconds = 0, remain = 0;
2214 to = ast_tvdiff_ms(nexteventts, now);
2218 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2219 if (time_left_ms < to) {
2223 if (time_left_ms <= 0) {
2224 if (user->end_sound) {
2225 res = ast_streamfile(chan, user->end_sound, chan->language);
2226 res = ast_waitstream(chan, "");
2232 if (time_left_ms >= 5000) {
2234 remain = (time_left_ms + 500) / 1000;
2235 if (remain / 60 >= 1) {
2236 minutes = remain / 60;
2237 seconds = remain % 60;
2242 /* force the time left to round up if appropriate */
2243 if (user->warning_sound && user->play_warning) {
2244 if (!strcmp(user->warning_sound, "timeleft")) {
2246 res = ast_streamfile(chan, "vm-youhave", chan->language);
2247 res = ast_waitstream(chan, "");
2249 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2250 res = ast_streamfile(chan, "queue-minutes", chan->language);
2251 res = ast_waitstream(chan, "");
2254 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2255 res = ast_streamfile(chan, "queue-seconds", chan->language);
2256 res = ast_waitstream(chan, "");
2259 res = ast_streamfile(chan, user->warning_sound, chan->language);
2260 res = ast_waitstream(chan, "");
2264 if (user->warning_freq) {
2265 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2267 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2273 if (timeout && now.tv_sec >= timeout) {
2277 /* if we have just exited from the menu, and the user had a channel-driver
2278 volume adjustment, restore it
2280 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2281 set_talk_volume(user, user->listen.desired);
2284 menu_was_active = menu_active;
2286 currentmarked = conf->markedusers;
2287 if (!(confflags & CONFFLAG_QUIET) &&
2288 (confflags & CONFFLAG_MARKEDUSER) &&
2289 (confflags & CONFFLAG_WAITMARKED) &&
2291 if (currentmarked == 1 && conf->users > 1) {
2292 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2293 if (conf->users - 1 == 1) {
2294 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2295 ast_waitstream(chan, "");
2298 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2299 ast_waitstream(chan, "");
2303 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2304 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2305 ast_waitstream(chan, "");
2310 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2312 /* Update the struct with the actual confflags */
2313 user->userflags = confflags;
2315 if (confflags & CONFFLAG_WAITMARKED) {
2316 if (currentmarked == 0) {
2317 if (lastmarked != 0) {
2318 if (!(confflags & CONFFLAG_QUIET)) {
2319 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2320 ast_waitstream(chan, "");
2323 if (confflags & CONFFLAG_MARKEDEXIT) {
2324 if (confflags & CONFFLAG_KICK_CONTINUE) {
2329 dahdic.confmode = DAHDI_CONF_CONF;
2330 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2331 ast_log(LOG_WARNING, "Error setting conference\n");
2337 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2338 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2341 } else if (currentmarked >= 1 && lastmarked == 0) {
2342 /* Marked user entered, so cancel timeout */
2344 if (confflags & CONFFLAG_MONITOR) {
2345 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2346 } else if (confflags & CONFFLAG_TALKER) {
2347 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2349 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2351 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2352 ast_log(LOG_WARNING, "Error setting conference\n");
2356 if (musiconhold && (confflags & CONFFLAG_MOH)) {
2360 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2361 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2362 ast_waitstream(chan, "");
2364 conf_play(chan, conf, ENTER);
2369 /* trying to add moh for single person conf */
2370 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2371 if (conf->users == 1) {
2373 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2384 /* Leave if the last marked user left */
2385 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2386 if (confflags & CONFFLAG_KICK_CONTINUE) {
2394 /* Check if my modes have changed */
2396 /* If I should be muted but am still talker, mute me */
2397 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2398 dahdic.confmode ^= DAHDI_CONF_TALKER;
2399 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2400 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2405 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2411 chan->name, chan->uniqueid, conf->confno, user->user_no);
2414 /* If I should be un-muted but am not talker, un-mute me */
2415 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2416 dahdic.confmode |= DAHDI_CONF_TALKER;
2417 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2418 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2423 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2429 chan->name, chan->uniqueid, conf->confno, user->user_no);
2432 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2433 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2434 talkreq_manager = 1;
2436 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2442 chan->name, chan->uniqueid, conf->confno, user->user_no);
2446 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
2447 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2448 talkreq_manager = 0;
2449 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
2455 chan->name, chan->uniqueid, conf->confno, user->user_no);
2458 /* If I have been kicked, exit the conference */
2459 if (user->adminflags & ADMINFLAG_KICKME) {
2460 /* You have been kicked. */
2461 if (!(confflags & CONFFLAG_QUIET) &&
2462 !ast_streamfile(chan, "conf-kicked", chan->language)) {
2463 ast_waitstream(chan, "");
2469 /* Perform an extra hangup check just in case */
2470 if (ast_check_hangup(chan)) {
2475 char dtmfstr[2] = "";
2477 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2479 /* Kill old pseudo */
2483 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2484 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2485 user->dahdichannel = !retrydahdi;
2488 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2489 f = ast_read_noaudio(c);
2496 if (f->frametype == AST_FRAME_DTMF) {
2497 dtmfstr[0] = f->subclass;
2501 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2502 if (user->talk.actual) {
2503 ast_frame_adjust_volume(f, user->talk.actual);
2506 if (!(confflags & CONFFLAG_MONITOR)) {
2509 if (user->talking == -1) {
2513 res = ast_dsp_silence(dsp, f, &totalsilence);
2514 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2516 if (confflags & CONFFLAG_MONITORTALKER)
2517 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2523 chan->name, chan->uniqueid, conf->confno, user->user_no);
2525 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2527 if (confflags & CONFFLAG_MONITORTALKER) {
2528 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2534 chan->name, chan->uniqueid, conf->confno, user->user_no);
2539 /* Absolutely do _not_ use careful_write here...
2540 it is important that we read data from the channel
2541 as fast as it arrives, and feed it into the conference.
2542 The buffering in the pseudo channel will take care of any
2543 timing differences, unless they are so drastic as to lose
2544 audio frames (in which case carefully writing would only
2545 have delayed the audio even further).
2547 /* As it turns out, we do want to use careful write. We just
2548 don't want to block, but we do want to at least *try*
2549 to write out all the samples.
2551 if (user->talking) {
2552 careful_write(fd, f->data.ptr, f->datalen, 0);
2555 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2556 if (confflags & CONFFLAG_PASS_DTMF) {
2557 conf_queue_dtmf(conf, user, f);
2559 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2560 ast_log(LOG_WARNING, "Error setting conference\n");
2566 /* if we are entering the menu, and the user has a channel-driver
2567 volume adjustment, clear it
2569 if (!menu_active && user->talk.desired && !user->talk.actual) {
2570 set_talk_volume(user, 0);
2576 if ((confflags & CONFFLAG_ADMIN)) {
2580 /* Record this sound! */
2581 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2582 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2583 ast_stopstream(chan);
2592 case '1': /* Un/Mute */
2595 /* for admin, change both admin and use flags */
2596 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2597 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2599 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2602 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2603 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2604 ast_waitstream(chan, "");
2607 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2608 ast_waitstream(chan, "");
2612 case '2': /* Un/Lock the Conference */
2616 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2617 ast_waitstream(chan, "");
2621 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2622 ast_waitstream(chan, "");
2626 case '3': /* Eject last user */
2628 usr = AST_LIST_LAST(&conf->userlist);
2629 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2630 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2631 ast_waitstream(chan, "");
2634 usr->adminflags |= ADMINFLAG_KICKME;
2636 ast_stopstream(chan);
2639 tweak_listen_volume(user, VOL_DOWN);
2642 /* Extend RT conference */
2644 if (!rt_extend_conf(conf->confno)) {
2645 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2646 ast_waitstream(chan, "");
2649 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2650 ast_waitstream(chan, "");
2653 ast_stopstream(chan);
2658 tweak_listen_volume(user, VOL_UP);
2661 tweak_talk_volume(user, VOL_DOWN);
2667 tweak_talk_volume(user, VOL_UP);
2671 /* Play an error message! */
2672 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2673 ast_waitstream(chan, "");
2682 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2683 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2684 ast_stopstream(chan);
2693 case '1': /* Un/Mute */
2696 /* user can only toggle the self-muted state */
2697 user->adminflags ^= ADMINFLAG_SELFMUTED;
2699 /* they can't override the admin mute state */
2700 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2701 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2702 ast_waitstream(chan, "");
2705 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2706 ast_waitstream(chan, "");
2712 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2713 user->adminflags |= ADMINFLAG_T_REQUEST;
2716 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2717 if (!ast_streamfile(chan, "beep", chan->language)) {
2718 ast_waitstream(chan, "");
2723 tweak_listen_volume(user, VOL_DOWN);
2726 /* Extend RT conference */
2728 rt_extend_conf(conf->confno);
2733 tweak_listen_volume(user, VOL_UP);
2736 tweak_talk_volume(user, VOL_DOWN);
2742 tweak_talk_volume(user, VOL_UP);
2746 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2747 ast_waitstream(chan, "");
2754 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2757 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2758 ast_log(LOG_WARNING, "Error setting conference\n");
2764 conf_flush(fd, chan);
2765 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2766 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2767 if (confflags & CONFFLAG_PASS_DTMF) {
2768 conf_queue_dtmf(conf, user, f);
2771 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2772 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2777 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2779 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2780 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2782 if (confflags & CONFFLAG_PASS_DTMF) {
2783 conf_queue_dtmf(conf, user, f);
2788 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2789 && confflags & CONFFLAG_PASS_DTMF) {
2790 conf_queue_dtmf(conf, user, f);
2791 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2792 switch (f->subclass) {
2793 case AST_CONTROL_HOLD:
2794 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2799 } else if (f->frametype == AST_FRAME_NULL) {
2800 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2803 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2804 chan->name, f->frametype, f->subclass);
2807 } else if (outfd > -1) {
2808 res = read(outfd, buf, CONF_SIZE);
2810 memset(&fr, 0, sizeof(fr));
2811 fr.frametype = AST_FRAME_VOICE;
2812 fr.subclass = AST_FORMAT_SLINEAR;
2814 fr.samples = res / 2;
2816 fr.offset = AST_FRIENDLY_OFFSET;
2817 if (!user->listen.actual &&
2818 ((confflags & CONFFLAG_MONITOR) ||
2819 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2820 (!user->talking)) ) {
2822 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2823 if (chan->rawwriteformat & (1 << idx)) {
2827 if (idx >= AST_FRAME_BITS) {
2828 goto bailoutandtrynormal;
2830 ast_mutex_lock(&conf->listenlock);
2831 if (!conf->transframe[idx]) {
2832 if (conf->origframe) {
2833 if (!conf->transpath[idx]) {
2834 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2836 if (conf->transpath[idx]) {
2837 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2838 if (!conf->transframe[idx]) {
2839 conf->transframe[idx] = &ast_null_frame;
2844 if (conf->transframe[idx]) {
2845 if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2846 if (ast_write(chan, conf->transframe[idx])) {
2847 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2851 ast_mutex_unlock(&conf->listenlock);
2852 goto bailoutandtrynormal;
2854 ast_mutex_unlock(&conf->listenlock);
2856 bailoutandtrynormal:
2857 if (user->listen.actual) {
2858 ast_frame_adjust_volume(&fr, user->listen.actual);
2860 if (ast_write(chan, &fr) < 0) {
2861 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2865 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2868 lastmarked = currentmarked;
2879 /* Take out of conference */
2882 dahdic.confmode = 0;
2883 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2884 ast_log(LOG_WARNING, "Error setting conference\n");
2888 reset_volumes(user);
2890 AST_LIST_LOCK(&confs);
2891 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2892 conf_play(chan, conf, LEAVE);
2895 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2896 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2897 if ((conf->chan) && (conf->users > 1)) {
2898 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language)) {
2899 ast_waitstream(conf->chan, "");
2901 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language)) {
2902 ast_waitstream(conf->chan, "");
2905 ast_filedelete(user->namerecloc, NULL);
2908 AST_LIST_UNLOCK(&confs);
2911 AST_LIST_LOCK(&confs);
2917 if (user->user_no) { /* Only cleanup users who really joined! */
2919 hr = (now.tv_sec - user->jointime) / 3600;
2920 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2921 sec = (now.tv_sec - user->jointime) % 60;
2924 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2929 "CallerIDNum: %s\r\n"
2930 "CallerIDName: %s\r\n"
2931 "Duration: %ld\r\n",
2932 chan->name, chan->uniqueid, conf->confno,
2934 S_OR(user->chan->cid.cid_num, "<unknown>"),
2935 S_OR(user->chan->cid.cid_name, "<unknown>"),
2936 (long)(now.tv_sec - user->jointime));
2941 if (rt_log_members) {
2943 snprintf(members, sizeof(members), "%d", conf->users);
2944 ast_realtime_require_field("meetme",
2945 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2946 "members", RQ_UINTEGER1, strlen(members),
2948 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2950 if (confflags & CONFFLAG_MARKEDUSER) {
2951 conf->markedusers--;
2954 /* Remove ourselves from the list */
2955 AST_LIST_REMOVE(&conf->userlist, user, list);
2957 /* Change any states */
2959 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2962 /* Return the number of seconds the user was in the conf */
2963 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2964 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2967 AST_LIST_UNLOCK(&confs);
2972 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2973 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
2974 char *useropts, char *adminopts, int *too_early)
2976 struct ast_variable *var, *origvar;
2977 struct ast_conference *cnf;
2981 /* Check first in the conference list */
2982 AST_LIST_LOCK(&confs);
2983 AST_LIST_TRAVERSE(&confs, cnf, list) {
2984 if (!strcmp(confno, cnf->confno))
2988 cnf->refcount += refcount;
2990 AST_LIST_UNLOCK(&confs);
2993 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2996 char currenttime[19] = "";
2997 char eatime[19] = "";
2998 struct ast_tm tm, etm;
2999 struct timeval endtime = { .tv_sec = 0 };
3004 ast_localtime(&now, &tm, NULL);
3005 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3007 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
3009 var = ast_load_realtime("meetme", "confno",
3010 confno, "starttime <= ", currenttime, "endtime >= ",
3013 if (!var && fuzzystart) {
3015 now.tv_sec += fuzzystart;
3017 ast_localtime(&now, &tm, NULL);
3018 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3019 var = ast_load_realtime("meetme", "confno",
3020 confno, "starttime <= ", currenttime, "endtime >= ",
3024 if (!var && earlyalert) {
3026 now.tv_sec += earlyalert;
3027 ast_localtime(&now, &etm, NULL);
3028 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
3029 var = ast_load_realtime("meetme", "confno",
3030 confno, "starttime <= ", eatime, "endtime >= ",